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
This commit is contained in:
Jannis Leidel 2011-01-02 01:33:11 +00:00
parent 7a89d3d503
commit 544ab30ed7
6 changed files with 102 additions and 7 deletions

View File

@ -9,6 +9,7 @@ a list of all possible variables.
import os import os
import re import re
import time # Needed for Windows import time # Needed for Windows
import warnings
from django.conf import global_settings from django.conf import global_settings
from django.utils.functional import LazyObject from django.utils.functional import LazyObject
@ -60,7 +61,19 @@ class LazySettings(LazyObject):
return bool(self._wrapped) return bool(self._wrapped)
configured = property(configured) 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): def __init__(self, settings_module):
# update this dict from global settings (but only for ALL_CAPS settings) # update this dict from global settings (but only for ALL_CAPS settings)
for setting in dir(global_settings): for setting in dir(global_settings):
@ -125,7 +138,8 @@ class Settings(object):
# ... then invoke it with the logging settings # ... then invoke it with the logging settings
logging_config_func(self.LOGGING) logging_config_func(self.LOGGING)
class UserSettingsHolder(object):
class UserSettingsHolder(BaseSettings):
""" """
Holder for user configured settings. Holder for user configured settings.
""" """

View File

@ -48,7 +48,7 @@ USE_L10N = True
MEDIA_ROOT = '' MEDIA_ROOT = ''
# URL that handles the media served from MEDIA_ROOT. Make sure to use a # 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/" # Examples: "http://media.lawrence.com/media/", "http://example.com/media/"
MEDIA_URL = '' MEDIA_URL = ''

View File

@ -101,6 +101,10 @@ their deprecation, as per the :ref:`Django deprecation policy
* Authentication backends need to define the boolean attribute * Authentication backends need to define the boolean attribute
``supports_inactive_user``. ``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 * 1.5
* The ``mod_python`` request handler has been deprecated since the 1.3 * The ``mod_python`` request handler has been deprecated since the 1.3
release. The ``mod_wsgi`` handler should be used instead. release. The ``mod_wsgi`` handler should be used instead.

View File

@ -1251,10 +1251,8 @@ for :doc:`managing stored files </topics/files>`.
Example: ``"http://media.lawrence.com/"`` Example: ``"http://media.lawrence.com/"``
Note that this should have a trailing slash if it has a path component. .. versionchanged:: 1.3
It must end in a slash if set to a non-empty value.
* Good: ``"http://www.example.com/media/"``
* Bad: ``"http://www.example.com/media"``
MESSAGE_LEVEL 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 :ref:`media definitions<form-media-paths>` and the
:doc:`staticfiles app</ref/contrib/staticfiles>`. :doc:`staticfiles app</ref/contrib/staticfiles>`.
It must end in a slash if set to a non-empty value.
See :setting:`STATIC_ROOT`. See :setting:`STATIC_ROOT`.
.. setting:: TEMPLATE_CONTEXT_PROCESSORS .. setting:: TEMPLATE_CONTEXT_PROCESSORS

View File

@ -192,6 +192,22 @@ The GeoDjango test suite is now included when
:ref:`running the Django test suite <running-unit-tests>` with ``runtests.py`` :ref:`running the Django test suite <running-unit-tests>` with ``runtests.py``
when using :ref:`spatial database backends <spatial-backends>`. 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 Everything else
~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~

View File

@ -1,5 +1,7 @@
from django.conf import settings from django.conf import settings
from django.utils import unittest from django.utils import unittest
from django.conf import settings, UserSettingsHolder, global_settings
class SettingsTests(unittest.TestCase): class SettingsTests(unittest.TestCase):
@ -15,3 +17,62 @@ class SettingsTests(unittest.TestCase):
def test_settings_delete_wrapped(self): def test_settings_delete_wrapped(self):
self.assertRaises(TypeError, delattr, settings, '_wrapped') 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)