Browse Source

Fixed #14693, #14709 -- Backwards incompatible change to rectify the confusion around the STATICFILES_URL and STATICFILES_ROOT settings.

  * Two new global settings that will be used by -- **but are not limited to** -- the staticfiles app: STATIC_ROOT and STATIC_URL.

  * Moving the 'django.contrib.staticfiles.templatetags.staticfiles' template tag to the core ('django.templatetags.static') and renaming it to 'get_static_prefix'.

  * Moving the context processor 'django.contrib.staticfiles.context_processors.staticfiles' to the core ('django.core.context_processors.static') and renaming it to 'static'.

  * Paths in media definitions will use STATIC_URL as the prefix if the value is not None, and falls back to the previously used MEDIA_URL.

Thanks again to the community for constructive criticism and Carl and Russ for sanity-inducing discussions on IRC.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@14592 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Jannis Leidel 14 năm trước cách đây
mục cha
commit
33d8fcde8a

+ 11 - 11
django/conf/global_settings.py

@@ -195,9 +195,9 @@ TEMPLATE_CONTEXT_PROCESSORS = (
     'django.core.context_processors.debug',
     'django.core.context_processors.i18n',
     'django.core.context_processors.media',
+    'django.core.context_processors.static',
 #    'django.core.context_processors.request',
     'django.contrib.messages.context_processors.messages',
-    'django.contrib.staticfiles.context_processors.staticfiles',
 )
 
 # Output to use in template system for invalid (e.g. misspelled) variables.
@@ -256,13 +256,21 @@ SECRET_KEY = ''
 DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage'
 
 # Absolute filesystem path to the directory that will hold user-uploaded files.
-# Example: "/home/media/media.lawrence.com/"
+# Example: "/home/media/media.lawrence.com/media/"
 MEDIA_ROOT = ''
 
 # URL that handles the media served from MEDIA_ROOT.
-# Example: "http://media.lawrence.com"
+# Example: "http://media.lawrence.com/media/"
 MEDIA_URL = ''
 
+# Absolute path to the directory that holds static files.
+# Example: "/home/media/media.lawrence.com/static/"
+STATIC_ROOT = ''
+
+# URL that handles the static files served from STATIC_ROOT.
+# Example: "http://media.lawrence.com/static/"
+STATIC_URL = None
+
 # List of upload handler classes to be applied in order.
 FILE_UPLOAD_HANDLERS = (
     'django.core.files.uploadhandler.MemoryFileUploadHandler',
@@ -552,14 +560,6 @@ FIXTURE_DIRS = ()
 # STATICFILES #
 ###############
 
-# Absolute path to the directory that holds media.
-# Example: "/home/media/media.lawrence.com/static/"
-STATICFILES_ROOT = ''
-
-# URL that handles the static files served from STATICFILES_ROOT.
-# Example: "http://media.lawrence.com/static/"
-STATICFILES_URL = '/static/'
-
 # A list of locations of additional static files
 STATICFILES_DIRS = ()
 

+ 6 - 6
django/conf/project_template/settings.py

@@ -49,16 +49,16 @@ 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).
-# Examples: "http://media.lawrence.com", "http://example.com/media/"
+# Examples: "http://media.lawrence.com/media/", "http://example.com/media/"
 MEDIA_URL = ''
 
-# Absolute path to the directory that holds media.
+# Absolute path to the directory that holds static files.
 # Example: "/home/media/media.lawrence.com/static/"
-STATICFILES_ROOT = ''
+STATIC_ROOT = ''
 
-# URL that handles the static files served from STATICFILES_ROOT.
-# Example: "http://static.lawrence.com/", "http://example.com/static/"
-STATICFILES_URL = '/static/'
+# URL that handles the static files served from STATIC_ROOT.
+# Example: "http://media.lawrence.com/static/"
+STATIC_URL = '/static/'
 
 # URL prefix for admin media -- CSS, JavaScript and images.
 # Make sure to use a trailing slash.

+ 3 - 7
django/contrib/admin/templatetags/adminmedia.py

@@ -1,15 +1,11 @@
 from django.template import Library
-from django.utils.encoding import iri_to_uri
+from django.templatetags.static import PrefixNode
 
 register = Library()
 
+@register.simple_tag
 def admin_media_prefix():
     """
     Returns the string contained in the setting ADMIN_MEDIA_PREFIX.
     """
-    try:
-        from django.conf import settings
-    except ImportError:
-        return ''
-    return iri_to_uri(settings.ADMIN_MEDIA_PREFIX)
-admin_media_prefix = register.simple_tag(admin_media_prefix)
+    return PrefixNode.handle_simple("ADMIN_MEDIA_PREFIX")

+ 0 - 6
django/contrib/staticfiles/context_processors.py

@@ -1,6 +0,0 @@
-from django.conf import settings
-
-def staticfiles(request):
-    return {
-        'STATICFILES_URL': settings.STATICFILES_URL,
-    }

+ 17 - 19
django/contrib/staticfiles/handlers.py

@@ -10,46 +10,44 @@ from django.contrib.staticfiles.views import serve
 class StaticFilesHandler(WSGIHandler):
     """
     WSGI middleware that intercepts calls to the static files directory, as
-    defined by the STATICFILES_URL setting, and serves those files.
+    defined by the STATIC_URL setting, and serves those files.
     """
-    def __init__(self, application, media_dir=None):
+    def __init__(self, application, base_dir=None):
         self.application = application
-        if media_dir:
-            self.media_dir = media_dir
+        if base_dir:
+            self.base_dir = base_dir
         else:
-            self.media_dir = self.get_media_dir()
-        self.media_url = urlparse(self.get_media_url())
-        if settings.DEBUG:
-            utils.check_settings()
+            self.base_dir = self.get_base_dir()
+        self.base_url = urlparse(self.get_base_url())
         super(StaticFilesHandler, self).__init__()
 
-    def get_media_dir(self):
-        return settings.STATICFILES_ROOT
+    def get_base_dir(self):
+        return settings.STATIC_ROOT
 
-    def get_media_url(self):
-        return settings.STATICFILES_URL
+    def get_base_url(self):
+        if settings.DEBUG:
+            utils.check_settings()
+        return settings.STATIC_URL
 
     def _should_handle(self, path):
         """
         Checks if the path should be handled. Ignores the path if:
 
-        * the host is provided as part of the media_url
+        * the host is provided as part of the base_url
         * the request's path isn't under the media path (or equal)
-        * settings.DEBUG isn't True
         """
-        return (self.media_url[2] != path and
-            path.startswith(self.media_url[2]) and not self.media_url[1])
+        return (self.base_url[2] != path and
+            path.startswith(self.base_url[2]) and not self.base_url[1])
 
     def file_path(self, url):
         """
         Returns the relative path to the media file on disk for the given URL.
 
-        The passed URL is assumed to begin with ``media_url``.  If the
+        The passed URL is assumed to begin with ``base_url``.  If the
         resultant file path is outside the media directory, then a ValueError
         is raised.
         """
-        # Remove ``media_url``.
-        relative_url = url[len(self.media_url[2]):]
+        relative_url = url[len(self.base_url[2]):]
         return urllib.url2pathname(relative_url)
 
     def serve(self, request):

+ 2 - 2
django/contrib/staticfiles/management/commands/collectstatic.py

