Author: jezdez Date: 2011-01-01 19:33:11 -0600 (Sat, 01 Jan 2011) New Revision: 15130
Modified: django/trunk/django/conf/__init__.py django/trunk/django/conf/project_template/settings.py django/trunk/docs/internals/deprecation.txt django/trunk/docs/ref/settings.txt django/trunk/docs/releases/1.3.txt django/trunk/tests/regressiontests/settings_tests/tests.py Log: 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. Modified: django/trunk/django/conf/__init__.py =================================================================== --- django/trunk/django/conf/__init__.py 2011-01-02 01:32:40 UTC (rev 15129) +++ django/trunk/django/conf/__init__.py 2011-01-02 01:33:11 UTC (rev 15130) @@ -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 @@ 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 @@ # ... then invoke it with the logging settings logging_config_func(self.LOGGING) -class UserSettingsHolder(object): + +class UserSettingsHolder(BaseSettings): """ Holder for user configured settings. """ Modified: django/trunk/django/conf/project_template/settings.py =================================================================== --- django/trunk/django/conf/project_template/settings.py 2011-01-02 01:32:40 UTC (rev 15129) +++ django/trunk/django/conf/project_template/settings.py 2011-01-02 01:33:11 UTC (rev 15130) @@ -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 = '' Modified: django/trunk/docs/internals/deprecation.txt =================================================================== --- django/trunk/docs/internals/deprecation.txt 2011-01-02 01:32:40 UTC (rev 15129) +++ django/trunk/docs/internals/deprecation.txt 2011-01-02 01:33:11 UTC (rev 15130) @@ -101,6 +101,10 @@ * 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. Modified: django/trunk/docs/ref/settings.txt =================================================================== --- django/trunk/docs/ref/settings.txt 2011-01-02 01:32:40 UTC (rev 15129) +++ django/trunk/docs/ref/settings.txt 2011-01-02 01:33:11 UTC (rev 15130) @@ -1251,11 +1251,9 @@ 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 ------------- @@ -1664,6 +1662,8 @@ :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 Modified: django/trunk/docs/releases/1.3.txt =================================================================== --- django/trunk/docs/releases/1.3.txt 2011-01-02 01:32:40 UTC (rev 15129) +++ django/trunk/docs/releases/1.3.txt 2011-01-02 01:33:11 UTC (rev 15130) @@ -192,6 +192,22 @@ :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 ~~~~~~~~~~~~~~~ Modified: django/trunk/tests/regressiontests/settings_tests/tests.py =================================================================== --- django/trunk/tests/regressiontests/settings_tests/tests.py 2011-01-02 01:32:40 UTC (rev 15129) +++ django/trunk/tests/regressiontests/settings_tests/tests.py 2011-01-02 01:33:11 UTC (rev 15130) @@ -1,6 +1,8 @@ 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_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) -- You received this message because you are subscribed to the Google Groups "Django updates" group. To post to this group, send email to django-upda...@googlegroups.com. To unsubscribe from this group, send email to django-updates+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/django-updates?hl=en.