Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #6213 -- Updated the flatpages app to only append a slash if th…

…e flatpage actually exist.

The FlatpageFallbackMiddleware (and the view) now only add a trailing slash and redirect if the resulting URL refers to an existing flatpage. Previously requesting /notaflatpageoravalidurl would redirect to /notaflatpageoravalidurl/, which would then raise a 404. Requesting /notaflatpageoravalidurl now will immediately raise a 404. Also, Redirects returned by flatpages are now permanent (301 status code) to match the behaviour of the CommonMiddleware.

Thanks to Steve Losh for the initial work on the patch.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@16048 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 196ac8f8b31302d026af6d79f764c8f146a8c78e 1 parent 96520e8
Jannis Leidel authored April 20, 2011
95  django/contrib/flatpages/tests/middleware.py
... ...
@@ -1,6 +1,7 @@
1 1
 import os
2 2
 from django.conf import settings
3 3
 from django.contrib.auth.models import User
  4
+from django.contrib.flatpages.models import FlatPage
4 5
 from django.test import TestCase
5 6
 
6 7
 class FlatpageMiddlewareTests(TestCase):
@@ -68,3 +69,97 @@ def test_fallback_authenticated_flatpage(self):
68 69
         response = self.client.get('/sekrit/')
69 70
         self.assertEqual(response.status_code, 200)
70 71
         self.assertContains(response, "<p>Isn't it sekrit!</p>")
  72
+
  73
+    def test_fallback_flatpage_special_chars(self):
  74
+        "A flatpage with special chars in the URL can be served by the fallback middleware"
  75
+        fp = FlatPage.objects.create(
  76
+            url="/some.very_special~chars-here/",
  77
+            title="A very special page",
  78
+            content="Isn't it special!",
  79
+            enable_comments=False,
  80
+            registration_required=False,
  81
+        )
  82
+        fp.sites.add(1)
  83
+
  84
+        response = self.client.get('/some.very_special~chars-here/')
  85
+        self.assertEquals(response.status_code, 200)
  86
+        self.assertContains(response, "<p>Isn't it special!</p>")
  87
+
  88
+
  89
+class FlatpageMiddlewareAppendSlashTests(TestCase):
  90
+    fixtures = ['sample_flatpages']
  91
+    urls = 'django.contrib.flatpages.tests.urls'
  92
+
  93
+    def setUp(self):
  94
+        self.old_MIDDLEWARE_CLASSES = settings.MIDDLEWARE_CLASSES
  95
+        flatpage_middleware_class = 'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware'
  96
+        if flatpage_middleware_class not in settings.MIDDLEWARE_CLASSES:
  97
+            settings.MIDDLEWARE_CLASSES += (flatpage_middleware_class,)
  98
+        self.old_TEMPLATE_DIRS = settings.TEMPLATE_DIRS
  99
+        settings.TEMPLATE_DIRS = (
  100
+            os.path.join(
  101
+                os.path.dirname(__file__),
  102
+                'templates'
  103
+            ),
  104
+        )
  105
+        self.old_LOGIN_URL = settings.LOGIN_URL
  106
+        settings.LOGIN_URL = '/accounts/login/'
  107
+        self.old_APPEND_SLASH = settings.APPEND_SLASH
  108
+        settings.APPEND_SLASH = True
  109
+
  110
+    def tearDown(self):
  111
+        settings.MIDDLEWARE_CLASSES = self.old_MIDDLEWARE_CLASSES
  112
+        settings.TEMPLATE_DIRS = self.old_TEMPLATE_DIRS
  113
+        settings.LOGIN_URL = self.old_LOGIN_URL
  114
+        settings.APPEND_SLASH = self.old_APPEND_SLASH
  115
+
  116
+    def test_redirect_view_flatpage(self):
  117
+        "A flatpage can be served through a view and should add a slash"
  118
+        response = self.client.get('/flatpage_root/flatpage')
  119
+        self.assertRedirects(response, '/flatpage_root/flatpage/', status_code=301)
  120
+
  121
+    def test_redirect_view_non_existent_flatpage(self):
  122
+        "A non-existent flatpage raises 404 when served through a view and should not add a slash"
  123
+        response = self.client.get('/flatpage_root/no_such_flatpage')
  124
+        self.assertEquals(response.status_code, 404)
  125
+
  126
+    def test_redirect_fallback_flatpage(self):
  127
+        "A flatpage can be served by the fallback middlware and should add a slash"
  128
+        response = self.client.get('/flatpage')
  129
+        self.assertRedirects(response, '/flatpage/', status_code=301)
  130
+
  131
+    def test_redirect_fallback_non_existent_flatpage(self):
  132