@@ -12,7 +12,7 @@ from django.contrib.staticfiles import finders
 class Command(NoArgsCommand):
     """
     Command that allows to copy or symlink media files from different
-    locations to the settings.STATICFILES_ROOT.
+    locations to the settings.STATIC_ROOT.
     """
     option_list = NoArgsCommand.option_list + (
         make_option('--noinput', action='store_false', dest='interactive',
@@ -85,7 +85,7 @@ Type 'yes' to continue, or 'no' to cancel: """)
             self.stdout.write("\n%s static file%s %s to '%s'%s.\n"
                               % (actual_count, actual_count != 1 and 's' or '',
                                  symlink and 'symlinked' or 'copied',
-                                 settings.STATICFILES_ROOT,
+                                 settings.STATIC_ROOT,
                                  unmodified_count and ' (%s unmodified)'
                                  % unmodified_count or ''))
 

+ 1 - 1
django/contrib/staticfiles/management/commands/runserver.py

@@ -8,7 +8,7 @@ from django.contrib.staticfiles.handlers import StaticFilesHandler
 class Command(BaseRunserverCommand):
     option_list = BaseRunserverCommand.option_list + (
         make_option('--nostatic', action="store_false", dest='use_static_handler', default=True,
-            help='Tells Django to NOT automatically serve static files at STATICFILES_URL.'),
+            help='Tells Django to NOT automatically serve static files at STATIC_URL.'),
         make_option('--insecure', action="store_true", dest='insecure_serving', default=False,
             help='Allows serving static files even if DEBUG is False.'),
     )

+ 8 - 7
django/contrib/staticfiles/storage.py

@@ -12,21 +12,22 @@ class StaticFilesStorage(FileSystemStorage):
     Standard file system storage for site media files.
     
     The defaults for ``location`` and ``base_url`` are
-    ``STATICFILES_ROOT`` and ``STATICFILES_URL``.
+    ``STATIC_ROOT`` and ``STATIC_URL``.
     """
     def __init__(self, location=None, base_url=None, *args, **kwargs):
         if location is None:
-            location = settings.STATICFILES_ROOT
+            location = settings.STATIC_ROOT
         if base_url is None:
-            base_url = settings.STATICFILES_URL
+            base_url = settings.STATIC_URL
         if not location:
             raise ImproperlyConfigured("You're using the staticfiles app "
-                "without having set the STATICFILES_ROOT setting. Set it to "
+                "without having set the STATIC_ROOT setting. Set it to "
                 "the absolute path of the directory that holds static media.")
-        if not base_url:
+        # check for None since we might use a root URL (``/``)
+        if base_url is None:
             raise ImproperlyConfigured("You're using the staticfiles app "
-                "without having set the STATICFILES_URL setting. Set it to "
-                "URL that handles the files served from STATICFILES_ROOT.")
+                "without having set the STATIC_URL setting. Set it to "
+                "URL that handles the files served from STATIC_ROOT.")
         if settings.DEBUG:
             utils.check_settings()
         super(StaticFilesStorage, self).__init__(location, base_url, *args, **kwargs)

+ 0 - 0
django/contrib/staticfiles/templatetags/__init__.py


+ 0 - 43
django/contrib/staticfiles/templatetags/staticfiles.py

@@ -1,43 +0,0 @@
-from django import template
-from django.utils.encoding import iri_to_uri
-
-register = template.Library()
-
-class StaticFilesPrefixNode(template.Node):
-
-    def __init__(self, varname=None):
-        self.varname = varname
-
-    def render(self, context):
-        try:
-            from django.conf import settings
-        except ImportError:
-            prefix = ''
-        else:
-            prefix = iri_to_uri(settings.STATICFILES_URL)
-        if self.varname is None:
-            return prefix
-        context[self.varname] = prefix
-        return ''
-
-@register.tag
-def get_staticfiles_prefix(parser, token):
-    """
-    Populates a template variable with the prefix (settings.STATICFILES_URL).
-
-    Usage::
-
-        {% get_staticfiles_prefix [as varname] %}
-
-    Examples::
-
-        {% get_staticfiles_prefix %}
-        {% get_staticfiles_prefix as staticfiles_prefix %}
-
-    """
-    tokens = token.contents.split()
-    if len(tokens) > 1 and tokens[1] != 'as':
-        raise template.TemplateSyntaxError(
-            "First argument in '%s' must be 'as'" % tokens[0])
-    return StaticFilesPrefixNode(varname=(len(tokens) > 1 and tokens[2] or None))
-

+ 3 - 8
django/contrib/staticfiles/urls.py

@@ -18,15 +18,10 @@ def staticfiles_urlpatterns(prefix=None):
     if not settings.DEBUG:
         return []
     if prefix is None:
-        prefix = settings.STATICFILES_URL
-    if not prefix:
+        prefix = settings.STATIC_URL
+    if not prefix or '://' in prefix:
         raise ImproperlyConfigured(
-            "The prefix for the 'staticfiles_urlpatterns' helper is empty. "
-            "Make sure the STATICFILES_URL setting is set correctly.")
-    if '://' in prefix:
-        raise ImproperlyConfigured(
-            "The STATICFILES_URL setting is a full URL, not a path and "
-            "can't be used with the 'staticfiles_urlpatterns' helper.")
+            "The prefix for the 'staticfiles_urlpatterns' helper is invalid.")
     if prefix.startswith("/"):
         prefix = prefix[1:]
     return patterns('',

+ 8 - 8
django/contrib/staticfiles/utils.py

@@ -33,13 +33,13 @@ def get_files(storage, ignore_patterns=[], location=''):
 
 def check_settings():
     """
-    Checks if the MEDIA_(ROOT|URL) and STATICFILES_(ROOT|URL)
+    Checks if the MEDIA_(ROOT|URL) and STATIC_(ROOT|URL)
     settings have the same value.
     """
-    if settings.MEDIA_URL == settings.STATICFILES_URL:
-        raise ImproperlyConfigured("The MEDIA_URL and STATICFILES_URL "
-                                   "settings must have individual values")
-    if ((settings.MEDIA_ROOT and settings.STATICFILES_ROOT) and
-            (settings.MEDIA_ROOT == settings.STATICFILES_ROOT)):
-        raise ImproperlyConfigured("The MEDIA_ROOT and STATICFILES_ROOT "
-                                   "settings must have individual values")
+    if settings.MEDIA_URL == settings.STATIC_URL:
+        raise ImproperlyConfigured("The MEDIA_URL and STATIC_URL "
+                                   "settings must have different values")
+    if ((settings.MEDIA_ROOT and settings.STATIC_ROOT) and
+            (settings.MEDIA_ROOT == settings.STATIC_ROOT)):
+        raise ImproperlyConfigured("The MEDIA_ROOT and STATIC_ROOT "
+                                   "settings must have different values")

+ 7 - 0
django/core/context_processors.py

@@ -66,6 +66,13 @@ def i18n(request):
 
     return context_extras
 
+def static(request):
+    """
+    Adds static-related context variables to the context.
+
+    """
+    return {'STATIC_URL': settings.STATIC_URL}
+
 def media(request):
     """
     Adds media-related context variables to the context.

+ 14 - 14
django/core/servers/basehttp.py

@@ -17,8 +17,8 @@ import warnings
 from django.core.management.color import color_style
 from django.utils.http import http_date
 from django.utils._os import safe_join
-from django.contrib.staticfiles.handlers import StaticFilesHandler
-from django.views import static
+
+from django.contrib.staticfiles import handlers, views as static
 
 __version__ = "0.1"
 __all__ = ['WSGIServer','WSGIRequestHandler']
@@ -635,19 +635,20 @@ class WSGIRequestHandler(BaseHTTPRequestHandler):
         sys.stderr.write(msg)
 
 
-class AdminMediaHandler(StaticFilesHandler):
+class AdminMediaHandler(handlers.StaticFilesHandler):
     """
     WSGI middleware that intercepts calls to the admin media directory, as
     defined by the ADMIN_MEDIA_PREFIX setting, and serves those images.
     Use this ONLY LOCALLY, for development! This hasn't been tested for
     security and is not super efficient.
-    """
 
-    def get_media_dir(self):
+    This is pending for deprecation since 1.3.
+    """
+    def get_base_dir(self):
         import django
         return os.path.join(django.__path__[0], 'contrib', 'admin', 'media')
 
-    def get_media_url(self):
+    def get_base_url(self):
         from django.conf import settings
         return settings.ADMIN_MEDIA_PREFIX
 
@@ -655,14 +656,13 @@ class AdminMediaHandler(StaticFilesHandler):
         """
         Returns the path to the media file on disk for the given URL.
 
-        The passed URL is assumed to begin with ``media_url``.  If the
-        resultant file path is outside the media directory, then a ValueError
+        The passed URL is assumed to begin with ``self.base_url``.  If the
+        resulting file path is outside the media directory, then a ValueError
         is raised.
         """
-        # Remove ``media_url``.
-        relative_url = url[len(self.media_url[2]):]
+        relative_url = url[len(self.base_url[2]):]
         relative_path = urllib.url2pathname(relative_url)
-        return safe_join(self.media_dir, relative_path)
+        return safe_join(self.base_dir, relative_path)
 
     def serve(self, request):
         document_root, path = os.path.split(self.file_path(request.path))
@@ -673,10 +673,10 @@ class AdminMediaHandler(StaticFilesHandler):
         """
         Checks if the path should be handled. Ignores the path if:
 
-        * the host is provided as part of the media_url
-        * the request's path isn't under the media path
+        * the host is provided as part of the base_url
+        * the request's path isn't under the base path
         """
-        return path.startswith(self.media_url[2]) and not self.media_url[1]
+        return path.startswith(self.base_url[2]) and not self.base_url[1]
 
 
 def run(addr, port, wsgi_handler):

+ 13 - 7
django/forms/widgets.py

@@ -1,9 +1,13 @@
 """
 HTML Widget classes
 """
+import datetime
+from itertools import chain
+import time
+from urlparse import urljoin
+from util import flatatt
 
 import django.utils.copycompat as copy
-from itertools import chain
 from django.conf import settings
 from django.utils.datastructures import MultiValueDict, MergeDict
 from django.utils.html import escape, conditional_escape
@@ -11,10 +15,6 @@ from django.utils.translation import ugettext, ugettext_lazy
 from django.utils.encoding import StrAndUnicode, force_unicode
 from django.utils.safestring import mark_safe
 from django.utils import datetime_safe, formats
-import time
-import datetime
-from util import flatatt
-from urlparse import urljoin
 
 __all__ = (
     'Media', 'MediaDefiningClass', 'Widget', 'TextInput', 'PasswordInput',
@@ -63,10 +63,16 @@ class Media(StrAndUnicode):
                     for path in self._css[medium]]
                 for medium in media])
 
-    def absolute_path(self, path):
+    def absolute_path(self, path, prefix=None):
         if path.startswith(u'http://') or path.startswith(u'https://') or path.startswith(u'/'):
             return path
-        return urljoin(settings.MEDIA_URL,path)
+        if prefix is None:
+            if settings.STATIC_URL is None:
+                 # backwards compatibility
+                prefix = settings.MEDIA_URL
+            else:
+                prefix = settings.STATIC_URL
+        return urljoin(prefix, path)
 
     def __getitem__(self, name):
         "Returns a Media object that only contains media of the given type"

+ 84 - 0
django/templatetags/static.py

@@ -0,0 +1,84 @@
+from django import template
+from django.utils.encoding import iri_to_uri
+
+register = template.Library()
+
+class PrefixNode(template.Node):
+
+    def __repr__(self):
+        return "<PrefixNode for %r>" % self.name
+
+    def __init__(self, varname=None, name=None):
+        if name is None:
+            raise template.TemplateSyntaxError(
+                "Prefix nodes must be given a name to return.")
+        self.varname = varname
+        self.name = name
+
+    @classmethod
+    def handle_token(cls, parser, token, name):
+        """
+        Class method to parse prefix node and return a Node.
+        """
+        tokens = token.contents.split()
+        if len(tokens) > 1 and tokens[1] != 'as':
+            raise template.TemplateSyntaxError(
+                "First argument in '%s' must be 'as'" % tokens[0])
+        if len(tokens) > 1:
+            varname = tokens[2]
+        else:
+            varname = None
+        return cls(varname, name)
+
+    @classmethod
+    def handle_simple(cls, name):
+        try:
+            from django.conf import settings
+        except ImportError:
+            prefix = ''
+        else:
+            prefix = iri_to_uri(getattr(settings, name, ''))
+        return prefix
+
+    def render(self, context):
+        prefix = self.handle_simple(self.name)
+        if self.varname is None:
+            return prefix
+        context[self.varname] = prefix
+        return ''
+
+@register.tag
+def get_static_prefix(parser, token):
+    """
+    Populates a template variable with the static prefix,
+    ``settings.STATIC_URL``.
+
+    Usage::
+
+        {% get_static_prefix [as varname] %}
+
+    Examples::
+
+        {% get_static_prefix %}
+        {% get_static_prefix as static_prefix %}
+
+    """
+    return PrefixNode.handle_token(parser, token, "STATIC_URL")
+
+@register.tag
+def get_media_prefix(parser, token):
+    """
+    Populates a template variable with the static prefix,
+    ``settings.MEDIA_URL``.
+
+    Usage::
+
+        {% get_media_prefix [as varname] %}
+
+    Examples::
+
+        {% get_media_prefix %}
+        {% get_media_prefix as media_prefix %}
+
+    """
+    return PrefixNode.handle_token(parser, token, "MEDIA_URL")

