Browse Source

Fixed #21221 -- Made form Media and static template tag use staticfiles if installed.

Johannes Hoppe 9 years ago
parent
commit
cf546e11ac
34 changed files with 145 additions and 144 deletions
  1. 1 2
      django/contrib/admin/helpers.py
  2. 2 3
      django/contrib/admin/options.py
  3. 1 1
      django/contrib/admin/templates/admin/auth/user/change_password.html
  4. 1 1
      django/contrib/admin/templates/admin/base.html
  5. 1 1
      django/contrib/admin/templates/admin/change_form.html
  6. 1 1
      django/contrib/admin/templates/admin/change_list.html
  7. 1 1
      django/contrib/admin/templates/admin/change_list_results.html
  8. 1 1
      django/contrib/admin/templates/admin/delete_confirmation.html
  9. 1 1
      django/contrib/admin/templates/admin/delete_selected_confirmation.html
  10. 1 1
      django/contrib/admin/templates/admin/edit_inline/stacked.html
  11. 1 1
      django/contrib/admin/templates/admin/edit_inline/tabular.html
  12. 1 1
      django/contrib/admin/templates/admin/index.html
  13. 1 1
      django/contrib/admin/templates/admin/login.html
  14. 1 1
      django/contrib/admin/templates/admin/popup_response.html
  15. 1 1
      django/contrib/admin/templates/admin/prepopulated_fields_js.html
  16. 1 1
      django/contrib/admin/templates/admin/related_widget_wrapper.html
  17. 1 1
      django/contrib/admin/templates/admin/search_form.html
  18. 1 1
      django/contrib/admin/templates/registration/password_change_form.html
  19. 1 1
      django/contrib/admin/templatetags/admin_list.py
  20. 3 9
      django/contrib/admin/templatetags/admin_static.py
  21. 3 4
      django/contrib/admin/widgets.py
  22. 9 26
      django/contrib/staticfiles/templatetags/staticfiles.py
  23. 8 5
      django/forms/widgets.py
  24. 10 1
      django/templatetags/static.py
  25. 6 6
      docs/howto/static-files/index.txt
  26. 1 1
      docs/intro/overview.txt
  27. 2 4
      docs/intro/tutorial06.txt
  28. 2 2
      docs/ref/contrib/admin/javascript.txt
  29. 11 41
      docs/ref/contrib/staticfiles.txt
  30. 11 12
      docs/ref/templates/builtins.txt
  31. 7 2
      docs/releases/1.10.txt
  32. 4 4
      docs/releases/1.4.txt
  33. 18 5
      docs/topics/forms/media.txt
  34. 30 0
      tests/staticfiles_tests/test_forms.py

+ 1 - 2
django/contrib/admin/helpers.py

@@ -5,7 +5,6 @@ import warnings
 
 from django import forms
 from django.conf import settings