+        "A non-existent flatpage raises a 404 when served by the fallback middlware and should not add a slash"
  133
+        response = self.client.get('/no_such_flatpage')
  134
+        self.assertEquals(response.status_code, 404)
  135
+
  136
+    def test_redirect_fallback_flatpage_special_chars(self):
  137
+        "A flatpage with special chars in the URL can be served by the fallback middleware and should add a slash"
  138
+        fp = FlatPage.objects.create(
  139
+            url="/some.very_special~chars-here/",
  140
+            title="A very special page",
  141
+            content="Isn't it special!",
  142
+            enable_comments=False,
  143
+            registration_required=False,
  144
+        )
  145
+        fp.sites.add(1)
  146
+
  147
+        response = self.client.get('/some.very_special~chars-here')
  148
+        self.assertRedirects(response, '/some.very_special~chars-here/', status_code=301)
  149
+
  150
+    def test_redirect_fallback_flatpage_root(self):
  151
+        "A flatpage at / should not cause a redirect loop when APPEND_SLASH is set"
  152
+        fp = FlatPage.objects.create(
  153
+            url="/",
  154
+            title="Root",
  155
+            content="Root",
  156
+            enable_comments=False,
  157
+            registration_required=False,
  158
+        )
  159
+        fp.sites.add(1)
  160
+
  161
+        response = self.client.get('/')
  162
+        self.assertEquals(response.status_code, 200)
  163
+        self.assertContains(response, "<p>Root</p>")
  164
+
  165
+
62  django/contrib/flatpages/tests/views.py
@@ -73,3 +73,65 @@ def test_view_flatpage_special_chars(self):
73 73
         response = self.client.get('/flatpage_root/some.very_special~chars-here/')
74 74
         self.assertEqual(response.status_code, 200)
75 75
         self.assertContains(response, "<p>Isn't it special!</p>")
  76
+
  77
+
  78
+class FlatpageViewAppendSlashTests(TestCase):
  79
+    fixtures = ['sample_flatpages']
  80
+    urls = 'django.contrib.flatpages.tests.urls'
  81
+
  82
+    def setUp(self):
  83
+        self.old_MIDDLEWARE_CLASSES = settings.MIDDLEWARE_CLASSES
  84
+        flatpage_middleware_class = 'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware'
  85
+        if flatpage_middleware_class in settings.MIDDLEWARE_CLASSES:
  86
+            settings.MIDDLEWARE_CLASSES = tuple(m for m in settings.MIDDLEWARE_CLASSES if m != flatpage_middleware_class)
  87
+        self.old_TEMPLATE_DIRS = settings.TEMPLATE_DIRS
  88
+        settings.TEMPLATE_DIRS = (
  89
+            os.path.join(
  90
+                os.path.dirname(__file__),
  91
+                'templates'
  92
+            ),
  93
+        )
  94
+        self.old_LOGIN_URL = settings.LOGIN_URL
  95
+        settings.LOGIN_URL = '/accounts/login/'
  96
+        self.old_APPEND_SLASH = settings.APPEND_SLASH
  97
+        settings.APPEND_SLASH = True
  98
+
  99
+    def tearDown(self):
  100
+        settings.MIDDLEWARE_CLASSES = self.old_MIDDLEWARE_CLASSES
  101
+        settings.TEMPLATE_DIRS = self.old_TEMPLATE_DIRS
  102
+        settings.LOGIN_URL = self.old_LOGIN_URL
  103
+        settings.APPEND_SLASH = self.old_APPEND_SLASH
  104
+
  105
+    def test_redirect_view_flatpage(self):
  106
+        "A flatpage can be served through a view and should add a slash"
  107
+        response = self.client.get('/flatpage_root/flatpage')
  108
+        self.assertRedirects(response, '/flatpage_root/flatpage/', status_code=301)
  109
+
  110
+    def test_redirect_view_non_existent_flatpage(self):
  111
+        "A non-existent flatpage raises 404 when served through a view and should not add a slash"
  112
+        response = self.client.get('/flatpage_root/no_such_flatpage')
  113
+        self.assertEquals(response.status_code, 404)
  114
+
  115
+    def test_redirect_fallback_flatpage(self):
  116
+        "A fallback flatpage won't be served if the middleware is disabled and should not add a slash"
  117
+        response = self.client.get('/flatpage')
  118
+        self.assertEquals(response.status_code, 404)
  119
+
  120
+    def test_redirect_fallback_non_existent_flatpage(self):
  121
+        "A non-existent flatpage won't be served if the fallback middlware is disabled and should not add a slash"
  122
+        response = self.client.get('/no_such_flatpage')
  123