+ 2 - 2
django/views/static.py

@@ -17,8 +17,8 @@ from django.http import Http404, HttpResponse, HttpResponseRedirect, HttpRespons
 from django.template import Template, Context, TemplateDoesNotExist
 from django.utils.http import http_date
 
-from django.contrib.staticfiles.views import \
-    directory_index, was_modified_since, serve as staticfiles_serve
+from django.contrib.staticfiles.views import (directory_index,
+    was_modified_since, serve as staticfiles_serve)
 
 
 def serve(request, path, document_root=None, show_indexes=False, insecure=False):

+ 30 - 30
docs/howto/static-files.txt

@@ -50,12 +50,12 @@ Here's the basic usage in a nutshell:
        First, you'll need to make sure that ``django.contrib.staticfiles`` is in
        your :setting:`INSTALLED_APPS`.
 
-       Next, you'll need to edit :setting:`STATICFILES_ROOT` to point to where
+       Next, you'll need to edit :setting:`STATIC_ROOT` to point to where
        you'd like your static media stored. For example::
 
-            STATICFILES_ROOT = "/home/jacob/projects/mysite.com/static_media"
+            STATIC_ROOT = "/home/jacob/projects/mysite.com/static_media"
 
-       You may also want to set the :setting:`STATICFILES_URL` setting at this
+       You may also want to set the :setting:`STATIC_URL` setting at this
        time, though the default value (of ``/static/``) is perfect for local
        development.
 
@@ -69,7 +69,7 @@ Here's the basic usage in a nutshell:
             ./manage.py collectstatic
 
        This'll churn through your static file storage and move them into the
-       directory given by :setting:`STATICFILES_ROOT`. (This is not necessary
+       directory given by :setting:`STATIC_ROOT`. (This is not necessary
        in local development if you are using :djadmin:`runserver` or adding
        ``staticfiles_urlpatterns`` to your URLconf; see below).
 
@@ -78,7 +78,7 @@ Here's the basic usage in a nutshell:
        If you're using the built-in development server (the
        :djadmin:`runserver` management command) and have the :setting:`DEBUG`
        setting set to ``True``, your staticfiles will automatically be served
-       from :setting:`STATICFILES_URL` in development.
+       from :setting:`STATIC_URL` in development.
 
        If you are using some other server for local development, you can
        quickly serve static media locally by adding::
@@ -98,7 +98,7 @@ Here's the basic usage in a nutshell:
 
           .. code-block:: html+django
 
-               <img src="{{ STATICFILES_URL }}images/hi.jpg />
+               <img src="{{ STATIC_URL }}images/hi.jpg />
 
        See :ref:`staticfiles-in-templates` for more details, including an
        alternate method (using a template tag).
@@ -115,7 +115,7 @@ the framework see :doc:`the staticfiles reference </ref/contrib/staticfiles>`.
    app is to make it easier to keep static files separate from user-uploaded
    files. For this reason, you will probably want to make your
    :setting:`MEDIA_ROOT` and :setting:`MEDIA_URL` different from your
-   :setting:`STATICFILES_ROOT` and :setting:`STATICFILES_URL`. You will need to
+   :setting:`STATIC_ROOT` and :setting:`STATIC_URL`. You will need to
    arrange for serving of files in :setting:`MEDIA_ROOT` yourself;
    ``staticfiles`` does not deal with user-uploaded media at all.
 
@@ -136,7 +136,7 @@ development, and it makes it *very* hard to change where you've deployed your
 media. If, for example, you wanted to switch to using a content delivery network
 (CDN), then you'd need to change more or less every single template.
 
-A far better way is to use the value of the :setting:`STATICFILES_URL` setting
+A far better way is to use the value of the :setting:`STATIC_URL` setting
 directly in your templates. This means that a switch of media servers only
 requires changing that single value. Much better!
 
@@ -147,7 +147,7 @@ With a context processor
 ------------------------
 
 The included context processor is the easy way. Simply make sure
-``'django.contrib.staticfiles.context_processors.staticfiles'`` is in your
+``'django.core.context_processors.static'`` is in your
 :setting:`TEMPLATE_CONTEXT_PROCESSORS`. It's there by default, and if you're
 editing that setting by hand it should look something like::
 
@@ -155,18 +155,18 @@ editing that setting by hand it should look something like::
         'django.core.context_processors.debug',
         'django.core.context_processors.i18n',
         'django.core.context_processors.media',
+        'django.core.context_processors.static',
         'django.contrib.auth.context_processors.auth',
         'django.contrib.messages.context_processors.messages',
-        'django.contrib.staticfiles.context_processors.staticfiles',
     )
 
-Once that's done, you can refer to :setting:`STATICFILES_URL` in your templates:
+Once that's done, you can refer to :setting:`STATIC_URL` in your templates:
 
 .. code-block:: html+django
 
-     <img src="{{ STATICFILES_URL }}images/hi.jpg />
+     <img src="{{ STATIC_URL }}images/hi.jpg />
 
-If ``{{ STATICFILES_URL }}`` isn't working in your template, you're probably not
+If ``{{ STATIC_URL }}`` isn't working in your template, you're probably not
 using :class:`~django.template.RequestContext` when rendering the template.
 
 As a brief refresher, context processors add variables into the contexts of
@@ -180,23 +180,23 @@ To see how that works, and to read more details, check out
 With a template tag
 -------------------
 
-The second option is the :ttag:`get_staticfiles_prefix` template tag. You can
+The second option is the :ttag:`get_static_prefix` template tag. You can
 use this if you're not using :class:`~django.template.RequestContext`, or if you
-need more control over exactly where and how :setting:`STATICFILES_URL` is
+need more control over exactly where and how :setting:`STATIC_URL` is
 injected into the template. Here's an example:
 
 .. code-block:: html+django
 
-    {% load staticfiles %}
-    <img src="{% get_staticfiles_prefix %}images/hi.jpg" />
+    {% load static %}
+    <img src="{% get_static_prefix %}images/hi.jpg" />
 
 There's also a second form you can use to avoid extra processing if you need the
 value multiple times:
 
 .. code-block:: html+django
 
-    {% load staticfiles %}
-    {% get_staticfiles_prefix as STATIC_PREFIX %}
+    {% load static %}
+    {% get_static_prefix as STATIC_PREFIX %}
 
     <img src="{{ STATIC_PREFIX }}images/hi.jpg" />
     <img src="{{ STATIC_PREFIX }}images/hi2.jpg" />
@@ -213,7 +213,7 @@ Thus, the ``staticfiles`` app ships with a quick and dirty helper view that you
 can use to serve files locally in development.
 
 This view is automatically enabled and will serve your static files at
-:setting:`STATICFILES_URL` when you use the built-in :djadmin:`runserver`.
+:setting:`STATIC_URL` when you use the built-in :djadmin:`runserver`.
 
 To enable this view if you are using some other server for local development,
 you'll add a couple of lines to your URLconf. The first line goes at the top of
@@ -225,11 +225,11 @@ the file, and the last line at the bottom::
 
     urlpatterns += staticfiles_urlpatterns()
 
-This will inspect your :setting:`STATICFILES_URL` and
-:setting:`STATICFILES_ROOT` settings and wire up the view to serve static media
+This will inspect your :setting:`STATIC_URL` and
+:setting:`STATIC_ROOT` settings and wire up the view to serve static media
 accordingly. Don't forget to set the :setting:`STATICFILES_DIRS` setting
 appropriately to let ``django.contrib.staticfiles`` know where to look for
-files.
+(additional) files.
 
 .. warning::
 
@@ -239,6 +239,9 @@ files.
     **insecure**. This is only intended for local development, and should
     **never be used in production**.
 
+    Additionally, your :setting:`STATIC_URL` setting can't be either empty
+    or a full URL such as ``http://static.example.com/``.
+
 For a few more details, including an alternate method of enabling this view,
 see :ref:`staticfiles-development-view`.
 
@@ -249,7 +252,7 @@ Serving static files in production
 
 The basic outline of putting static files into production is simple: run the
 :djadmin:`collectstatic` command when static media changes, then arrange for the
-collected media directory (:setting:`STATICFILES_ROOT`) to be moved to the media
+collected media directory (:setting:`STATIC_ROOT`) to be moved to the media
 server and served.
 
 Of course, as with all deployment tasks, the devil's in the details. Every
@@ -264,8 +267,8 @@ app, the basic outline gets modified to look something like:
 
     * Push your code up to the deployment server.
     * On the server, run :djadmin:`collectstatic` to move all the media into
-      :setting:`STATICFILES_ROOT`.
-    * Point your web server at :setting:`STATICFILES_ROOT`. For example, here's
+      :setting:`STATIC_ROOT`.
+    * Point your web server at :setting:`STATIC_ROOT`. For example, here's
       :ref:`how to do this under Apache and mod_wsgi <serving-media-files>`.
 
 You'll probably want to automate this process, especially if you've got multiple
@@ -322,7 +325,7 @@ Since your media server won't be running Django, you'll need to modify the
 deployment strategy to look something like:
 
     * When your media changes, run :djadmin:`collectstatic` locally.
