Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Fixed #6218 -- Made MEDIA_URL and STATIC_URL require a trailing slash…

… to ensure there is a consistent way to combine paths in templates. Thanks to Michael Toomim, Chris Heisel and Chris Beaven.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@15130 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 544ab30ed71e0d78c4c061008758de29ff79e8f7 1 parent 7a89d3d
Jannis Leidel jezdez authored
18 django/conf/__init__.py
View
@@ -9,6 +9,7 @@
import os
import re
import time # Needed for Windows
+import warnings
from django.conf import global_settings
from django.utils.functional import LazyObject
@@ -60,7 +61,19 @@ def configured(self):
return bool(self._wrapped)
configured = property(configured)
-class Settings(object):
+
+class BaseSettings(object):
+ """
+ Common logic for settings whether set by a module or by the user.
+ """
+ def __setattr__(self, name, value):
+ if name in ("MEDIA_URL", "STATIC_URL") and value and not value.endswith('/'):
+ warnings.warn('If set, %s must end with a slash' % name,
+ PendingDeprecationWarning)
+ object.__setattr__(self, name, value)
+
+
+class Settings(BaseSettings):
def __init__(self, settings_module):
# update this dict from global settings (but only for ALL_CAPS settings)
for setting in dir(global_settings):
@@ -125,7 +138,8 @@ def __init__(self, settings_module):
# ... then invoke it with the logging settings
logging_config_func(self.LOGGING)
-class UserSettingsHolder(object):
+
+class UserSettingsHolder(BaseSettings):
"""
Holder for user configured settings.
"""
2  django/conf/project_template/settings.py
View
@@ -48,7 +48,7 @@
MEDIA_ROOT = ''
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
-# trailing slash if there is a path component (optional in other cases).
+# trailing slash.
# Examples: "http://media.lawrence.com/media/", "http://example.com/media/"
MEDIA_URL = ''
4 docs/internals/deprecation.txt
View
@@ -101,6 +101,10 @@ their deprecation, as per the :ref:`Django deprecation policy
* Authentication backends need to define the boolean attribute
``supports_inactive_user``.
+ * The ``MEDIA_URL`` or ``STATIC_URL`` settings are required to end
+ with a trailing slash to ensure there is a consistent way to
+ combine paths in templates.
+
* 1.5
* The ``mod_python`` request handler has been deprecated since the 1.3
release. The ``mod_wsgi`` handler should be used instead.
8 docs/ref/settings.txt
View
@@ -1251,10 +1251,8 @@ for :doc:`managing stored files </topics/files>`.
Example: ``"http://media.lawrence.com/"``
-Note that this should have a trailing slash if it has a path component.
-
- * Good: ``"http://www.example.com/media/"``
- * Bad: ``"http://www.example.com/media"``
+.. versionchanged:: 1.3
+ It must end in a slash if set to a non-empty value.
MESSAGE_LEVEL
-------------
@@ -1664,6 +1662,8 @@ If not ``None``, this will be used as the base path for
:ref:`media definitions<form-media-paths>` and the
:doc:`staticfiles app</ref/contrib/staticfiles>`.
+It must end in a slash if set to a non-empty value.
+
See :setting:`STATIC_ROOT`.
.. setting:: TEMPLATE_CONTEXT_PROCESSORS
16 docs/releases/1.3.txt
View
@@ -192,6 +192,22 @@ The GeoDjango test suite is now included when
:ref:`running the Django test suite <running-unit-tests>` with ``runtests.py``
when using :ref:`spatial database backends <spatial-backends>`.
+``MEDIA_URL`` and ``STATIC_URL`` must end in a slash
+----------------------------------------------------
+
+Previously, the ``MEDIA_URL`` setting only required a trailing slash if it
+contained a suffix beyond the domain name.
+
+A trailing slash is now *required* for ``MEDIA_URL`` and the new
+``STATIC_URL`` setting as long as it is not blank. This ensures there is
+a consistent way to combine paths in templates.
+
+Project settings which provide either of both settings without a trailing
+slash will now raise a ``PendingDeprecation`` warning.
+
+In Django 1.4 this same condition will raise an ``ImproperlyConfigured``
+exception.
+
Everything else
~~~~~~~~~~~~~~~
61 tests/regressiontests/settings_tests/tests.py
View
@@ -1,5 +1,7 @@
from django.conf import settings
from django.utils import unittest
+from django.conf import settings, UserSettingsHolder, global_settings
+
class SettingsTests(unittest.TestCase):
@@ -15,3 +17,62 @@ def test_settings_delete(self):
def test_settings_delete_wrapped(self):
self.assertRaises(TypeError, delattr, settings, '_wrapped')
+
+
+class TrailingSlashURLTests(unittest.TestCase):
+ settings_module = settings
+
+ def setUp(self):
+ self._original_media_url = self.settings_module.MEDIA_URL
+
+ def tearDown(self):
+ self.settings_module.MEDIA_URL = self._original_media_url
+
+ def test_blank(self):
+ """
+ If blank, no PendingDeprecationWarning error will be raised, even though it
+ doesn't end in a slash.
+ """
+ self.settings_module.MEDIA_URL = ''
+ self.assertEqual('', self.settings_module.MEDIA_URL)
+
+ def test_end_slash(self):
+ """
+ MEDIA_URL works if you end in a slash.
+ """
+ self.settings_module.MEDIA_URL = '/foo/'
+ self.assertEqual('/foo/', self.settings_module.MEDIA_URL)
+
+ self.settings_module.MEDIA_URL = 'http://media.foo.com/'
+ self.assertEqual('http://media.foo.com/',
+ self.settings_module.MEDIA_URL)
+
+ def test_no_end_slash(self):
+ """
+ MEDIA_URL raises an PendingDeprecationWarning error if it doesn't end in a
+ slash.
+ """
+ import warnings
+ warnings.filterwarnings('error', 'If set, MEDIA_URL must end with a slash', PendingDeprecationWarning)
+
+ def setattr_settings(settings_module, attr, value):
+ setattr(settings_module, attr, value)
+
+ self.assertRaises(PendingDeprecationWarning, setattr_settings,
+ self.settings_module, 'MEDIA_URL', '/foo')
+
+ self.assertRaises(PendingDeprecationWarning, setattr_settings,
+ self.settings_module, 'MEDIA_URL',
+ 'http://media.foo.com')
+
+ def test_double_slash(self):
+ """
+ If a MEDIA_URL ends in more than one slash, presume they know what
+ they're doing.
+ """
+ self.settings_module.MEDIA_URL = '/stupid//'
+ self.assertEqual('/stupid//', self.settings_module.MEDIA_URL)
+
+ self.settings_module.MEDIA_URL = 'http://media.foo.com/stupid//'
+ self.assertEqual('http://media.foo.com/stupid//',
+ self.settings_module.MEDIA_URL)
Please sign in to comment.
Something went wrong with that request. Please try again.