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.

Reply via email to