-    * Push your local :setting:`STATICFILES_ROOT` up to the media server
+    * Push your local :setting:`STATIC_ROOT` up to the media server
       into the directory that's being served. ``rsync`` is a good
       choice for this step since it only needs to transfer the
       bits of static media that have changed.
@@ -403,9 +406,6 @@ you'll need to make a few changes:
     * The management commands ``build_static`` and ``resolve_static`` are now
       called :djadmin:`collectstatic` and :djadmin:`findstatic`.
 
-    * The settings ``STATIC_URL`` and ``STATIC_ROOT`` were renamed to
-      :setting:`STATICFILES_URL` and :setting:`STATICFILES_ROOT`.
-
     * The settings ``STATICFILES_PREPEND_LABEL_APPS``,
       ``STATICFILES_MEDIA_DIRNAMES`` and ``STATICFILES_EXCLUDED_APPS`` were
       removed.

+ 1 - 1
docs/man/django-admin.1

@@ -165,7 +165,7 @@ Do not prompt the user for input.
 Disable the development server's auto\-reloader.
 .TP
 .I \-\-nostatic
-Disable automatic serving of static files from STATICFILES_URL.
+Disable automatic serving of static files from STATIC_URL.
 .TP
 .I \-\-insecure
 Enables serving of static files even if DEBUG is False.

+ 61 - 61
docs/ref/contrib/staticfiles.txt

@@ -23,48 +23,11 @@ Settings
 
 .. highlight:: python
 
-The following settings control the behavior of the staticfiles app. Only
-:setting:`STATICFILES_ROOT` is required, but you'll probably also need to
-configure :setting:`STATICFILES_URL` as well.
-
-.. setting:: STATICFILES_ROOT
-
-STATICFILES_ROOT
-----------------
-
-Default: ``''`` (Empty string)
-
-The absolute path to the directory that the :djadmin:`collectstatic` management
-command will collect static files into, for serving from
-:setting:`STATICFILES_URL`::
-
-   STATICFILES_ROOT = "/home/example.com/static/"
-
-This is a **required setting** unless you've overridden
-:setting:`STATICFILES_STORAGE` and are using a custom storage backend.
-
-This is not a place to store your static files permanently under version
-control; you should do that in directories that will be found by your
-:setting:`STATICFILES_FINDERS` (by default, per-app ``static/`` subdirectories,
-and any directories you include in :setting:`STATICFILES_DIRS`). Files from
-those locations will be collected into :setting:`STATICFILES_ROOT`.
-
-.. setting:: STATICFILES_URL
-
-STATICFILES_URL
----------------
-
-Default: ``'/static/'``
-
-The URL that handles the files served from :setting:`STATICFILES_ROOT`, e.g.::
-
-    STATICFILES_URL = '/site_media/static/'
-
-... or perhaps::
-
-    STATICFILES_URL = 'http://static.example.com/'
+.. note::
 
-This should **always** have a trailing slash.
+    The following settings control the behavior of the staticfiles app.
+    Configuring the global settings :setting:`STATIC_ROOT` and
+    :setting:`STATIC_URL` is **required**.
 
 .. setting:: STATICFILES_DIRS
 
@@ -98,7 +61,7 @@ tuples, e.g.::
 
 With this configuration, the :djadmin:`collectstatic` management command would
 for example collect the stats files in a ``'downloads'`` directory. So
-assuming you have :setting:`STATICFILES_URL` set ``'/static/'``, this would
+assuming you have :setting:`STATIC_URL` set ``'/static/'``, this would
 allow you to refer to the file ``'/opt/webfiles/stats/polls_20101022.tar.gz'``
 with ``'/static/downloads/polls_20101022.tar.gz'`` in your templates.
 
@@ -153,14 +116,14 @@ Management Commands
 
 .. highlight:: console
 
-``django.contrib.staticfiles`` exposes two management commands.
+``django.contrib.staticfiles`` exposes three management commands.
 
 collectstatic
 -------------
 
 .. django-admin:: collectstatic
 
-Collects the static files into :setting:`STATICFILES_ROOT`.
+Collects the static files into :setting:`STATIC_ROOT`.
 
 Duplicate file names are by default resolved in a similar way to how template
 resolution works: the file that is first found in one of the specified
@@ -218,44 +181,76 @@ for each relative path, use the ``--first`` option::
 This is a debugging aid; it'll show you exactly which static file will be
 collected for a given path.
 
+runserver
+---------
+
+Overrides the core :djadmin:`runserver` command if the ``staticfiles`` app
+is :setting:`installed<INSTALLED_APPS>` and adds automatic serving of static
+files and the following new options.
+
+.. django-admin-option:: --nostatic
+
+Use the ``--nostatic`` option to disable serving of static files with the
+:doc:`staticfiles </ref/contrib/staticfiles>` app entirely. This option is
+only available if the :doc:`staticfiles </ref/contrib/staticfiles>` app is
+in your project's :setting:`INSTALLED_APPS` setting.
+
+Example usage::
+
+    django-admin.py runserver --nostatic
+
+.. django-admin-option:: --insecure
+
+Use the ``--insecure`` option to force serving of static files with the
+:doc:`staticfiles </ref/contrib/staticfiles>` app even if the :setting:`DEBUG`
+setting is ``False``. By using this you acknowledge the fact that it's
+**grossly inefficient** and probably **insecure**. This is only intended for
+local development, should **never be used in production** and is only
+available if the :doc:`staticfiles </ref/contrib/staticfiles>` app is
+in your project's :setting:`INSTALLED_APPS` setting.
+
+Example usage::
+
+    django-admin.py runserver --insecure
+
 .. currentmodule:: None
 
 Other Helpers
 =============
 
-The ``staticfiles`` context processor
--------------------------------------
+The ``static`` context processor
+--------------------------------
 
-.. function:: django.contrib.staticfiles.context_processors.staticfiles
+.. function:: django.core.context_processors.static
 
-This context processor adds the :setting:`STATICFILES_URL` into each template
-context as the variable ``{{ STATICFILES_URL }}``. To use it, make sure that
-``'django.contrib.staticfiles.context_processors.staticfiles'`` appears
-somewhere in your :setting:`TEMPLATE_CONTEXT_PROCESSORS` setting.
+This context processor adds the :setting:`STATIC_URL` into each template
+context as the variable ``{{ STATIC_URL }}``. To use it, make sure that
+``'django.core.context_processors.static'`` appears somewhere in your
+:setting:`TEMPLATE_CONTEXT_PROCESSORS` setting.
 
 Remember, only templates rendered with :class:`~django.template.RequestContext`
 will have acces to the data provided by this (and any) context processor.
 
-.. templatetag:: get_staticfiles_prefix
+.. templatetag:: get_static_prefix
 
-The ``get_staticfiles_prefix`` templatetag
-==========================================
+The ``get_static_prefix`` templatetag
+=====================================
 
 .. highlight:: html+django
 
 If you're not using :class:`~django.template.RequestContext`, or if you need
-more control over exactly where and how :setting:`STATICFILES_URL` is injected
-into the template, you can use the :ttag:`get_staticfiles_prefix` template tag
+more control over exactly where and how :setting:`STATIC_URL` is injected
+into the template, you can use the :ttag:`get_static_prefix` template tag
 instead::
 
-    {% load staticfiles %}
-    <img src="{% get_staticfiles_prefix %}images/hi.jpg" />
+    {% load static %}
+    <img src="{% get_static_prefix %}images/hi.jpg" />
 
 There's also a second form you can use to avoid extra processing if you need
 the value multiple times::
 
-    {% load staticfiles %}
-    {% get_staticfiles_prefix as STATIC_PREFIX %}
+    {% load static %}
+    {% get_static_prefix as STATIC_PREFIX %}
 
     <img src="{{ STATIC_PREFIX }}images/hi.jpg" />
     <img src="{{ STATIC_PREFIX }}images/hi2.jpg" />
@@ -292,7 +287,7 @@ primary URL configuration::
        )
 
 Note, the begin of the pattern (``r'^static/'``) should be your
-:setting:`STATICFILES_URL` setting.
+:setting:`STATIC_URL` setting.
 
 Since this is a bit finicky, there's also a helper function that'll do this for you:
 
@@ -307,3 +302,8 @@ already defined pattern list. Use it like this::
 
    urlpatterns += staticfiles_urlpatterns()
 
+.. warning::
+
+    This helper function will only work if :setting:`DEBUG` is ``True``
+    and your :setting:`STATIC_URL` setting is neither empty nor a full
+    URL such as ``http://static.example.com/``.

+ 0 - 29
docs/ref/django-admin.txt

@@ -681,35 +681,6 @@ Example usage::
 
     django-admin.py runserver --noreload
 
-.. django-admin-option:: --nostatic
-
-Use the ``--nostatic`` option to disable serving of static files with the
-:doc:`staticfiles </ref/contrib/staticfiles>` app entirely. This option is
-only available if the :doc:`staticfiles </ref/contrib/staticfiles>` app is
-in your project's :setting:`INSTALLED_APPS` setting.
-
-Example usage::
-
-    django-admin.py runserver --nostatic
-
-.. django-admin-option:: --insecure
-
-Use the ``--insecure`` option to force serving of static files with the
-:doc:`staticfiles </ref/contrib/staticfiles>` app even if the :setting:`DEBUG`
-setting is ``False``. By using this you acknowledge the fact that it's
-**grossly inefficient** and probably **insecure**. This is only intended for
-local development, should **never be used in production** and is only
-available if the :doc:`staticfiles </ref/contrib/staticfiles>` app is
-in your project's :setting:`INSTALLED_APPS` setting.
-
-See the :doc:`reference documentation of the app </ref/contrib/staticfiles>`
-for more details and learn how to :doc:`manage and deploy static files
-</howto/static-files>` correctly.
-
-Example usage::
-
-    django-admin.py runserver --insecure
-
 Examples of using different ports and addresses
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 

+ 54 - 6
docs/ref/settings.txt

@@ -53,10 +53,10 @@ Default: ``'/static/admin/'``
 
 The URL prefix for admin media -- CSS, JavaScript and images used by the Django
 administrative interface. Make sure to use a trailing slash, and to have this be