-from django.contrib.admin.templatetags.admin_static import static
 from django.contrib.admin.utils import (
     display_for_field, flatten_fieldsets, help_text_for_field, label_for_field,
     lookup_field,
@@ -77,7 +76,7 @@ class Fieldset(object):
             js = ['vendor/jquery/jquery%s.js' % extra,
                   'jquery.init.js',
                   'collapse%s.js' % extra]
-            return forms.Media(js=[static('admin/js/%s' % url) for url in js])
+            return forms.Media(js=['admin/js/%s' % url for url in js])
         return forms.Media()
     media = property(_media)
 

+ 2 - 3
django/contrib/admin/options.py

@@ -14,7 +14,6 @@ from django.contrib.admin.checks import (
     BaseModelAdminChecks, InlineModelAdminChecks, ModelAdminChecks,
 )
 from django.contrib.admin.exceptions import DisallowedModelAdminToField
-from django.contrib.admin.templatetags.admin_static import static
 from django.contrib.admin.templatetags.admin_urls import add_preserved_filters
 from django.contrib.admin.utils import (
     NestedObjects, flatten_fieldsets, get_deleted_objects,
@@ -577,7 +576,7 @@ class ModelAdmin(BaseModelAdmin):
             'prepopulate%s.js' % extra,
             'vendor/xregexp/xregexp.min.js',
         ]
-        return forms.Media(js=[static('admin/js/%s' % url) for url in js])
+        return forms.Media(js=['admin/js/%s' % url for url in js])
 
     def get_model_perms(self, request):
         """
@@ -1820,7 +1819,7 @@ class InlineModelAdmin(BaseModelAdmin):
               'inlines%s.js' % extra]
         if self.filter_vertical or self.filter_horizontal:
             js.extend(['SelectBox.js', 'SelectFilter2.js'])
-        return forms.Media(js=[static('admin/js/%s' % url) for url in js])
+        return forms.Media(js=['admin/js/%s' % url for url in js])
 
     def get_extra(self, request, obj=None, **kwargs):
         """Hook for customizing the number of extra inline forms."""

+ 1 - 1
django/contrib/admin/templates/admin/auth/user/change_password.html

@@ -1,5 +1,5 @@
 {% extends "admin/base_site.html" %}
-{% load i18n admin_static %}
+{% load i18n static %}
 {% load admin_urls %}
 
 {% block extrahead %}{{ block.super }}

+ 1 - 1
django/contrib/admin/templates/admin/base.html

@@ -1,4 +1,4 @@
-{% load i18n admin_static %}<!DOCTYPE html>
+{% load i18n static %}<!DOCTYPE html>
 {% get_current_language as LANGUAGE_CODE %}{% get_current_language_bidi as LANGUAGE_BIDI %}
 <html lang="{{ LANGUAGE_CODE|default:"en-us" }}" {% if LANGUAGE_BIDI %}dir="rtl"{% endif %}>
 <head>

+ 1 - 1
django/contrib/admin/templates/admin/change_form.html

@@ -1,5 +1,5 @@
 {% extends "admin/base_site.html" %}
-{% load i18n admin_urls admin_static admin_modify %}
+{% load i18n admin_urls static admin_modify %}
 
 {% block extrahead %}{{ block.super }}
 <script type="text/javascript" src="{% url 'admin:jsi18n' %}"></script>

+ 1 - 1
django/contrib/admin/templates/admin/change_list.html

@@ -1,5 +1,5 @@
 {% extends "admin/base_site.html" %}
-{% load i18n admin_urls admin_static admin_list %}
+{% load i18n admin_urls static admin_list %}
 
 {% block extrastyle %}
   {{ block.super }}

+ 1 - 1
django/contrib/admin/templates/admin/change_list_results.html

@@ -1,4 +1,4 @@
-{% load i18n admin_static %}
+{% load i18n static %}
 {% if result_hidden_fields %}
 <div class="hiddenfields">{# DIV for HTML validation #}
 {% for item in result_hidden_fields %}{{ item }}{% endfor %}

+ 1 - 1
django/contrib/admin/templates/admin/delete_confirmation.html

@@ -1,5 +1,5 @@
 {% extends "admin/base_site.html" %}
-{% load i18n admin_urls admin_static %}
+{% load i18n admin_urls static %}
 
 {% block extrahead %}
     {{ media }}

+ 1 - 1
django/contrib/admin/templates/admin/delete_selected_confirmation.html

@@ -1,5 +1,5 @@
 {% extends "admin/base_site.html" %}
-{% load i18n l10n admin_urls admin_static %}
+{% load i18n l10n admin_urls static %}
 
 {% block extrahead %}
     {{ media }}

+ 1 - 1
django/contrib/admin/templates/admin/edit_inline/stacked.html

@@ -1,4 +1,4 @@
-{% load i18n admin_urls admin_static %}
+{% load i18n admin_urls static %}
 <div class="js-inline-admin-formset inline-group"
      id="{{ inline_admin_formset.formset.prefix }}-group"
      data-inline-type="stacked"

+ 1 - 1
django/contrib/admin/templates/admin/edit_inline/tabular.html

@@ -1,4 +1,4 @@
-{% load i18n admin_urls admin_static admin_modify %}
+{% load i18n admin_urls static admin_modify %}
 <div class="js-inline-admin-formset inline-group" id="{{ inline_admin_formset.formset.prefix }}-group"
      data-inline-type="tabular"
      data-inline-formset="{{ inline_admin_formset.inline_formset_data }}">

+ 1 - 1
django/contrib/admin/templates/admin/index.html

@@ -1,5 +1,5 @@
 {% extends "admin/base_site.html" %}
-{% load i18n admin_static %}
+{% load i18n static %}
 
 {% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% static "admin/css/dashboard.css" %}" />{% endblock %}
 

+ 1 - 1
django/contrib/admin/templates/admin/login.html

@@ -1,5 +1,5 @@
 {% extends "admin/base_site.html" %}
-{% load i18n admin_static %}
+{% load i18n static %}
 
 {% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% static "admin/css/login.css" %}" />
 {{ form.media }}

+ 1 - 1
django/contrib/admin/templates/admin/popup_response.html

@@ -1,4 +1,4 @@
-{% load i18n admin_static %}<!DOCTYPE html>
+{% load i18n static %}<!DOCTYPE html>
 <html>
   <head><title>{% trans 'Popup closing...' %}</title></head>
   <body>

+ 1 - 1
django/contrib/admin/templates/admin/prepopulated_fields_js.html

@@ -1,4 +1,4 @@
-{% load l10n admin_static %}
+{% load l10n static %}
 <script type="text/javascript"
         id="django-admin-prepopulated-fields-constants"
         src="{% static "admin/js/prepopulate_init.js" %}"

+ 1 - 1
django/contrib/admin/templates/admin/related_widget_wrapper.html

@@ -1,4 +1,4 @@
-{% load i18n admin_static %}
+{% load i18n static %}
 <div class="related-widget-wrapper">
     {{ widget }}
     {% block links %}

+ 1 - 1
django/contrib/admin/templates/admin/search_form.html

@@ -1,4 +1,4 @@
-{% load i18n admin_static %}
+{% load i18n static %}
 {% if cl.search_fields %}
 <div id="toolbar"><form id="changelist-search" method="get">
 <div><!-- DIV needed for valid HTML -->

+ 1 - 1
django/contrib/admin/templates/registration/password_change_form.html

@@ -1,5 +1,5 @@
 {% extends "admin/base_site.html" %}
-{% load i18n admin_static %}
+{% load i18n static %}
 {% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% static "admin/css/forms.css" %}" />{% endblock %}
 {% block userlinks %}{% url 'django-admindocs-docroot' as docsroot %}{% if docsroot %}<a href="{{ docsroot }}">{% trans 'Documentation' %}</a> / {% endif %} {% trans 'Change password' %} / <a href="{% url 'admin:logout' %}">{% trans 'Log out' %}</a>{% endblock %}
 {% block breadcrumbs %}

+ 1 - 1
django/contrib/admin/templatetags/admin_list.py

@@ -3,7 +3,6 @@ from __future__ import unicode_literals
 import datetime
 import warnings
 
-from django.contrib.admin.templatetags.admin_static import static
 from django.contrib.admin.templatetags.admin_urls import add_preserved_filters
 from django.contrib.admin.utils import (
     display_for_field, display_for_value, label_for_field, lookup_field,
@@ -16,6 +15,7 @@ from django.core.urlresolvers import NoReverseMatch
 from django.db import models
 from django.template import Library
 from django.template.loader import get_template
+from django.templatetags.static import static
 from django.utils import formats
 from django.utils.deprecation import RemovedInDjango20Warning
 from django.utils.encoding import force_text

+ 3 - 9
django/contrib/admin/templatetags/admin_static.py

@@ -1,17 +1,11 @@
-from django.apps import apps
 from django.template import Library
+from django.templatetags.static import static as _static
 
 register = Library()
 
-_static = None
-
 
 @register.simple_tag
 def static(path):
-    global _static
-    if _static is None:
-        if apps.is_installed('django.contrib.staticfiles'):
-            from django.contrib.staticfiles.templatetags.staticfiles import static as _static
-        else:
-            from django.templatetags.static import static as _static
+    # Backwards compatibility alias for django.templatetags.static.static().
+    # Deprecation should start in Django 2.0.
     return _static(path)

+ 3 - 4
django/contrib/admin/widgets.py

@@ -6,7 +6,6 @@ from __future__ import unicode_literals
 import copy
 
 from django import forms
-from django.contrib.admin.templatetags.admin_static import static
 from django.core.urlresolvers import reverse
 from django.db.models.deletion import CASCADE
 from django.forms.utils import flatatt
@@ -32,7 +31,7 @@ class FilteredSelectMultiple(forms.SelectMultiple):
     @property
     def media(self):
         js = ["core.js", "SelectBox.js", "SelectFilter2.js"]
-        return forms.Media(js=[static("admin/js/%s" % path) for path in js])
+        return forms.Media(js=["admin/js/%s" % path for path in js])
 
     def __init__(self, verbose_name, is_stacked, attrs=None, choices=()):
         self.verbose_name = verbose_name
@@ -56,7 +55,7 @@ class AdminDateWidget(forms.DateInput):
     @property
     def media(self):
         js = ["calendar.js", "admin/DateTimeShortcuts.js"]
-        return forms.Media(js=[static("admin/js/%s" % path) for path in js])
+        return forms.Media(js=["admin/js/%s" % path for path in js])
 
     def __init__(self, attrs=None, format=None):
         final_attrs = {'class': 'vDateField', 'size': '10'}
@@ -69,7 +68,7 @@ class AdminTimeWidget(forms.TimeInput):
     @property
     def media(self):
         js = ["calendar.js", "admin/DateTimeShortcuts.js"]
-        return forms.Media(js=[static("admin/js/%s" % path) for path in js])
+        return forms.Media(js=["admin/js/%s" % path for path in js])
 
     def __init__(self, attrs=None, format=None):
         final_attrs = {'class': 'vTimeField', 'size': '8'}

+ 9 - 26
django/contrib/staticfiles/templatetags/staticfiles.py

@@ -1,36 +1,19 @@
 from django import template
-from django.contrib.staticfiles.storage import staticfiles_storage
-from django.templatetags.static import StaticNode
+from django.templatetags.static import (
+    do_static as _do_static, static as _static,
+)
 
 register = template.Library()
 
 
 def static(path):
-    return staticfiles_storage.url(path)
-
-
-class StaticFilesNode(StaticNode):
-
-    def url(self, context):
-        path = self.path.resolve(context)
-        return static(path)
+    # Backwards compatibility alias for django.templatetags.static.static().
+    # Deprecation should start in Django 2.0.
+    return _static(path)
 
 
 @register.tag('static')
 def do_static(parser, token):
-    """
-    A template tag that returns the URL to a file
-    using staticfiles' storage backend
-
-    Usage::
-
-        {% static path [as varname] %}
-
-    Examples::
-
-        {% static "myapp/css/base.css" %}
-        {% static variable_with_path %}
-        {% static "myapp/css/base.css" as admin_base_css %}
-        {% static variable_with_path as varname %}
-    """
-    return StaticFilesNode.handle_token(parser, token)
+    # Backwards compatibility alias for django.templatetags.static.do_static().
+    # Deprecation should start in Django 2.0.
+    return _do_static(parser, token)

+ 8 - 5
django/forms/widgets.py

@@ -11,6 +11,7 @@ from itertools import chain
 
 from django.conf import settings
 from django.forms.utils import flatatt, to_current_timezone
+from django.templatetags.static import static
 from django.utils import datetime_safe, formats, six
 from django.utils.datastructures import MultiValueDict
 from django.utils.dates import MONTHS
@@ -21,7 +22,6 @@ from django.utils.formats import get_format
 from django.utils.html import conditional_escape, format_html, html_safe
 from django.utils.safestring import mark_safe
 from django.utils.six.moves import range
-from django.utils.six.moves.urllib.parse import urljoin
 from django.utils.translation import ugettext_lazy
 
 __all__ = (
@@ -77,12 +77,15 @@ class Media(object):
             ) for path in self._css[medium]
         ] for medium in media])
 
-    def absolute_path(self, path, prefix=None):
+    def absolute_path(self, path):
+        """
+        Given a relative or absolute path to a static asset, return an absolute
+        path. An absolute path will be returned unchanged while a relative path
+        will be passed to django.templatetags.static.static().
+        """
         if path.startswith(('http://', 'https://', '/')):
             return path
-        if prefix is None:
-            prefix = settings.STATIC_URL
-        return urljoin(prefix, path)
+        return static(path)
 
     def __getitem__(self, name):
         "Returns a Media object that only contains media of the given type"

+ 10 - 1
django/templatetags/static.py

@@ -1,4 +1,5 @@
 from django import template
+from django.apps import apps
 from django.utils.encoding import iri_to_uri
 from django.utils.six.moves.urllib.parse import urljoin
 
@@ -108,7 +109,11 @@ class StaticNode(template.Node):
 
     @classmethod
     def handle_simple(cls, path):
-        return urljoin(PrefixNode.handle_simple("STATIC_URL"), path)
+        if apps.is_installed('django.contrib.staticfiles'):
+            from django.contrib.staticfiles.storage import staticfiles_storage
+            return staticfiles_storage.url(path)
+        else:
+            return urljoin(PrefixNode.handle_simple("STATIC_URL"), path)
 
     @classmethod
     def handle_token(cls, parser, token):
@@ -151,4 +156,8 @@ def do_static(parser, token):
 
 
 def static(path):
+    """
+    Given a relative path to a static asset, return the absolute path to the
+    asset.
+    """
     return StaticNode.handle_simple(path)

+ 6 - 6
docs/howto/static-files/index.txt

@@ -19,17 +19,17 @@ Configuring static files
       STATIC_URL = '/static/'
 
 3. In your templates, either hardcode the url like
-   ``/static/my_app/myexample.jpg`` or, preferably, use the
-   :ttag:`static<staticfiles-static>` template tag to build the URL for the given
-   relative path by using the configured :setting:`STATICFILES_STORAGE` storage
-   (this makes it much easier when you want to switch to a content delivery
-   network (CDN) for serving static files).
+   ``/static/my_app/myexample.jpg`` or, preferably, use the :ttag:`static`
+   template tag to build the URL for the given relative path by using the
+   configured :setting:`STATICFILES_STORAGE` storage (this makes it much easier
+   when you want to switch to a content delivery network (CDN) for serving
+   static files).
 
    .. _staticfiles-in-templates:
 
    .. code-block:: html+django
 
-        {% load staticfiles %}
+        {% load static %}
         <img src="{% static "my_app/myexample.jpg" %}" alt="My image"/>
 
 4. Store your static files in a folder called ``static`` in your app. For

+ 1 - 1
docs/intro/overview.txt

@@ -304,7 +304,7 @@ Here's what the "base.html" template, including the use of :doc:`static files
 .. snippet:: html+django
     :filename: mysite/templates/base.html
 
-    {% load staticfiles %}
+    {% load static %}
     <html>
     <head>
         <title>{% block title %}{% endblock %}</title>

+ 2 - 4
docs/intro/tutorial06.txt

@@ -68,13 +68,11 @@ Next, add the following at the top of ``polls/templates/polls/index.html``:
 .. snippet:: html+django
     :filename: polls/templates/polls/index.html
 
-    {% load staticfiles %}
+    {% load static %}
 
     <link rel="stylesheet" type="text/css" href="{% static 'polls/style.css' %}" />
 
-``{% load staticfiles %}`` loads the :ttag:`{% static %} <staticfiles-static>`
-template tag from the ``staticfiles`` template library. The ``{% static %}``
-template tag generates the absolute URL of the static file.
+The ``{% static %}`` template tag generates the absolute URL of static files.
 
 That's all you need to do for development. Reload
 ``http://localhost:8000/polls/`` and you should see that the question links are

+ 2 - 2
docs/ref/contrib/admin/javascript.txt

@@ -26,7 +26,7 @@ In your custom ``change_form.html`` template, extend the
 .. code-block:: html+django
 
     {% extends 'admin/change_form.html' %}
-    {% load admin_static %}
+    {% load static %}
 
     {% block admin_change_form_document_ready %}
     {{ block.super }}
@@ -65,7 +65,7 @@ namespace, just listen to the event triggered from there. For example:
 .. code-block:: html+django
 
     {% extends 'admin/change_form.html' %}
-    {% load admin_static %}
+    {% load static %}
 
     {% block admin_change_form_document_ready %}
     {{ block.super }}

+ 11 - 41
docs/ref/contrib/staticfiles.txt

@@ -285,11 +285,16 @@ following requirements are met:
 * the :setting:`STATICFILES_STORAGE` setting is set to
   ``'django.contrib.staticfiles.storage.ManifestStaticFilesStorage'``
 * the :setting:`DEBUG` setting is set to ``False``
-* you use the ``staticfiles`` :ttag:`static<staticfiles-static>` template
-  tag to refer to your static files in your templates
 * you've collected all your static files by using the
   :djadmin:`collectstatic` management command
 
+.. versionchanged:: 1.10
+
+    In older versions, you also had to use
+    ``{% load static from staticfiles %}`` in your template. The
+    :ttag:`static` template tag (``{% load static %}``) now uses
+    :mod:`django.contrib.staticfiles` if it's installed.
+
 Since creating the MD5 hash can be a performance burden to your website
 during runtime, ``staticfiles`` will automatically store the mapping with
 hashed names for all processed files in a file called ``staticfiles.json``.
@@ -331,43 +336,6 @@ If you want to override certain options of the cache backend the storage uses,
 simply specify a custom entry in the :setting:`CACHES` setting named
 ``'staticfiles'``. It falls back to using the ``'default'`` cache backend.
 
-.. currentmodule:: django.contrib.staticfiles.templatetags.staticfiles
-
-Template tags
-=============
-
-static
-------
-
-.. templatetag:: staticfiles-static
-
-Uses the configured :setting:`STATICFILES_STORAGE` storage to create the
-full URL for the given relative path, e.g.:
-
-.. code-block:: html+django
-
-    {% load static from staticfiles %}
-    <img src="{% static "images/hi.jpg" %}" alt="Hi!" />
-
-The previous example is equal to calling the ``url`` method of an instance of
-:setting:`STATICFILES_STORAGE` with ``"images/hi.jpg"``. This is especially
-useful when using a non-local storage backend to deploy files as documented
-in :ref:`staticfiles-from-cdn`.
-
-If you'd like to retrieve a static URL without displaying it, you can use a
-slightly different call:
-
-.. code-block:: html+django
-
-    {% load static from staticfiles %}
-    {% static "images/hi.jpg" as myphoto %}
-    <img src="{{ myphoto }}" alt="Hi!" />
-
-.. admonition:: Using Jinja2 templates?
-
-    See :class:`django.template.backends.jinja2.Jinja2` for information on
-    using the ``static`` tag with Jinja2.
-
 Finders Module
 ==============
 
@@ -390,8 +358,10 @@ files:
   which adds :setting:`STATIC_URL` to every template context rendered
   with :class:`~django.template.RequestContext` contexts.
 
-- The builtin template tag :ttag:`static` which takes a path and
-  urljoins it with the static prefix :setting:`STATIC_URL`.
+- The builtin template tag :ttag:`static` which takes a path and urljoins it
+  with the static prefix :setting:`STATIC_URL`. If
+  ``django.contrib.staticfiles`` is installed, the tag uses the ``url()``
+  method of the :setting:`STATICFILES_STORAGE` instead.
 
 - The builtin template tag :ttag:`get_static_prefix` which populates a
   template variable with the static prefix :setting:`STATIC_URL` to be

+ 11 - 12
docs/ref/templates/builtins.txt

@@ -2399,8 +2399,9 @@ static
 """"""
 
 To link to static files that are saved in :setting:`STATIC_ROOT` Django ships
-with a :ttag:`static` template tag. You can use this regardless if you're
-using :class:`~django.template.RequestContext` or not. For example::
+with a :ttag:`static` template tag. If the :mod:`django.contrib.staticfiles`
+app is installed, the tag will serve files using ``url()`` method of the
+storage specified by :setting:`STATICFILES_STORAGE`. For example::
 
     {% load static %}
     <img src="{% static "images/hi.jpg" %}" alt="Hi!" />
@@ -2418,18 +2419,16 @@ slightly different call::
     {% static "images/hi.jpg" as myphoto %}
     <img src="{{ myphoto }}"></img>
 
-.. note::
+.. admonition:: Using Jinja2 templates?
+
+    See :class:`~django.template.backends.jinja2.Jinja2` for information on
+    using the ``static`` tag with Jinja2.
 
-    The :mod:`staticfiles<django.contrib.staticfiles>` contrib app also ships
-    with a :ttag:`static template tag<staticfiles-static>` which uses
-    ``staticfiles'`` :setting:`STATICFILES_STORAGE` to build the URL of the
-    given path (rather than simply using :func:`urllib.parse.urljoin` with the
-    :setting:`STATIC_URL` setting and the given path). Use that instead if you
-    have an advanced use case such as :ref:`using a cloud service to serve
-    static files<staticfiles-from-cdn>`::
+.. versionchanged:: 1.10
 
-        {% load static from staticfiles %}
-        <img src="{% static "images/hi.jpg" %}" alt="Hi!" />
+    In older versions, you had to use ``{% load static from staticfiles %}`` in
+    your template to serve files from the storage defined in
+    :setting:`STATICFILES_STORAGE`. This is no longer required.
 
 .. templatetag:: get_static_prefix
 

+ 7 - 2
docs/releases/1.10.txt

@@ -127,7 +127,11 @@ Minor features
 :mod:`django.contrib.staticfiles`
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-* ...
+* The :ttag:`static` template tag now uses ``django.contrib.staticfiles``
+  if it's in ``INSTALLED_APPS``. This is especially useful for third-party apps
+  which can now always use ``{% load static %}`` (instead of
+  ``{% load staticfiles %}`` or ``{% load static from staticfiles %}``) and
+  not worry about whether or not the ``staticfiles`` app is installed.
 
 :mod:`django.contrib.syndication`
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -169,7 +173,8 @@ File Uploads
 Forms
 ^^^^^
 
-* ...
+* Form and widget ``Media`` is now served using
+  :mod:`django.contrib.staticfiles` if installed.
 
 Generic Views
 ^^^^^^^^^^^^^

+ 4 - 4
docs/releases/1.4.txt

@@ -459,10 +459,10 @@ more details.
 ~~~~~~~~~~~~~~~~~~~~~~~
 
 The :mod:`staticfiles<django.contrib.staticfiles>` contrib app has a new
-:ttag:`static<staticfiles-static>` template tag to refer to files saved with
-the :setting:`STATICFILES_STORAGE` storage backend. It uses the storage
-backend's ``url`` method and therefore supports advanced features such as
-:ref:`serving files from a cloud service<staticfiles-from-cdn>`.
+``static`` template tag to refer to files saved with the
+:setting:`STATICFILES_STORAGE` storage backend. It uses the storage backend's
+``url`` method and therefore supports advanced features such as :ref:`serving
+files from a cloud service<staticfiles-from-cdn>`.
 
 ``CachedStaticFilesStorage`` storage backend
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

+ 18 - 5
docs/topics/forms/media.txt

@@ -203,12 +203,13 @@ Paths in asset definitions
 Paths used to specify assets 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 the appropriate prefix.
+be prepended with the value of the appropriate prefix. If the
+:mod:`django.contrib.staticfiles` app is installed, it will be used to serve
+assets.
 
-As part of the introduction of the
-:doc:`staticfiles app </ref/contrib/staticfiles>` two new settings were added
-to refer to "static files" (images, CSS, JavaScript, etc.) that are needed
-to render a complete web page: :setting:`STATIC_URL` and :setting:`STATIC_ROOT`.
+Whether or not you use :mod:`django.contrib.staticfiles`,  the
+:setting:`STATIC_URL` and :setting:`STATIC_ROOT` settings are required to
+render a complete web page.
 
 To find the appropriate prefix to use, Django will check if the
 :setting:`STATIC_URL` setting is not ``None`` and automatically fall back
@@ -238,6 +239,18 @@ But if :setting:`STATIC_URL` is ``'http://static.example.com/'``::
     <script type="text/javascript" src="http://static.example.com/animations.js"></script>
     <script type="text/javascript" src="http://othersite.com/actions.js"></script>
 
+Or if :mod:`~django.contrib.staticfiles` is configured using the
+`~django.contib.staticfiles.ManifestStaticFilesStorage`::
+
+    >>> w = CalendarWidget()
+    >>> print(w.media)
+    <link href="/css/pretty.css" type="text/css" media="all" rel="stylesheet" />
+    <script type="text/javascript" src="https://static.example.com/animations.27e20196a850.js"></script>
+    <script type="text/javascript" src="http://othersite.com/actions.js"></script>
+
+.. versionchanged:: 1.10
+
+    Older versions didn't serve assets using :mod:`django.contrib.staticfiles`.
 
 ``Media`` objects
 -----------------

+ 30 - 0
tests/staticfiles_tests/test_forms.py

@@ -0,0 +1,30 @@
+from django.contrib.staticfiles import storage
+from django.forms import Media
+from django.test import SimpleTestCase, override_settings
+from django.utils.six.moves.urllib.parse import urljoin
+
+
+class StaticTestStorage(storage.StaticFilesStorage):
+    def url(self, name):
+        return urljoin('https://example.com/assets/', name)
+
+
+@override_settings(
+    STATIC_URL='http://media.example.com/static/',
+    INSTALLED_APPS=('django.contrib.staticfiles', ),
+    STATICFILES_STORAGE='staticfiles_tests.test_forms.StaticTestStorage',
+)
+class StaticFilesFormsMediaTestCase(SimpleTestCase):
+    def test_absolute_url(self):
+        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="https://example.com/assets/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>"""
+        )