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
This commit is contained in:
Jannis Leidel 2010-11-17 15:36:26 +00:00
parent 9b45f6cd54
commit 33d8fcde8a
31 changed files with 908 additions and 329 deletions

View File

@ -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 = ()

View File

@ -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.

View File

@ -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")

View File

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

View File

@ -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):

View File

@ -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 ''))

View File

@ -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.'),
)

View File

@ -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)

View File

@ -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))

View File

@ -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('',

View File

@ -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")

View File

@ -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.

View File

@ -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):

View File

@ -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"

View File

@ -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")

View File

@ -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):

View File

@ -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.

View File

@ -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.

View File

@ -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.
.. note::
.. 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/'
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/``.

View File

@ -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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -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

View File

@ -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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -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
==========================

View File

@ -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
======================

View File

@ -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.

View File

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

View File

@ -190,16 +190,30 @@ 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.
class CalendarWidget(forms.TextInput):
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',),
@ -209,9 +223,18 @@ path, and left as-is. All other paths will be prepended with the value of
>>> 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://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://static.example.com/animations.js"></script>
<script type="text/javascript" src="http://othersite.com/actions.js"></script>
Media objects
-------------

View File

@ -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" />""")

View File

@ -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)

View File

@ -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):