-different from the :setting:``MEDIA_URL`` setting (since the same URL cannot be
+different from the :setting:`MEDIA_URL` setting (since the same URL cannot be
 mapped onto two different sets of files). For integration with :doc:`staticfiles
 </ref/contrib/staticfiles>`, this should be the same as
-:setting:`STATICFILES_URL` followed by ``'admin/'``.
+:setting:`STATIC_URL` followed by ``'admin/'``.
 
 .. setting:: ADMINS
 
@@ -1122,12 +1122,12 @@ Default: ``''`` (Empty string)
 URL that handles the media served from :setting:`MEDIA_ROOT`, used
 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.
 
- * Good: ``"http://www.example.com/static/"``
- * Bad: ``"http://www.example.com/static"``
+ * Good: ``"http://www.example.com/media/"``
+ * Bad: ``"http://www.example.com/media"``
 
 MESSAGE_LEVEL
 -------------
@@ -1486,6 +1486,49 @@ See :doc:`/ref/contrib/sites`.
 
 .. _site framework docs: ../sites/
 
+.. setting:: STATIC_ROOT
+
+STATIC_ROOT
+-----------
+
+Default: ``''`` (Empty string)
+
+The absolute path to the directory that contains static content.
+
+Example: ``"/home/example.com/static/"``
+
+When using the :djadmin:`collectstatic` management command of the optional,
+:doc:`staticfiles</ref/contrib/staticfiles>` app this will be used to collect
+static files into and served from :setting:`STATIC_URL`.
+
+In that case this is a **required setting**, unless you've overridden
+:setting:`STATICFILES_STORAGE` and are using a custom storage backend.
+
+This is not a place to store your static files permanently under version
+control; you should do that in directories that will be found by your
+:setting:`STATICFILES_FINDERS` (by default, per-app ``static/`` subdirectories,
+and any directories you include in :setting:`STATICFILES_DIRS`). Files from
+those locations will be collected into :setting:`STATIC_ROOT`.
+
+See :doc:`/ref/contrib/staticfiles` and :setting:`STATIC_URL`.
+
+.. setting:: STATIC_URL
+
+STATIC_URL
+----------
+
+Default: ``None``
+
+URL that handles the files served from :setting:`STATIC_ROOT`.
+
+Example: ``"/site_media/static/"`` or ``"http://static.example.com/"``
+
+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>`.
+
+See :setting:`STATIC_ROOT`.
+
 .. setting:: TEMPLATE_CONTEXT_PROCESSORS
 
 TEMPLATE_CONTEXT_PROCESSORS
@@ -1496,7 +1539,8 @@ Default::
     ("django.contrib.auth.context_processors.auth",
     "django.core.context_processors.debug",
     "django.core.context_processors.i18n",
-    "django.contrib.staticfiles.context_processors.staticfiles",
+    "django.core.context_processors.media",
+    "django.core.context_processors.static",
     "django.contrib.messages.context_processors.messages")
 
 A tuple of callables that are used to populate the context in ``RequestContext``.
@@ -1513,6 +1557,10 @@ of items to be merged into the context.
     ``django.core.context_processors.auth`` to
     ``django.contrib.auth.context_processors.auth``.
 
+.. versionadded:: 1.3
+    The ``django.core.context_processors.static`` context processor
+    was added in this release.
+
 .. setting:: TEMPLATE_DEBUG
 
 TEMPLATE_DEBUG

+ 11 - 2
docs/ref/templates/api.txt

@@ -312,8 +312,8 @@ and return a dictionary of items to be merged into the context. By default,
     "django.core.context_processors.debug",
     "django.core.context_processors.i18n",
     "django.core.context_processors.media",
-    "django.contrib.messages.context_processors.messages",
-    "django.contrib.staticfiles.context_processors.staticfiles")
+    "django.core.context_processors.static",
+    "django.contrib.messages.context_processors.messages")
 
 .. versionadded:: 1.2
    In addition to these, ``RequestContext`` always uses
@@ -435,6 +435,15 @@ If :setting:`TEMPLATE_CONTEXT_PROCESSORS` contains this processor, every
 ``RequestContext`` will contain a variable ``MEDIA_URL``, providing the
 value of the :setting:`MEDIA_URL` setting.
 
+django.core.context_processors.static
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. versionadded:: 1.3
+
+If :setting:`TEMPLATE_CONTEXT_PROCESSORS` contains this processor, every
+``RequestContext`` will contain a variable ``STATIC_URL``, providing the
+value of the :setting:`STATIC_URL` setting.
+
 django.core.context_processors.csrf
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 

+ 2 - 4
docs/releases/1.3-alpha-1.txt

@@ -21,7 +21,7 @@ changes`_ and an easy upgrade path from Django 1.2.
 
 .. _new features: `What's new in Django 1.3 alpha 1`_
 
-.. _backwards incompatible changes: backwards-incompatible-changes-1.3_
+.. _backwards incompatible changes: backwards-incompatible-changes-1.3-alpha-1_
 
 What's new in Django 1.3 alpha 1
 ================================
@@ -161,7 +161,7 @@ requests. These include:
       easier to test the database activity associated with a view.
 
 
-.. _backwards-incompatible-changes-1.3:
+.. _backwards-incompatible-changes-1.3-alpha-1:
 
 Backwards-incompatible changes in 1.3 alpha 1
 =============================================
@@ -270,8 +270,6 @@ local flavors:
       official designation "Aceh (ACE)".
 
 
-.. _deprecated-features-1.3:
-
 Features deprecated in 1.3
 ==========================
 

+ 41 - 4
docs/releases/1.3-alpha-2.txt

@@ -13,10 +13,6 @@ prior to the final 1.3 release.
 As such, this release is *not* intended for production use, and any such use
 is discouraged.
 
-.. _new features: `What's new in Django 1.3 alpha 2`_
-
-.. _backwards incompatible changes: backwards-incompatible-changes-1.3-alpha-2_
-
 What's new in Django 1.3 alpha 2
 ================================
 
@@ -43,6 +39,47 @@ See the :doc:`reference documentation of the app </ref/contrib/staticfiles>`
 for more details or learn how to :doc:`manage static files
 </howto/static-files>`.
 
+Backwards-incompatible changes in 1.3 alpha 2
+=============================================
+
+Introduction of STATIC_URL and STATIC_ROOT settings
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The newly introduced :doc:`/ref/contrib/staticfiles` app extends Django's
+abilities to handle static app and project files, required the additon of
+settings to refer to those files in templates and code, especially in
+contrast to the :setting:`MEDIA_URL` and :setting:`MEDIA_ROOT` settings that
+refer to user-uploaded files.
+
+Prior to 1.3 alpha 2 these settings were called ``STATICFILES_URL`` and
+``STATICFILES_ROOT`` to follow the naming scheme for app centric settings.
+Based on feedback from the community it became apparent that those settings
+created confusion, especially given the fact handling static files is also
+desired outside the use of the optional ``staticfiles`` app.
+
+As a result, we take the followig steps to rectify the issue:
+
+  * Two new global settings that will be used by -- **but are not limited
+    to** -- the :doc:`staticfiles</ref/contrib/staticfiles>` app:
+
+    * :setting:`STATIC_ROOT` (formally ``STATICFILES_ROOT``)
+
+    * :setting:`STATIC_URL` (formally ``STATICFILES_URL``)
+
+  * Moving the
+    ``django.contrib.staticfiles.templatetags.staticfiles.get_staticfiles_prefix``
+    template tag to the core (``django.templatetags.static``) and renaming
+    it to :ttag:`get_static_prefix`.
+
+  * Moving the context processor
+    ``django.contrib.staticfiles.context_processors.staticfiles`` to the
+    core (``django.core.context_processors.static``) and renaming it to
+    :func:`~django.core.context_processors.static`.
+
+  * :ref:`form-media-paths` will use :setting:`STATIC_URL` as the prefix
+    **if the value is not None**, and falls back to the previously used
+    :setting:`MEDIA_URL`.
+
 The Django 1.3 roadmap
 ======================
 

+ 1 - 1
docs/releases/1.3.txt

@@ -59,7 +59,7 @@ In previous versions of Django, it was common to place static assets in
 app is to make it easier to keep static files separate from user-uploaded
 files. For this reason, you will probably want to make your
 :setting:`MEDIA_ROOT` and :setting:`MEDIA_URL` different from your
-:setting:`STATICFILES_ROOT` and :setting:`STATICFILES_URL`. You will need to
+:setting:`STATIC_ROOT` and :setting:`STATIC_URL`. You will need to
 arrange for serving of files in :setting:`MEDIA_ROOT` yourself;
 ``staticfiles`` does not deal with user-uploaded media at all.
 

+ 1 - 0
docs/releases/index.txt

@@ -64,6 +64,7 @@ notes.
 .. toctree::
    :maxdepth: 1
 
+   1.3-alpha-2
    1.3-alpha-1
    1.2-rc-1
    1.2-beta-1

+ 32 - 9
docs/topics/forms/media.txt

@@ -190,28 +190,51 @@ also be defined in a dynamic fashion::
 See the section on `Media objects`_ for more details on how to construct
 return values for dynamic media properties.
 
+.. _form-media-paths:
+
 Paths in media definitions
 --------------------------
 
+.. versionchanged:: 1.3
+
 Paths used to specify media can be either relative or absolute. If a path
 starts with '/', 'http://' or 'https://', it will be interpreted as an absolute
 path, and left as-is. All other paths will be prepended with the value of
-``settings.MEDIA_URL``. For example, if the MEDIA_URL for your site was
-``http://media.example.com/``::
+the appropriate prefix.
+
+As part of the introduction of the
+:doc:`staticfiles app </ref/contrib/staticfiles>` two new settings were added
+to refer to "static content" (images, CSS, Javascript, etc.) that are needed
+to render a complete web page: :setting:`STATIC_URL` and :setting:`STATIC_ROOT`.
+
+To find the appropriate prefix to use, Django will check if the
+:setting:`STATIC_URL` setting is not ``None`` and automatically fall back
+to using :setting:`MEDIA_URL`. For example, if the :setting:`MEDIA_URL` for
+your site was ``'http://uploads.example.com/'`` and :setting:`STATIC_URL`
+was ``None``::
+
+    >>> class CalendarWidget(forms.TextInput):
+            class Media:
+                css = {
+                    'all': ('/css/pretty.css',),
+                }
+                js = ('animations.js', 'http://othersite.com/actions.js')
 
-    class CalendarWidget(forms.TextInput):
-        class Media:
-            css = {
-                'all': ('/css/pretty.css',),
-            }
-            js = ('animations.js', 'http://othersite.com/actions.js')
+    >>> w = CalendarWidget()
+    >>> print w.media
+    <link href="/css/pretty.css" type="text/css" media="all" rel="stylesheet" />
+    <script type="text/javascript" src="http://uploads.example.com/animations.js"></script>
+    <script type="text/javascript" src="http://othersite.com/actions.js"></script>
+
+But if :setting:`STATIC_URL` is ``'http://static.example.com/'``::
 
     >>> w = CalendarWidget()
     >>> print w.media
     <link href="/css/pretty.css" type="text/css" media="all" rel="stylesheet" />
-    <script type="text/javascript" src="http://media.example.com/animations.js"></script>
+    <script type="text/javascript" src="http://static.example.com/animations.js"></script>
     <script type="text/javascript" src="http://othersite.com/actions.js"></script>
 
+
 Media objects
 -------------
 

+ 460 - 0
tests/regressiontests/forms/tests/media.py

@@ -458,3 +458,463 @@ class FormsMediaTestCase(TestCase):
 <link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
 <link href="/path/to/css3" type="text/css" media="all" rel="stylesheet" />
 <link href="/some/form/css" type="text/css" media="all" rel="stylesheet" />""")
+
+
+class StaticFormsMediaTestCase(TestCase):
+    # Tests for the media handling on widgets and forms
+    def setUp(self):
+        super(StaticFormsMediaTestCase, self).setUp()
+        self.original_media_url = settings.MEDIA_URL
+        self.original_static_url = settings.STATIC_URL
+        settings.MEDIA_URL = 'http://media.example.com/static/'
+        settings.STATIC_URL = 'http://media.example.com/static/'
+
+    def tearDown(self):
+        settings.MEDIA_URL = self.original_media_url
+        settings.STATIC_URL = self.original_static_url
+        super(StaticFormsMediaTestCase, self).tearDown()
+
+    def test_construction(self):
+        # Check construction of media objects
+        m = Media(css={'all': ('path/to/css1','/path/to/css2')}, js=('/path/to/js1','http://media.other.com/path/to/js2','https://secure.other.com/path/to/js3'))
+        self.assertEqual(str(m), """<link href="http://media.example.com/static/path/to/css1" type="text/css" media="all" rel="stylesheet" />
+<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
+<script type="text/javascript" src="/path/to/js1"></script>
+<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
+<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>""")
+
+        class Foo:
+            css = {
+               'all': ('path/to/css1','/path/to/css2')
+            }
+            js = ('/path/to/js1','http://media.other.com/path/to/js2','https://secure.other.com/path/to/js3')
+
+        m3 = Media(Foo)
+        self.assertEqual(str(m3), """<link href="http://media.example.com/static/path/to/css1" type="text/css" media="all" rel="stylesheet" />
+<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
+<script type="text/javascript" src="/path/to/js1"></script>
+<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
+<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>""")
+
+        # A widget can exist without a media definition
+        class MyWidget(TextInput):
+            pass
+
+        w = MyWidget()
+        self.assertEqual(str(w.media), '')
+
+    def test_media_dsl(self):
+        ###############################################################
+        # DSL Class-based media definitions
+        ###############################################################
+
+        # A widget can define media if it needs to.
+        # Any absolute path will be preserved; relative paths are combined
+        # with the value of settings.MEDIA_URL
+        class MyWidget1(TextInput):
+            class Media:
+                css = {
+                   'all': ('path/to/css1','/path/to/css2')
+                }
+                js = ('/path/to/js1','http://media.other.com/path/to/js2','https://secure.other.com/path/to/js3')
+
+        w1 = MyWidget1()
+        self.assertEqual(str(w1.media), """<link href="http://media.example.com/static/path/to/css1" type="text/css" media="all" rel="stylesheet" />
+<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
+<script type="text/javascript" src="/path/to/js1"></script>
+<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
+<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>""")
+
+        # Media objects can be interrogated by media type
+        self.assertEqual(str(w1.media['css']), """<link href="http://media.example.com/static/path/to/css1" type="text/css" media="all" rel="stylesheet" />
+<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />""")
+
+        self.assertEqual(str(w1.media['js']), """<script type="text/javascript" src="/path/to/js1"></script>
+<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
+<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>""")
+
+    def test_combine_media(self):
+        # Media objects can be combined. Any given media resource will appear only
+        # once. Duplicated media definitions are ignored.
+        class MyWidget1(TextInput):
+            class Media:
+                css = {
+                   'all': ('path/to/css1','/path/to/css2')
+                }
+                js = ('/path/to/js1','http://media.other.com/path/to/js2','https://secure.other.com/path/to/js3')
+
+        class MyWidget2(TextInput):
+            class Media:
+                css = {
+                   'all': ('/path/to/css2','/path/to/css3')
+                }
+                js = ('/path/to/js1','/path/to/js4')
+
+        class MyWidget3(TextInput):
+            class Media:
+                css = {
+                   'all': ('/path/to/css3','path/to/css1')
+                }
+                js = ('/path/to/js1','/path/to/js4')
+
+        w1 = MyWidget1()
+        w2 = MyWidget2()
+        w3 = MyWidget3()
+        self.assertEqual(str(w1.media + w2.media + w3.media), """<link href="http://media.example.com/static/path/to/css1" type="text/css" media="all" rel="stylesheet" />
+<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
+<link href="/path/to/css3" type="text/css" media="all" rel="stylesheet" />
+<script type="text/javascript" src="/path/to/js1"></script>
+<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
+<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
+<script type="text/javascript" src="/path/to/js4"></script>""")
+
+        # Check that media addition hasn't affected the original objects
+        self.assertEqual(str(w1.media), """<link href="http://media.example.com/static/path/to/css1" type="text/css" media="all" rel="stylesheet" />
+<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
+<script type="text/javascript" src="/path/to/js1"></script>
+<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
+<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>""")
+
+        # Regression check for #12879: specifying the same CSS or JS file
+        # multiple times in a single Media instance should result in that file
+        # only being included once.
+        class MyWidget4(TextInput):
+            class Media:
+                css = {'all': ('/path/to/css1', '/path/to/css1')}
+                js = ('/path/to/js1', '/path/to/js1')
+
+        w4 = MyWidget4()
+        self.assertEqual(str(w4.media), """<link href="/path/to/css1" type="text/css" media="all" rel="stylesheet" />
+<script type="text/javascript" src="/path/to/js1"></script>""")
+
+    def test_media_property(self):
+        ###############################################################
+        # Property-based media definitions
+        ###############################################################
+
+        # Widget media can be defined as a property
+        class MyWidget4(TextInput):
+            def _media(self):
+                return Media(css={'all': ('/some/path',)}, js = ('/some/js',))
+            media = property(_media)
+
+        w4 = MyWidget4()
+        self.assertEqual(str(w4.media), """<link href="/some/path" type="text/css" media="all" rel="stylesheet" />
+<script type="text/javascript" src="/some/js"></script>""")
+
+        # Media properties can reference the media of their parents
+        class MyWidget5(MyWidget4):
+            def _media(self):
+                return super(MyWidget5, self).media + Media(css={'all': ('/other/path',)}, js = ('/other/js',))
+            media = property(_media)
+
+        w5 = MyWidget5()
+        self.assertEqual(str(w5.media), """<link href="/some/path" type="text/css" media="all" rel="stylesheet" />
+<link href="/other/path" type="text/css" media="all" rel="stylesheet" />
+<script type="text/javascript" src="/some/js"></script>
+<script type="text/javascript" src="/other/js"></script>""")
+
+    def test_media_property_parent_references(self):
+        # Media properties can reference the media of their parents,
+        # even if the parent media was defined using a class
+        class MyWidget1(TextInput):
+            class Media:
+                css = {
+                   'all': ('path/to/css1','/path/to/css2')
+                }
+                js = ('/path/to/js1','http://media.other.com/path/to/js2','https://secure.other.com/path/to/js3')
+
+        class MyWidget6(MyWidget1):
+            def _media(self):
+                return super(MyWidget6, self).media + Media(css={'all': ('/other/path',)}, js = ('/other/js',))
+            media = property(_media)
+
+        w6 = MyWidget6()
+        self.assertEqual(str(w6.media), """<link href="http://media.example.com/static/path/to/css1" type="text/css" media="all" rel="stylesheet" />
+<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
+<link href="/other/path" type="text/css" media="all" rel="stylesheet" />
+<script type="text/javascript" src="/path/to/js1"></script>
+<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
+<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
+<script type="text/javascript" src="/other/js"></script>""")
+
+    def test_media_inheritance(self):
+        ###############################################################
+        # Inheritance of media
+        ###############################################################
+
+        # If a widget extends another but provides no media definition, it inherits the parent widget's media
+        class MyWidget1(TextInput):
+            class Media:
+                css = {
+                   'all': ('path/to/css1','/path/to/css2')
+                }
+                js = ('/path/to/js1','http://media.other.com/path/to/js2','https://secure.other.com/path/to/js3')
+
+        class MyWidget7(MyWidget1):
+            pass
+
+        w7 = MyWidget7()
+        self.assertEqual(str(w7.media), """<link href="http://media.example.com/static/path/to/css1" type="text/css" media="all" rel="stylesheet" />
+<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
+<script type="text/javascript" src="/path/to/js1"></script>
+<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
+<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>""")
+
+        # If a widget extends another but defines media, it extends the parent widget's media by default
+        class MyWidget8(MyWidget1):
+            class Media:
+                css = {
+                   'all': ('/path/to/css3','path/to/css1')
+                }
+                js = ('/path/to/js1','/path/to/js4')
+
+        w8 = MyWidget8()
+        self.assertEqual(str(w8.media), """<link href="http://media.example.com/static/path/to/css1" type="text/css" media="all" rel="stylesheet" />
+<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
+<link href="/path/to/css3" type="text/css" media="all" rel="stylesheet" />
+<script type="text/javascript" src="/path/to/js1"></script>
+<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
+<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
+<script type="text/javascript" src="/path/to/js4"></script>""")
+
+    def test_media_inheritance_from_property(self):
+        # If a widget extends another but defines media, it extends the parents widget's media,
+        # even if the parent defined media using a property.
+        class MyWidget1(TextInput):
+            class Media:
+                css = {
+                   'all': ('path/to/css1','/path/to/css2')
+                }
+                js = ('/path/to/js1','http://media.other.com/path/to/js2','https://secure.other.com/path/to/js3')
+
+        class MyWidget4(TextInput):
+            def _media(self):
+                return Media(css={'all': ('/some/path',)}, js = ('/some/js',))
+            media = property(_media)
+
+        class MyWidget9(MyWidget4):
+            class Media:
+                css = {
+                    'all': ('/other/path',)
+                }
+                js = ('/other/js',)
+
+        w9 = MyWidget9()
+        self.assertEqual(str(w9.media), """<link href="/some/path" type="text/css" media="all" rel="stylesheet" />
+<link href="/other/path" type="text/css" media="all" rel="stylesheet" />
+<script type="text/javascript" src="/some/js"></script>
+<script type="text/javascript" src="/other/js"></script>""")
+
+        # A widget can disable media inheritance by specifying 'extend=False'
+        class MyWidget10(MyWidget1):
+            class Media:
+                extend = False
+                css = {
+                   'all': ('/path/to/css3','path/to/css1')
+                }
+                js = ('/path/to/js1','/path/to/js4')
+
+        w10 = MyWidget10()
+        self.assertEqual(str(w10.media), """<link href="/path/to/css3" type="text/css" media="all" rel="stylesheet" />
+<link href="http://media.example.com/static/path/to/css1" type="text/css" media="all" rel="stylesheet" />
+<script type="text/javascript" src="/path/to/js1"></script>
+<script type="text/javascript" src="/path/to/js4"></script>""")
+
+    def test_media_inheritance_extends(self):
+        # A widget can explicitly enable full media inheritance by specifying 'extend=True'
+        class MyWidget1(TextInput):
+            class Media:
+                css = {
+                   'all': ('path/to/css1','/path/to/css2')
+                }
+                js = ('/path/to/js1','http://media.other.com/path/to/js2','https://secure.other.com/path/to/js3')
+
+        class MyWidget11(MyWidget1):
+            class Media:
+                extend = True
+                css = {
+                   'all': ('/path/to/css3','path/to/css1')
+                }
+                js = ('/path/to/js1','/path/to/js4')
+
+        w11 = MyWidget11()
+        self.assertEqual(str(w11.media), """<link href="http://media.example.com/static/path/to/css1" type="text/css" media="all" rel="stylesheet" />
+<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
+<link href="/path/to/css3" type="text/css" media="all" rel="stylesheet" />
+<script type="text/javascript" src="/path/to/js1"></script>
+<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
+<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
+<script type="text/javascript" src="/path/to/js4"></script>""")
+
+    def test_media_inheritance_single_type(self):
+        # A widget can enable inheritance of one media type by specifying extend as a tuple
+        class MyWidget1(TextInput):
+            class Media:
+                css = {
+                   'all': ('path/to/css1','/path/to/css2')
+                }
+                js = ('/path/to/js1','http://media.other.com/path/to/js2','https://secure.other.com/path/to/js3')
+
+        class MyWidget12(MyWidget1):
+            class Media:
+                extend = ('css',)
+                css = {
+                   'all': ('/path/to/css3','path/to/css1')
+                }
+                js = ('/path/to/js1','/path/to/js4')
+
+        w12 = MyWidget12()
+        self.assertEqual(str(w12.media), """<link href="http://media.example.com/static/path/to/css1" type="text/css" media="all" rel="stylesheet" />
+<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
+<link href="/path/to/css3" type="text/css" media="all" rel="stylesheet" />
+<script type="text/javascript" src="/path/to/js1"></script>
+<script type="text/javascript" src="/path/to/js4"></script>""")
+
+    def test_multi_media(self):
+        ###############################################################
+        # Multi-media handling for CSS
+        ###############################################################
+
+        # A widget can define CSS media for multiple output media types
+        class MultimediaWidget(TextInput):
+            class Media:
+                css = {
+                   'screen, print': ('/file1','/file2'),
+                   'screen': ('/file3',),
+                   'print': ('/file4',)
+                }
+                js = ('/path/to/js1','/path/to/js4')
+
+        multimedia = MultimediaWidget()
+        self.assertEqual(str(multimedia.media), """<link href="/file4" type="text/css" media="print" rel="stylesheet" />
+<link href="/file3" type="text/css" media="screen" rel="stylesheet" />
+<link href="/file1" type="text/css" media="screen, print" rel="stylesheet" />
+<link href="/file2" type="text/css" media="screen, print" rel="stylesheet" />
+<script type="text/javascript" src="/path/to/js1"></script>
+<script type="text/javascript" src="/path/to/js4"></script>""")
+
+    def test_multi_widget(self):
+        ###############################################################
+        # Multiwidget media handling
+        ###############################################################
+
+        class MyWidget1(TextInput):
+            class Media:
+                css = {
+                   'all': ('path/to/css1','/path/to/css2')
+                }
+                js = ('/path/to/js1','http://media.other.com/path/to/js2','https://secure.other.com/path/to/js3')
+
+        class MyWidget2(TextInput):
+            class Media:
+                css = {
+                   'all': ('/path/to/css2','/path/to/css3')
+                }
+                js = ('/path/to/js1','/path/to/js4')
+
+        class MyWidget3(TextInput):
+            class Media:
+                css = {
+                   'all': ('/path/to/css3','path/to/css1')
+                }
+                js = ('/path/to/js1','/path/to/js4')
+
+        # MultiWidgets have a default media definition that gets all the
+        # media from the component widgets
+        class MyMultiWidget(MultiWidget):
+            def __init__(self, attrs=None):
+                widgets = [MyWidget1, MyWidget2, MyWidget3]
+                super(MyMultiWidget, self).__init__(widgets, attrs)
+
+        mymulti = MyMultiWidget()
+        self.assertEqual(str(mymulti.media), """<link href="http://media.example.com/static/path/to/css1" type="text/css" media="all" rel="stylesheet" />
+<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
+<link href="/path/to/css3" type="text/css" media="all" rel="stylesheet" />
+<script type="text/javascript" src="/path/to/js1"></script>
+<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
+<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
+<script type="text/javascript" src="/path/to/js4"></script>""")
+
+    def test_form_media(self):
+        ###############################################################
+        # Media processing for forms
+        ###############################################################
+
+        class MyWidget1(TextInput):
+            class Media:
+                css = {
+                   'all': ('path/to/css1','/path/to/css2')
+                }
+                js = ('/path/to/js1','http://media.other.com/path/to/js2','https://secure.other.com/path/to/js3')
+
+        class MyWidget2(TextInput):
+            class Media:
+                css = {
+                   'all': ('/path/to/css2','/path/to/css3')
+                }
+                js = ('/path/to/js1','/path/to/js4')
+
+        class MyWidget3(TextInput):
+            class Media:
+                css = {
+                   'all': ('/path/to/css3','path/to/css1')
+                }
+                js = ('/path/to/js1','/path/to/js4')
+
+        # You can ask a form for the media required by its widgets.
+        class MyForm(Form):
+            field1 = CharField(max_length=20, widget=MyWidget1())
+            field2 = CharField(max_length=20, widget=MyWidget2())
+        f1 = MyForm()
+        self.assertEqual(str(f1.media), """<link href="http://media.example.com/static/path/to/css1" type="text/css" media="all" rel="stylesheet" />
+<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
+<link href="/path/to/css3" type="text/css" media="all" rel="stylesheet" />
+<script type="text/javascript" src="/path/to/js1"></script>
+<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
+<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
+<script type="text/javascript" src="/path/to/js4"></script>""")
+
+        # Form media can be combined to produce a single media definition.
+        class AnotherForm(Form):
+            field3 = CharField(max_length=20, widget=MyWidget3())
+        f2 = AnotherForm()
+        self.assertEqual(str(f1.media + f2.media), """<link href="http://media.example.com/static/path/to/css1" type="text/css" media="all" rel="stylesheet" />
+<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
+<link href="/path/to/css3" type="text/css" media="all" rel="stylesheet" />
+<script type="text/javascript" src="/path/to/js1"></script>
+<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
+<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
+<script type="text/javascript" src="/path/to/js4"></script>""")
+
+        # Forms can also define media, following the same rules as widgets.
+        class FormWithMedia(Form):
+            field1 = CharField(max_length=20, widget=MyWidget1())
+            field2 = CharField(max_length=20, widget=MyWidget2())
+            class Media:
+                js = ('/some/form/javascript',)
+                css = {
+                    'all': ('/some/form/css',)
+                }
+        f3 = FormWithMedia()
+        self.assertEqual(str(f3.media), """<link href="http://media.example.com/static/path/to/css1" type="text/css" media="all" rel="stylesheet" />
+<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
+<link href="/path/to/css3" type="text/css" media="all" rel="stylesheet" />
+<link href="/some/form/css" type="text/css" media="all" rel="stylesheet" />
+<script type="text/javascript" src="/path/to/js1"></script>
+<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
+<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
+<script type="text/javascript" src="/path/to/js4"></script>
+<script type="text/javascript" src="/some/form/javascript"></script>""")
+
+        # Media works in templates
+        from django.template import Template, Context
+        self.assertEqual(Template("{{ form.media.js }}{{ form.media.css }}").render(Context({'form': f3})), """<script type="text/javascript" src="/path/to/js1"></script>
+<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
+<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
+<script type="text/javascript" src="/path/to/js4"></script>
+<script type="text/javascript" src="/some/form/javascript"></script><link href="http://media.example.com/static/path/to/css1" type="text/css" media="all" rel="stylesheet" />
+<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
+<link href="/path/to/css3" type="text/css" media="all" rel="stylesheet" />
+<link href="/some/form/css" type="text/css" media="all" rel="stylesheet" />""")
+

+ 15 - 35
tests/regressiontests/staticfiles_tests/tests.py

@@ -23,8 +23,8 @@ class StaticFilesTestCase(TestCase):
     Test case with a couple utility assertions.
     """
     def setUp(self):
-        self.old_staticfiles_url = settings.STATICFILES_URL
-        self.old_staticfiles_root = settings.STATICFILES_ROOT
+        self.old_static_url = settings.STATIC_URL
+        self.old_static_root = settings.STATIC_ROOT
         self.old_staticfiles_dirs = settings.STATICFILES_DIRS
         self.old_staticfiles_finders = settings.STATICFILES_FINDERS
         self.old_media_root = settings.MEDIA_ROOT
@@ -40,8 +40,8 @@ class StaticFilesTestCase(TestCase):
         settings.DEBUG = True
         settings.MEDIA_ROOT =  os.path.join(site_media, 'media')
         settings.MEDIA_URL = '/media/'
-        settings.STATICFILES_ROOT = os.path.join(site_media, 'static')
-        settings.STATICFILES_URL = '/static/'
+        settings.STATIC_ROOT = os.path.join(site_media, 'static')
+        settings.STATIC_URL = '/static/'
         settings.ADMIN_MEDIA_PREFIX = '/static/admin/'
         settings.STATICFILES_DIRS = (
             os.path.join(TEST_ROOT, 'project', 'documents'),
@@ -52,6 +52,7 @@ class StaticFilesTestCase(TestCase):
             'django.contrib.staticfiles.finders.DefaultStorageFinder',
         )
         settings.INSTALLED_APPS = [
+            "django.contrib.staticfiles",
             "regressiontests.staticfiles_tests",
         ]
 
@@ -65,8 +66,8 @@ class StaticFilesTestCase(TestCase):
         settings.MEDIA_ROOT = self.old_media_root
         settings.MEDIA_URL = self.old_media_url
         settings.ADMIN_MEDIA_PREFIX = self.old_admin_media_prefix
-        settings.STATICFILES_ROOT = self.old_staticfiles_root
-        settings.STATICFILES_URL = self.old_staticfiles_url
+        settings.STATIC_ROOT = self.old_static_root
+        settings.STATIC_URL = self.old_static_url
         settings.STATICFILES_DIRS = self.old_staticfiles_dirs
         settings.STATICFILES_FINDERS = self.old_staticfiles_finders
         settings.INSTALLED_APPS = self.old_installed_apps
@@ -91,13 +92,13 @@ class BuildStaticTestCase(StaticFilesTestCase):
     def setUp(self):
         super(BuildStaticTestCase, self).setUp()
         self.old_staticfiles_storage = settings.STATICFILES_STORAGE
-        self.old_root = settings.STATICFILES_ROOT
-        settings.STATICFILES_ROOT = tempfile.mkdtemp()
+        self.old_root = settings.STATIC_ROOT
+        settings.STATIC_ROOT = tempfile.mkdtemp()
         self.run_collectstatic()
 
     def tearDown(self):
-        shutil.rmtree(settings.STATICFILES_ROOT)
-        settings.STATICFILES_ROOT = self.old_root
+        shutil.rmtree(settings.STATIC_ROOT)
+        settings.STATIC_ROOT = self.old_root
         super(BuildStaticTestCase, self).tearDown()
 
     def run_collectstatic(self, **kwargs):
@@ -106,7 +107,7 @@ class BuildStaticTestCase(StaticFilesTestCase):
 
     def _get_file(self, filepath):
         assert filepath, 'filepath is empty.'
-        filepath = os.path.join(settings.STATICFILES_ROOT, filepath)
+        filepath = os.path.join(settings.STATIC_ROOT, filepath)
         f = open(filepath)
         try:
             return f.read()
@@ -231,7 +232,7 @@ class TestBuildStaticDryRun(BuildStaticTestCase):
         """
         With --dry-run, no files created in destination dir.
         """
-        self.assertEquals(os.listdir(settings.STATICFILES_ROOT), [])
+        self.assertEquals(os.listdir(settings.STATIC_ROOT), [])
 
 
 if sys.platform != 'win32':
@@ -251,7 +252,7 @@ if sys.platform != 'win32':
             With ``--link``, symbolic links are created.
 
             """
-            self.failUnless(os.path.islink(os.path.join(settings.STATICFILES_ROOT, 'test.txt')))
+            self.failUnless(os.path.islink(os.path.join(settings.STATIC_ROOT, 'test.txt')))
 
 
 class TestServeStatic(StaticFilesTestCase):
@@ -262,7 +263,7 @@ class TestServeStatic(StaticFilesTestCase):
 
     def _response(self, filepath):
         return self.client.get(
-            posixpath.join(settings.STATICFILES_URL, filepath))
+            posixpath.join(settings.STATIC_URL, filepath))
 
     def assertFileContains(self, filepath, text):
         self.assertContains(self._response(filepath), text)
@@ -372,24 +373,3 @@ class TestMiscFinder(TestCase):
             finders.get_finder, "django.contrib.staticfiles.finders.FooBarFinder")
         self.assertRaises(ImproperlyConfigured,
             finders.get_finder, "foo.bar.FooBarFinder")
-
-
-class TemplateTagTest(TestCase):
-    def test_get_staticfiles_prefix(self):
-        """
-        Test the get_staticfiles_prefix helper return the STATICFILES_URL setting.
-        """
-        self.assertEquals(Template(
-            "{% load staticfiles %}"
-            "{% get_staticfiles_prefix %}"
-        ).render(Context()), settings.STATICFILES_URL)
-
-    def test_get_staticfiles_prefix_with_as(self):
-        """
-        Test the get_staticfiles_prefix helper return the STATICFILES_URL setting.
-        """
-        self.assertEquals(Template(
-            "{% load staticfiles %}"
-            "{% get_staticfiles_prefix as staticfiles_prefix %}"
-            "{{ staticfiles_prefix }}"
-        ).render(Context()), settings.STATICFILES_URL)

+ 20 - 6
tests/regressiontests/templates/tests.py

@@ -114,6 +114,16 @@ class UTF8Class:
         return u'ŠĐĆŽćžšđ'.encode('utf-8')
 
 class Templates(unittest.TestCase):
+    def setUp(self):
+        self.old_static_url = settings.STATIC_URL
+        self.old_media_url = settings.MEDIA_URL
+        settings.STATIC_URL = u"/static/"
+        settings.MEDIA_URL = u"/media/"
+
+    def tearDown(self):
+        settings.STATIC_URL = self.old_static_url
+        settings.MEDIA_URL = self.old_media_url
+
     def test_loaders_security(self):
         ad_loader = app_directories.Loader()
         fs_loader = filesystem.Loader()
@@ -1328,24 +1338,28 @@ class Templates(unittest.TestCase):
             'autoescape-filtertag01': ("{{ first }}{% filter safe %}{{ first }} x<y{% endfilter %}", {"first": "<a>"}, template.TemplateSyntaxError),
 
             # ifqeual compares unescaped vales.
-            'autoescape-ifequal01': ('{% ifequal var "this & that" %}yes{% endifequal %}', { "var": "this & that" }, "yes" ),
+            'autoescape-ifequal01': ('{% ifequal var "this & that" %}yes{% endifequal %}', { "var": "this & that" }, "yes"),
 
             # Arguments to filters are 'safe' and manipulate their input unescaped.
             'autoescape-filters01': ('{{ var|cut:"&" }}', { "var": "this & that" }, "this  that" ),
-            'autoescape-filters02': ('{{ var|join:" & \" }}', { "var": ("Tom", "Dick", "Harry") }, "Tom & Dick & Harry" ),
+            'autoescape-filters02': ('{{ var|join:" & \" }}', { "var": ("Tom", "Dick", "Harry") }, "Tom & Dick & Harry"),
 
             # Literal strings are safe.
-            'autoescape-literals01': ('{{ "this & that" }}',{}, "this & that" ),
+            'autoescape-literals01': ('{{ "this & that" }}',{}, "this & that"),
 
             # Iterating over strings outputs safe characters.
-            'autoescape-stringiterations01': ('{% for l in var %}{{ l }},{% endfor %}', {'var': 'K&R'}, "K,&amp;,R," ),
+            'autoescape-stringiterations01': ('{% for l in var %}{{ l }},{% endfor %}', {'var': 'K&R'}, "K,&amp;,R,"),
 
             # Escape requirement survives lookup.
-            'autoescape-lookup01': ('{{ var.key }}', { "var": {"key": "this & that" }}, "this &amp; that" ),
+            'autoescape-lookup01': ('{{ var.key }}', { "var": {"key": "this & that" }}, "this &amp; that"),
 
+            # Static template tags
+            'static-prefixtag01': ('{% load static %}{% get_static_prefix %}', {}, settings.STATIC_URL),
+            'static-prefixtag02': ('{% load static %}{% get_static_prefix as static_prefix %}{{ static_prefix }}', {}, settings.STATIC_URL),
+            'static-prefixtag03': ('{% load static %}{% get_media_prefix %}', {}, settings.MEDIA_URL),
+            'static-prefixtag04': ('{% load static %}{% get_media_prefix as media_prefix %}{{ media_prefix }}', {}, settings.MEDIA_URL),
         }
 
-
 class TemplateTagLoading(unittest.TestCase):
 
     def setUp(self):