+        self.assertEquals(response.status_code, 404)
  124
+
  125
+    def test_redirect_view_flatpage_special_chars(self):
  126
+        "A flatpage with special chars in the URL can be served through a view and should add a slash"
  127
+        fp = FlatPage.objects.create(
  128
+            url="/some.very_special~chars-here/",
  129
+            title="A very special page",
  130
+            content="Isn't it special!",
  131
+            enable_comments=False,
  132
+            registration_required=False,
  133
+        )
  134
+        fp.sites.add(1)
  135
+
  136
+        response = self.client.get('/flatpage_root/some.very_special~chars-here')
  137
+        self.assertRedirects(response, '/flatpage_root/some.very_special~chars-here/', status_code=301)
18  django/contrib/flatpages/views.py
... ...
@@ -1,7 +1,7 @@
1 1
 from django.contrib.flatpages.models import FlatPage
2 2
 from django.template import loader, RequestContext
3 3
 from django.shortcuts import get_object_or_404
4  
-from django.http import HttpResponse, HttpResponseRedirect
  4
+from django.http import Http404, HttpResponse, HttpResponsePermanentRedirect
5 5
 from django.conf import settings
6 6
 from django.core.xheaders import populate_xheaders
7 7
 from django.utils.safestring import mark_safe
@@ -28,11 +28,19 @@ def flatpage(request, url):
28 28
         flatpage
29 29
             `flatpages.flatpages` object
30 30
     """
31  
-    if not url.endswith('/') and settings.APPEND_SLASH:
32  
-        return HttpResponseRedirect("%s/" % request.path)
33 31
     if not url.startswith('/'):
34  
-        url = "/" + url
35  
-    f = get_object_or_404(FlatPage, url__exact=url, sites__id__exact=settings.SITE_ID)
  32
+        url = '/' + url
  33
+    try:
  34
+        f = get_object_or_404(FlatPage,
  35
+            url__exact=url, sites__id__exact=settings.SITE_ID)
  36
+    except Http404:
  37
+        if not url.endswith('/') and settings.APPEND_SLASH:
  38
+            url += '/'
  39
+            f = get_object_or_404(FlatPage,
  40
+                url__exact=url, sites__id__exact=settings.SITE_ID)
  41
+            return HttpResponsePermanentRedirect('%s/' % request.path)
  42
+        else:
  43
+            raise
36 44
     return render_flatpage(request, f)
37 45
 
38 46
 @csrf_protect
12  docs/ref/contrib/flatpages.txt
@@ -77,6 +77,18 @@ does all of the work.
77 77
           :class:`~django.template.RequestContext` in rendering the
78 78
           template.
79 79
 
  80
+    .. versionchanged:: 1.4
  81
+       The middleware will only add a trailing slash and redirect (by looking
  82
+       at the :setting:`APPEND_SLASH` setting) if the resulting URL refers to
  83
+       a valid flatpage. Previously requesting a non-existent flatpage
  84
+       would redirect to the same URL with an apppended slash first and
  85
+       subsequently raise a 404.
  86
+
  87
+    .. versionchanged:: 1.4
  88
+       Redirects by the middlware are permanent (301 status code) instead of
  89
+       temporary (302) to match behaviour of the
  90
+       :class:`~django.middleware.common.CommonMiddleware`.
  91
+
80 92
     If it doesn't find a match, the request continues to be processed as usual.
81 93
 
82 94
     The middleware only gets activated for 404s -- not for 500s or responses
13  docs/releases/1.4.txt
@@ -78,3 +78,16 @@ Django instance, and try to submit it to the upgraded Django instance:
78 78
 
79 79
   * time period: the amount of time you expect user to take filling out
80 80
     such forms.
  81
+
  82
+django.contrib.flatpages
  83
+~~~~~~~~~~~~~~~~~~~~~~~~
  84
+
  85
+Starting in the 1.4 release the
  86
+:class:`~django.contrib.flatpages.middleware.FlatpageFallbackMiddleware` only
  87
+adds a trailing slash and redirects if the resulting URL refers to an existing
  88
+flatpage. For example, requesting ``/notaflatpageoravalidurl`` in a previous
  89
+version would redirect to ``/notaflatpageoravalidurl/``, which would
  90
+subsequently raise a 404. Requesting ``/notaflatpageoravalidurl`` now will
  91
+immediately raise a 404. Additionally redirects returned by flatpages are now
  92
+permanent (301 status code) to match the behaviour of the
  93
+:class:`~django.middleware.common.CommonMiddleware`.

0 notes on commit 196ac8f

Please sign in to comment.
Something went wrong with that request. Please try again.