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:
parent
9b45f6cd54
commit
33d8fcde8a
|
@ -195,9 +195,9 @@ TEMPLATE_CONTEXT_PROCESSORS = (
|
||||||
'django.core.context_processors.debug',
|
'django.core.context_processors.debug',
|
||||||
'django.core.context_processors.i18n',
|
'django.core.context_processors.i18n',
|
||||||
'django.core.context_processors.media',
|
'django.core.context_processors.media',
|
||||||
|
'django.core.context_processors.static',
|
||||||
# 'django.core.context_processors.request',
|
# 'django.core.context_processors.request',
|
||||||
'django.contrib.messages.context_processors.messages',
|
'django.contrib.messages.context_processors.messages',
|
||||||
'django.contrib.staticfiles.context_processors.staticfiles',
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Output to use in template system for invalid (e.g. misspelled) variables.
|
# 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'
|
DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage'
|
||||||
|
|
||||||
# Absolute filesystem path to the directory that will hold user-uploaded files.
|
# 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 = ''
|
MEDIA_ROOT = ''
|
||||||
|
|
||||||
# URL that handles the media served from MEDIA_ROOT.
|
# URL that handles the media served from MEDIA_ROOT.
|
||||||
# Example: "http://media.lawrence.com"
|
# Example: "http://media.lawrence.com/media/"
|
||||||
MEDIA_URL = ''
|
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.
|
# List of upload handler classes to be applied in order.
|
||||||
FILE_UPLOAD_HANDLERS = (
|
FILE_UPLOAD_HANDLERS = (
|
||||||
'django.core.files.uploadhandler.MemoryFileUploadHandler',
|
'django.core.files.uploadhandler.MemoryFileUploadHandler',
|
||||||
|
@ -552,14 +560,6 @@ FIXTURE_DIRS = ()
|
||||||
# STATICFILES #
|
# 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
|
# A list of locations of additional static files
|
||||||
STATICFILES_DIRS = ()
|
STATICFILES_DIRS = ()
|
||||||
|
|
||||||
|
|
|
@ -49,16 +49,16 @@ MEDIA_ROOT = ''
|
||||||
|
|
||||||
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
|
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
|
||||||
# trailing slash if there is a path component (optional in other cases).
|
# trailing slash 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 = ''
|
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/"
|
# Example: "/home/media/media.lawrence.com/static/"
|
||||||
STATICFILES_ROOT = ''
|
STATIC_ROOT = ''
|
||||||
|
|
||||||
# URL that handles the static files served from STATICFILES_ROOT.
|
# URL that handles the static files served from STATIC_ROOT.
|
||||||
# Example: "http://static.lawrence.com/", "http://example.com/static/"
|
# Example: "http://media.lawrence.com/static/"
|
||||||
STATICFILES_URL = '/static/'
|
STATIC_URL = '/static/'
|
||||||
|
|
||||||
# URL prefix for admin media -- CSS, JavaScript and images.
|
# URL prefix for admin media -- CSS, JavaScript and images.
|
||||||
# Make sure to use a trailing slash.
|
# Make sure to use a trailing slash.
|
||||||
|
|
|
@ -1,15 +1,11 @@
|
||||||
from django.template import Library
|
from django.template import Library
|
||||||
from django.utils.encoding import iri_to_uri
|
from django.templatetags.static import PrefixNode
|
||||||
|
|
||||||
register = Library()
|
register = Library()
|
||||||
|
|
||||||
|
@register.simple_tag
|
||||||
def admin_media_prefix():
|
def admin_media_prefix():
|
||||||
"""
|
"""
|
||||||
Returns the string contained in the setting ADMIN_MEDIA_PREFIX.
|
Returns the string contained in the setting ADMIN_MEDIA_PREFIX.
|
||||||
"""
|
"""
|
||||||
try:
|
return PrefixNode.handle_simple("ADMIN_MEDIA_PREFIX")
|
||||||
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)
|
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
def staticfiles(request):
|
|
||||||
return {
|
|
||||||
'STATICFILES_URL': settings.STATICFILES_URL,
|
|
||||||
}
|
|
|
@ -10,46 +10,44 @@ from django.contrib.staticfiles.views import serve
|
||||||
class StaticFilesHandler(WSGIHandler):
|
class StaticFilesHandler(WSGIHandler):
|
||||||
"""
|
"""
|
||||||
WSGI middleware that intercepts calls to the static files directory, as
|
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
|
self.application = application
|
||||||
if media_dir:
|
if base_dir:
|
||||||
self.media_dir = media_dir
|
self.base_dir = base_dir
|
||||||
else:
|
else:
|
||||||
self.media_dir = self.get_media_dir()
|
self.base_dir = self.get_base_dir()
|
||||||
self.media_url = urlparse(self.get_media_url())
|
self.base_url = urlparse(self.get_base_url())
|
||||||
if settings.DEBUG:
|
|
||||||
utils.check_settings()
|
|
||||||
super(StaticFilesHandler, self).__init__()
|
super(StaticFilesHandler, self).__init__()
|
||||||
|
|
||||||
def get_media_dir(self):
|
def get_base_dir(self):
|
||||||
return settings.STATICFILES_ROOT
|
return settings.STATIC_ROOT
|
||||||
|
|
||||||
def get_media_url(self):
|
def get_base_url(self):
|
||||||
return settings.STATICFILES_URL
|
if settings.DEBUG:
|
||||||
|
utils.check_settings()
|
||||||
|
return settings.STATIC_URL
|
||||||
|
|
||||||
def _should_handle(self, path):
|
def _should_handle(self, path):
|
||||||
"""
|
"""
|
||||||
Checks if the path should be handled. Ignores the path if:
|
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)
|
* the request's path isn't under the media path (or equal)
|
||||||
* settings.DEBUG isn't True
|
|
||||||
"""
|
"""
|
||||||
return (self.media_url[2] != path and
|
return (self.base_url[2] != path and
|
||||||
path.startswith(self.media_url[2]) and not self.media_url[1])
|
path.startswith(self.base_url[2]) and not self.base_url[1])
|
||||||
|
|
||||||
def file_path(self, url):
|
def file_path(self, url):
|
||||||
"""
|
"""
|
||||||
Returns the relative path to the media file on disk for the given 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
|
resultant file path is outside the media directory, then a ValueError
|
||||||
is raised.
|
is raised.
|
||||||
"""
|
"""
|
||||||
# Remove ``media_url``.
|
relative_url = url[len(self.base_url[2]):]
|
||||||
relative_url = url[len(self.media_url[2]):]
|
|
||||||
return urllib.url2pathname(relative_url)
|
return urllib.url2pathname(relative_url)
|
||||||
|
|
||||||
def serve(self, request):
|
def serve(self, request):
|
||||||
|
|
|
@ -12,7 +12,7 @@ from django.contrib.staticfiles import finders
|
||||||
class Command(NoArgsCommand):
|
class Command(NoArgsCommand):
|
||||||
"""
|
"""
|
||||||
Command that allows to copy or symlink media files from different
|
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 + (
|
option_list = NoArgsCommand.option_list + (
|
||||||
make_option('--noinput', action='store_false', dest='interactive',
|
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"
|
self.stdout.write("\n%s static file%s %s to '%s'%s.\n"
|
||||||
% (actual_count, actual_count != 1 and 's' or '',
|
% (actual_count, actual_count != 1 and 's' or '',
|
||||||
symlink and 'symlinked' or 'copied',
|
symlink and 'symlinked' or 'copied',
|
||||||
settings.STATICFILES_ROOT,
|
settings.STATIC_ROOT,
|
||||||
unmodified_count and ' (%s unmodified)'
|
unmodified_count and ' (%s unmodified)'
|
||||||
% unmodified_count or ''))
|
% unmodified_count or ''))
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ from django.contrib.staticfiles.handlers import StaticFilesHandler
|
||||||
class Command(BaseRunserverCommand):
|
class Command(BaseRunserverCommand):
|
||||||
option_list = BaseRunserverCommand.option_list + (
|
option_list = BaseRunserverCommand.option_list + (
|
||||||
make_option('--nostatic', action="store_false", dest='use_static_handler', default=True,
|
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,
|
make_option('--insecure', action="store_true", dest='insecure_serving', default=False,
|
||||||
help='Allows serving static files even if DEBUG is False.'),
|
help='Allows serving static files even if DEBUG is False.'),
|
||||||
)
|
)
|
||||||
|
|
|
@ -12,21 +12,22 @@ class StaticFilesStorage(FileSystemStorage):
|
||||||
Standard file system storage for site media files.
|
Standard file system storage for site media files.
|
||||||
|
|
||||||
The defaults for ``location`` and ``base_url`` are
|
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):
|
def __init__(self, location=None, base_url=None, *args, **kwargs):
|
||||||
if location is None:
|
if location is None:
|
||||||
location = settings.STATICFILES_ROOT
|
location = settings.STATIC_ROOT
|
||||||
if base_url is None:
|
if base_url is None:
|
||||||
base_url = settings.STATICFILES_URL
|
base_url = settings.STATIC_URL
|
||||||
if not location:
|
if not location:
|
||||||
raise ImproperlyConfigured("You're using the staticfiles app "
|
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.")
|
"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 "
|
raise ImproperlyConfigured("You're using the staticfiles app "
|
||||||
"without having set the STATICFILES_URL setting. Set it to "
|
"without having set the STATIC_URL setting. Set it to "
|
||||||
"URL that handles the files served from STATICFILES_ROOT.")
|
"URL that handles the files served from STATIC_ROOT.")
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
utils.check_settings()
|
utils.check_settings()
|
||||||
super(StaticFilesStorage, self).__init__(location, base_url, *args, **kwargs)
|
super(StaticFilesStorage, self).__init__(location, base_url, *args, **kwargs)
|
||||||
|
|
|
@ -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))
|
|
||||||
|
|
|
@ -18,15 +18,10 @@ def staticfiles_urlpatterns(prefix=None):
|
||||||
if not settings.DEBUG:
|
if not settings.DEBUG:
|
||||||
return []
|
return []
|
||||||
if prefix is None:
|
if prefix is None:
|
||||||
prefix = settings.STATICFILES_URL
|
prefix = settings.STATIC_URL
|
||||||
if not prefix:
|
if not prefix or '://' in prefix:
|
||||||
raise ImproperlyConfigured(
|
raise ImproperlyConfigured(
|
||||||
"The prefix for the 'staticfiles_urlpatterns' helper is empty. "
|
"The prefix for the 'staticfiles_urlpatterns' helper is invalid.")
|
||||||
"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.")
|
|
||||||
if prefix.startswith("/"):
|
if prefix.startswith("/"):
|
||||||
prefix = prefix[1:]
|
prefix = prefix[1:]
|
||||||
return patterns('',
|
return patterns('',
|
||||||
|
|
|
@ -33,13 +33,13 @@ def get_files(storage, ignore_patterns=[], location=''):
|
||||||
|
|
||||||
def check_settings():
|
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.
|
settings have the same value.
|
||||||
"""
|
"""
|
||||||
if settings.MEDIA_URL == settings.STATICFILES_URL:
|
if settings.MEDIA_URL == settings.STATIC_URL:
|
||||||
raise ImproperlyConfigured("The MEDIA_URL and STATICFILES_URL "
|
raise ImproperlyConfigured("The MEDIA_URL and STATIC_URL "
|
||||||
"settings must have individual values")
|
"settings must have different values")
|
||||||
if ((settings.MEDIA_ROOT and settings.STATICFILES_ROOT) and
|
if ((settings.MEDIA_ROOT and settings.STATIC_ROOT) and
|
||||||
(settings.MEDIA_ROOT == settings.STATICFILES_ROOT)):
|
(settings.MEDIA_ROOT == settings.STATIC_ROOT)):
|
||||||
raise ImproperlyConfigured("The MEDIA_ROOT and STATICFILES_ROOT "
|
raise ImproperlyConfigured("The MEDIA_ROOT and STATIC_ROOT "
|
||||||
"settings must have individual values")
|
"settings must have different values")
|
||||||
|
|
|
@ -66,6 +66,13 @@ def i18n(request):
|
||||||
|
|
||||||
return context_extras
|
return context_extras
|
||||||
|
|
||||||
|
def static(request):
|
||||||
|
"""
|
||||||
|
Adds static-related context variables to the context.
|
||||||
|
|
||||||
|
"""
|
||||||
|
return {'STATIC_URL': settings.STATIC_URL}
|
||||||
|
|
||||||
def media(request):
|
def media(request):
|
||||||
"""
|
"""
|
||||||
Adds media-related context variables to the context.
|
Adds media-related context variables to the context.
|
||||||
|
|
|
@ -17,8 +17,8 @@ import warnings
|
||||||
from django.core.management.color import color_style
|
from django.core.management.color import color_style
|
||||||
from django.utils.http import http_date
|
from django.utils.http import http_date
|
||||||
from django.utils._os import safe_join
|
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"
|
__version__ = "0.1"
|
||||||
__all__ = ['WSGIServer','WSGIRequestHandler']
|
__all__ = ['WSGIServer','WSGIRequestHandler']
|
||||||
|
@ -635,19 +635,20 @@ class WSGIRequestHandler(BaseHTTPRequestHandler):
|
||||||
sys.stderr.write(msg)
|
sys.stderr.write(msg)
|
||||||
|
|
||||||
|
|
||||||
class AdminMediaHandler(StaticFilesHandler):
|
class AdminMediaHandler(handlers.StaticFilesHandler):
|
||||||
"""
|
"""
|
||||||
WSGI middleware that intercepts calls to the admin media directory, as
|
WSGI middleware that intercepts calls to the admin media directory, as
|
||||||
defined by the ADMIN_MEDIA_PREFIX setting, and serves those images.
|
defined by the ADMIN_MEDIA_PREFIX setting, and serves those images.
|
||||||
Use this ONLY LOCALLY, for development! This hasn't been tested for
|
Use this ONLY LOCALLY, for development! This hasn't been tested for
|
||||||
security and is not super efficient.
|
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
|
import django
|
||||||
return os.path.join(django.__path__[0], 'contrib', 'admin', 'media')
|
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
|
from django.conf import settings
|
||||||
return settings.ADMIN_MEDIA_PREFIX
|
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.
|
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
|
The passed URL is assumed to begin with ``self.base_url``. If the
|
||||||
resultant file path is outside the media directory, then a ValueError
|
resulting file path is outside the media directory, then a ValueError
|
||||||
is raised.
|
is raised.
|
||||||
"""
|
"""
|
||||||
# Remove ``media_url``.
|
relative_url = url[len(self.base_url[2]):]
|
||||||
relative_url = url[len(self.media_url[2]):]
|
|
||||||
relative_path = urllib.url2pathname(relative_url)
|
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):
|
def serve(self, request):
|
||||||
document_root, path = os.path.split(self.file_path(request.path))
|
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:
|
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
|
* 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):
|
def run(addr, port, wsgi_handler):
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
"""
|
"""
|
||||||
HTML Widget classes
|
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
|
import django.utils.copycompat as copy
|
||||||
from itertools import chain
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils.datastructures import MultiValueDict, MergeDict
|
from django.utils.datastructures import MultiValueDict, MergeDict
|
||||||
from django.utils.html import escape, conditional_escape
|
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.encoding import StrAndUnicode, force_unicode
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils import datetime_safe, formats
|
from django.utils import datetime_safe, formats
|
||||||
import time
|
|
||||||
import datetime
|
|
||||||
from util import flatatt
|
|
||||||
from urlparse import urljoin
|
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'Media', 'MediaDefiningClass', 'Widget', 'TextInput', 'PasswordInput',
|
'Media', 'MediaDefiningClass', 'Widget', 'TextInput', 'PasswordInput',
|
||||||
|
@ -63,10 +63,16 @@ class Media(StrAndUnicode):
|
||||||
for path in self._css[medium]]
|
for path in self._css[medium]]
|
||||||
for medium in media])
|
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'/'):
|
if path.startswith(u'http://') or path.startswith(u'https://') or path.startswith(u'/'):
|
||||||
return path
|
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):
|
def __getitem__(self, name):
|
||||||
"Returns a Media object that only contains media of the given type"
|
"Returns a Media object that only contains media of the given type"
|
||||||
|
|
|
@ -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")
|
|
@ -17,8 +17,8 @@ from django.http import Http404, HttpResponse, HttpResponseRedirect, HttpRespons
|
||||||
from django.template import Template, Context, TemplateDoesNotExist
|
from django.template import Template, Context, TemplateDoesNotExist
|
||||||
from django.utils.http import http_date
|
from django.utils.http import http_date
|
||||||
|
|
||||||
from django.contrib.staticfiles.views import \
|
from django.contrib.staticfiles.views import (directory_index,
|
||||||
directory_index, was_modified_since, serve as staticfiles_serve
|
was_modified_since, serve as staticfiles_serve)
|
||||||
|
|
||||||
|
|
||||||
def serve(request, path, document_root=None, show_indexes=False, insecure=False):
|
def serve(request, path, document_root=None, show_indexes=False, insecure=False):
|
||||||
|
|
|
@ -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
|
First, you'll need to make sure that ``django.contrib.staticfiles`` is in
|
||||||
your :setting:`INSTALLED_APPS`.
|
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::
|
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
|
time, though the default value (of ``/static/``) is perfect for local
|
||||||
development.
|
development.
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ Here's the basic usage in a nutshell:
|
||||||
./manage.py collectstatic
|
./manage.py collectstatic
|
||||||
|
|
||||||
This'll churn through your static file storage and move them into the
|
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
|
in local development if you are using :djadmin:`runserver` or adding
|
||||||
``staticfiles_urlpatterns`` to your URLconf; see below).
|
``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
|
If you're using the built-in development server (the
|
||||||
:djadmin:`runserver` management command) and have the :setting:`DEBUG`
|
:djadmin:`runserver` management command) and have the :setting:`DEBUG`
|
||||||
setting set to ``True``, your staticfiles will automatically be served
|
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
|
If you are using some other server for local development, you can
|
||||||
quickly serve static media locally by adding::
|
quickly serve static media locally by adding::
|
||||||
|
@ -98,7 +98,7 @@ Here's the basic usage in a nutshell:
|
||||||
|
|
||||||
.. code-block:: html+django
|
.. 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
|
See :ref:`staticfiles-in-templates` for more details, including an
|
||||||
alternate method (using a template tag).
|
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
|
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
|
files. For this reason, you will probably want to make your
|
||||||
:setting:`MEDIA_ROOT` and :setting:`MEDIA_URL` different from 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;
|
arrange for serving of files in :setting:`MEDIA_ROOT` yourself;
|
||||||
``staticfiles`` does not deal with user-uploaded media at all.
|
``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
|
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.
|
(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
|
directly in your templates. This means that a switch of media servers only
|
||||||
requires changing that single value. Much better!
|
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
|
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
|
:setting:`TEMPLATE_CONTEXT_PROCESSORS`. It's there by default, and if you're
|
||||||
editing that setting by hand it should look something like::
|
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.debug',
|
||||||
'django.core.context_processors.i18n',
|
'django.core.context_processors.i18n',
|
||||||
'django.core.context_processors.media',
|
'django.core.context_processors.media',
|
||||||
|
'django.core.context_processors.static',
|
||||||
'django.contrib.auth.context_processors.auth',
|
'django.contrib.auth.context_processors.auth',
|
||||||
'django.contrib.messages.context_processors.messages',
|
'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
|
.. 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.
|
using :class:`~django.template.RequestContext` when rendering the template.
|
||||||
|
|
||||||
As a brief refresher, context processors add variables into the contexts of
|
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
|
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
|
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:
|
injected into the template. Here's an example:
|
||||||
|
|
||||||
.. code-block:: html+django
|
.. code-block:: html+django
|
||||||
|
|
||||||
{% load staticfiles %}
|
{% load static %}
|
||||||
<img src="{% get_staticfiles_prefix %}images/hi.jpg" />
|
<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
|
There's also a second form you can use to avoid extra processing if you need the
|
||||||
value multiple times:
|
value multiple times:
|
||||||
|
|
||||||
.. code-block:: html+django
|
.. code-block:: html+django
|
||||||
|
|
||||||
{% load staticfiles %}
|
{% load static %}
|
||||||
{% get_staticfiles_prefix as STATIC_PREFIX %}
|
{% get_static_prefix as STATIC_PREFIX %}
|
||||||
|
|
||||||
<img src="{{ STATIC_PREFIX }}images/hi.jpg" />
|
<img src="{{ STATIC_PREFIX }}images/hi.jpg" />
|
||||||
<img src="{{ STATIC_PREFIX }}images/hi2.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.
|
can use to serve files locally in development.
|
||||||
|
|
||||||
This view is automatically enabled and will serve your static files at
|
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,
|
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
|
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()
|
urlpatterns += staticfiles_urlpatterns()
|
||||||
|
|
||||||
This will inspect your :setting:`STATICFILES_URL` and
|
This will inspect your :setting:`STATIC_URL` and
|
||||||
:setting:`STATICFILES_ROOT` settings and wire up the view to serve static media
|
:setting:`STATIC_ROOT` settings and wire up the view to serve static media
|
||||||
accordingly. Don't forget to set the :setting:`STATICFILES_DIRS` setting
|
accordingly. Don't forget to set the :setting:`STATICFILES_DIRS` setting
|
||||||
appropriately to let ``django.contrib.staticfiles`` know where to look for
|
appropriately to let ``django.contrib.staticfiles`` know where to look for
|
||||||
files.
|
(additional) files.
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
|
|
||||||
|
@ -239,6 +239,9 @@ files.
|
||||||
**insecure**. This is only intended for local development, and should
|
**insecure**. This is only intended for local development, and should
|
||||||
**never be used in production**.
|
**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,
|
For a few more details, including an alternate method of enabling this view,
|
||||||
see :ref:`staticfiles-development-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
|
The basic outline of putting static files into production is simple: run the
|
||||||
:djadmin:`collectstatic` command when static media changes, then arrange for 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.
|
server and served.
|
||||||
|
|
||||||
Of course, as with all deployment tasks, the devil's in the details. Every
|
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.
|
* Push your code up to the deployment server.
|
||||||
* On the server, run :djadmin:`collectstatic` to move all the media into
|
* On the server, run :djadmin:`collectstatic` to move all the media into
|
||||||
:setting:`STATICFILES_ROOT`.
|
:setting:`STATIC_ROOT`.
|
||||||
* Point your web server at :setting:`STATICFILES_ROOT`. For example, here's
|
* 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>`.
|
: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
|
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:
|
deployment strategy to look something like:
|
||||||
|
|
||||||
* When your media changes, run :djadmin:`collectstatic` locally.
|
* 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
|
into the directory that's being served. ``rsync`` is a good
|
||||||
choice for this step since it only needs to transfer the
|
choice for this step since it only needs to transfer the
|
||||||
bits of static media that have changed.
|
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
|
* The management commands ``build_static`` and ``resolve_static`` are now
|
||||||
called :djadmin:`collectstatic` and :djadmin:`findstatic`.
|
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``,
|
* The settings ``STATICFILES_PREPEND_LABEL_APPS``,
|
||||||
``STATICFILES_MEDIA_DIRNAMES`` and ``STATICFILES_EXCLUDED_APPS`` were
|
``STATICFILES_MEDIA_DIRNAMES`` and ``STATICFILES_EXCLUDED_APPS`` were
|
||||||
removed.
|
removed.
|
||||||
|
|
|
@ -165,7 +165,7 @@ Do not prompt the user for input.
|
||||||
Disable the development server's auto\-reloader.
|
Disable the development server's auto\-reloader.
|
||||||
.TP
|
.TP
|
||||||
.I \-\-nostatic
|
.I \-\-nostatic
|
||||||
Disable automatic serving of static files from STATICFILES_URL.
|
Disable automatic serving of static files from STATIC_URL.
|
||||||
.TP
|
.TP
|
||||||
.I \-\-insecure
|
.I \-\-insecure
|
||||||
Enables serving of static files even if DEBUG is False.
|
Enables serving of static files even if DEBUG is False.
|
||||||
|
|
|
@ -23,48 +23,11 @@ Settings
|
||||||
|
|
||||||
.. highlight:: python
|
.. highlight:: python
|
||||||
|
|
||||||
The following settings control the behavior of the staticfiles app. Only
|
.. note::
|
||||||
:setting:`STATICFILES_ROOT` is required, but you'll probably also need to
|
|
||||||
configure :setting:`STATICFILES_URL` as well.
|
|
||||||
|
|
||||||
.. setting:: STATICFILES_ROOT
|
The following settings control the behavior of the staticfiles app.
|
||||||
|
Configuring the global settings :setting:`STATIC_ROOT` and
|
||||||
STATICFILES_ROOT
|
:setting:`STATIC_URL` is **required**.
|
||||||
----------------
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
.. setting:: STATICFILES_DIRS
|
.. setting:: STATICFILES_DIRS
|
||||||
|
|
||||||
|
@ -98,7 +61,7 @@ tuples, e.g.::
|
||||||
|
|
||||||
With this configuration, the :djadmin:`collectstatic` management command would
|
With this configuration, the :djadmin:`collectstatic` management command would
|
||||||
for example collect the stats files in a ``'downloads'`` directory. So
|
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'``
|
allow you to refer to the file ``'/opt/webfiles/stats/polls_20101022.tar.gz'``
|
||||||
with ``'/static/downloads/polls_20101022.tar.gz'`` in your templates.
|
with ``'/static/downloads/polls_20101022.tar.gz'`` in your templates.
|
||||||
|
|
||||||
|
@ -153,14 +116,14 @@ Management Commands
|
||||||
|
|
||||||
.. highlight:: console
|
.. highlight:: console
|
||||||
|
|
||||||
``django.contrib.staticfiles`` exposes two management commands.
|
``django.contrib.staticfiles`` exposes three management commands.
|
||||||
|
|
||||||
collectstatic
|
collectstatic
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
.. django-admin:: 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
|
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
|
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
|
This is a debugging aid; it'll show you exactly which static file will be
|
||||||
collected for a given path.
|
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
|
.. currentmodule:: None
|
||||||
|
|
||||||
Other Helpers
|
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
|
This context processor adds the :setting:`STATIC_URL` into each template
|
||||||
context as the variable ``{{ STATICFILES_URL }}``. To use it, make sure that
|
context as the variable ``{{ STATIC_URL }}``. To use it, make sure that
|
||||||
``'django.contrib.staticfiles.context_processors.staticfiles'`` appears
|
``'django.core.context_processors.static'`` appears somewhere in your
|
||||||
somewhere in your :setting:`TEMPLATE_CONTEXT_PROCESSORS` setting.
|
:setting:`TEMPLATE_CONTEXT_PROCESSORS` setting.
|
||||||
|
|
||||||
Remember, only templates rendered with :class:`~django.template.RequestContext`
|
Remember, only templates rendered with :class:`~django.template.RequestContext`
|
||||||
will have acces to the data provided by this (and any) context processor.
|
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
|
.. highlight:: html+django
|
||||||
|
|
||||||
If you're not using :class:`~django.template.RequestContext`, or if you need
|
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
|
more control over exactly where and how :setting:`STATIC_URL` is injected
|
||||||
into the template, you can use the :ttag:`get_staticfiles_prefix` template tag
|
into the template, you can use the :ttag:`get_static_prefix` template tag
|
||||||
instead::
|
instead::
|
||||||
|
|
||||||
{% load staticfiles %}
|
{% load static %}
|
||||||
<img src="{% get_staticfiles_prefix %}images/hi.jpg" />
|
<img src="{% get_static_prefix %}images/hi.jpg" />
|
||||||
|
|
||||||
There's also a second form you can use to avoid extra processing if you need
|
There's also a second form you can use to avoid extra processing if you need
|
||||||
the value multiple times::
|
the value multiple times::
|
||||||
|
|
||||||
{% load staticfiles %}
|
{% load static %}
|
||||||
{% get_staticfiles_prefix as STATIC_PREFIX %}
|
{% get_static_prefix as STATIC_PREFIX %}
|
||||||
|
|
||||||
<img src="{{ STATIC_PREFIX }}images/hi.jpg" />
|
<img src="{{ STATIC_PREFIX }}images/hi.jpg" />
|
||||||
<img src="{{ STATIC_PREFIX }}images/hi2.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
|
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:
|
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()
|
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/``.
|
||||||
|
|
|
@ -681,35 +681,6 @@ Example usage::
|
||||||
|
|
||||||
django-admin.py runserver --noreload
|
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
|
Examples of using different ports and addresses
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -53,10 +53,10 @@ Default: ``'/static/admin/'``
|
||||||
|
|
||||||
The URL prefix for admin media -- CSS, JavaScript and images used by the Django
|
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
|
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
|
mapped onto two different sets of files). For integration with :doc:`staticfiles
|
||||||
</ref/contrib/staticfiles>`, this should be the same as
|
</ref/contrib/staticfiles>`, this should be the same as
|
||||||
:setting:`STATICFILES_URL` followed by ``'admin/'``.
|
:setting:`STATIC_URL` followed by ``'admin/'``.
|
||||||
|
|
||||||
.. setting:: ADMINS
|
.. setting:: ADMINS
|
||||||
|
|
||||||
|
@ -1122,12 +1122,12 @@ Default: ``''`` (Empty string)
|
||||||
URL that handles the media served from :setting:`MEDIA_ROOT`, used
|
URL that handles the media served from :setting:`MEDIA_ROOT`, used
|
||||||
for :doc:`managing stored files </topics/files>`.
|
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.
|
Note that this should have a trailing slash if it has a path component.
|
||||||
|
|
||||||
* Good: ``"http://www.example.com/static/"``
|
* Good: ``"http://www.example.com/media/"``
|
||||||
* Bad: ``"http://www.example.com/static"``
|
* Bad: ``"http://www.example.com/media"``
|
||||||
|
|
||||||
MESSAGE_LEVEL
|
MESSAGE_LEVEL
|
||||||
-------------
|
-------------
|
||||||
|
@ -1486,6 +1486,49 @@ See :doc:`/ref/contrib/sites`.
|
||||||
|
|
||||||
.. _site framework docs: ../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
|
.. setting:: TEMPLATE_CONTEXT_PROCESSORS
|
||||||
|
|
||||||
TEMPLATE_CONTEXT_PROCESSORS
|
TEMPLATE_CONTEXT_PROCESSORS
|
||||||
|
@ -1496,7 +1539,8 @@ Default::
|
||||||
("django.contrib.auth.context_processors.auth",
|
("django.contrib.auth.context_processors.auth",
|
||||||
"django.core.context_processors.debug",
|
"django.core.context_processors.debug",
|
||||||
"django.core.context_processors.i18n",
|
"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")
|
"django.contrib.messages.context_processors.messages")
|
||||||
|
|
||||||
A tuple of callables that are used to populate the context in ``RequestContext``.
|
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.core.context_processors.auth`` to
|
||||||
``django.contrib.auth.context_processors.auth``.
|
``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
|
.. setting:: TEMPLATE_DEBUG
|
||||||
|
|
||||||
TEMPLATE_DEBUG
|
TEMPLATE_DEBUG
|
||||||
|
|
|
@ -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.debug",
|
||||||
"django.core.context_processors.i18n",
|
"django.core.context_processors.i18n",
|
||||||
"django.core.context_processors.media",
|
"django.core.context_processors.media",
|
||||||
"django.contrib.messages.context_processors.messages",
|
"django.core.context_processors.static",
|
||||||
"django.contrib.staticfiles.context_processors.staticfiles")
|
"django.contrib.messages.context_processors.messages")
|
||||||
|
|
||||||
.. versionadded:: 1.2
|
.. versionadded:: 1.2
|
||||||
In addition to these, ``RequestContext`` always uses
|
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
|
``RequestContext`` will contain a variable ``MEDIA_URL``, providing the
|
||||||
value of the :setting:`MEDIA_URL` setting.
|
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
|
django.core.context_processors.csrf
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -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`_
|
.. _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
|
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.
|
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
|
Backwards-incompatible changes in 1.3 alpha 1
|
||||||
=============================================
|
=============================================
|
||||||
|
@ -270,8 +270,6 @@ local flavors:
|
||||||
official designation "Aceh (ACE)".
|
official designation "Aceh (ACE)".
|
||||||
|
|
||||||
|
|
||||||
.. _deprecated-features-1.3:
|
|
||||||
|
|
||||||
Features deprecated in 1.3
|
Features deprecated in 1.3
|
||||||
==========================
|
==========================
|
||||||
|
|
||||||
|
|
|
@ -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
|
As such, this release is *not* intended for production use, and any such use
|
||||||
is discouraged.
|
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
|
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
|
for more details or learn how to :doc:`manage static files
|
||||||
</howto/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
|
The Django 1.3 roadmap
|
||||||
======================
|
======================
|
||||||
|
|
||||||
|
|
|
@ -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
|
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
|
files. For this reason, you will probably want to make your
|
||||||
:setting:`MEDIA_ROOT` and :setting:`MEDIA_URL` different from 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;
|
arrange for serving of files in :setting:`MEDIA_ROOT` yourself;
|
||||||
``staticfiles`` does not deal with user-uploaded media at all.
|
``staticfiles`` does not deal with user-uploaded media at all.
|
||||||
|
|
||||||
|
|
|
@ -64,6 +64,7 @@ notes.
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 1
|
:maxdepth: 1
|
||||||
|
|
||||||
|
1.3-alpha-2
|
||||||
1.3-alpha-1
|
1.3-alpha-1
|
||||||
1.2-rc-1
|
1.2-rc-1
|
||||||
1.2-beta-1
|
1.2-beta-1
|
||||||
|
|
|
@ -190,28 +190,51 @@ also be defined in a dynamic fashion::
|
||||||
See the section on `Media objects`_ for more details on how to construct
|
See the section on `Media objects`_ for more details on how to construct
|
||||||
return values for dynamic media properties.
|
return values for dynamic media properties.
|
||||||
|
|
||||||
|
.. _form-media-paths:
|
||||||
|
|
||||||
Paths in media definitions
|
Paths in media definitions
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
|
.. versionchanged:: 1.3
|
||||||
|
|
||||||
Paths used to specify media can be either relative or absolute. If a path
|
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
|
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
|
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
|
the appropriate prefix.
|
||||||
``http://media.example.com/``::
|
|
||||||
|
|
||||||
class CalendarWidget(forms.TextInput):
|
As part of the introduction of the
|
||||||
class Media:
|
:doc:`staticfiles app </ref/contrib/staticfiles>` two new settings were added
|
||||||
css = {
|
to refer to "static content" (images, CSS, Javascript, etc.) that are needed
|
||||||
'all': ('/css/pretty.css',),
|
to render a complete web page: :setting:`STATIC_URL` and :setting:`STATIC_ROOT`.
|
||||||
}
|
|
||||||
js = ('animations.js', 'http://othersite.com/actions.js')
|
To find the appropriate prefix to use, Django will check if the
|
||||||
|
:setting:`STATIC_URL` setting is not ``None`` and automatically fall back
|
||||||
|
to using :setting:`MEDIA_URL`. For example, if the :setting:`MEDIA_URL` for
|
||||||
|
your site was ``'http://uploads.example.com/'`` and :setting:`STATIC_URL`
|
||||||
|
was ``None``::
|
||||||
|
|
||||||
|
>>> class CalendarWidget(forms.TextInput):
|
||||||
|
class Media:
|
||||||
|
css = {
|
||||||
|
'all': ('/css/pretty.css',),
|
||||||
|
}
|
||||||
|
js = ('animations.js', 'http://othersite.com/actions.js')
|
||||||
|
|
||||||
>>> w = CalendarWidget()
|
>>> w = CalendarWidget()
|
||||||
>>> print w.media
|
>>> print w.media
|
||||||
<link href="/css/pretty.css" type="text/css" media="all" rel="stylesheet" />
|
<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>
|
<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
|
Media objects
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
|
|
|
@ -458,3 +458,463 @@ class FormsMediaTestCase(TestCase):
|
||||||
<link href="/path/to/css2" 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="/path/to/css3" type="text/css" media="all" rel="stylesheet" />
|
||||||
<link href="/some/form/css" 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" />""")
|
||||||
|
|
||||||
|
|
|
@ -23,8 +23,8 @@ class StaticFilesTestCase(TestCase):
|
||||||
Test case with a couple utility assertions.
|
Test case with a couple utility assertions.
|
||||||
"""
|
"""
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.old_staticfiles_url = settings.STATICFILES_URL
|
self.old_static_url = settings.STATIC_URL
|
||||||
self.old_staticfiles_root = settings.STATICFILES_ROOT
|
self.old_static_root = settings.STATIC_ROOT
|
||||||
self.old_staticfiles_dirs = settings.STATICFILES_DIRS
|
self.old_staticfiles_dirs = settings.STATICFILES_DIRS
|
||||||
self.old_staticfiles_finders = settings.STATICFILES_FINDERS
|
self.old_staticfiles_finders = settings.STATICFILES_FINDERS
|
||||||
self.old_media_root = settings.MEDIA_ROOT
|
self.old_media_root = settings.MEDIA_ROOT
|
||||||
|
@ -40,8 +40,8 @@ class StaticFilesTestCase(TestCase):
|
||||||
settings.DEBUG = True
|
settings.DEBUG = True
|
||||||
settings.MEDIA_ROOT = os.path.join(site_media, 'media')
|
settings.MEDIA_ROOT = os.path.join(site_media, 'media')
|
||||||
settings.MEDIA_URL = '/media/'
|
settings.MEDIA_URL = '/media/'
|
||||||
settings.STATICFILES_ROOT = os.path.join(site_media, 'static')
|
settings.STATIC_ROOT = os.path.join(site_media, 'static')
|
||||||
settings.STATICFILES_URL = '/static/'
|
settings.STATIC_URL = '/static/'
|
||||||
settings.ADMIN_MEDIA_PREFIX = '/static/admin/'
|
settings.ADMIN_MEDIA_PREFIX = '/static/admin/'
|
||||||
settings.STATICFILES_DIRS = (
|
settings.STATICFILES_DIRS = (
|
||||||
os.path.join(TEST_ROOT, 'project', 'documents'),
|
os.path.join(TEST_ROOT, 'project', 'documents'),
|
||||||
|
@ -52,6 +52,7 @@ class StaticFilesTestCase(TestCase):
|
||||||
'django.contrib.staticfiles.finders.DefaultStorageFinder',
|
'django.contrib.staticfiles.finders.DefaultStorageFinder',
|
||||||
)
|
)
|
||||||
settings.INSTALLED_APPS = [
|
settings.INSTALLED_APPS = [
|
||||||
|
"django.contrib.staticfiles",
|
||||||
"regressiontests.staticfiles_tests",
|
"regressiontests.staticfiles_tests",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -65,8 +66,8 @@ class StaticFilesTestCase(TestCase):
|
||||||
settings.MEDIA_ROOT = self.old_media_root
|
settings.MEDIA_ROOT = self.old_media_root
|
||||||
settings.MEDIA_URL = self.old_media_url
|
settings.MEDIA_URL = self.old_media_url
|
||||||
settings.ADMIN_MEDIA_PREFIX = self.old_admin_media_prefix
|
settings.ADMIN_MEDIA_PREFIX = self.old_admin_media_prefix
|
||||||
settings.STATICFILES_ROOT = self.old_staticfiles_root
|
settings.STATIC_ROOT = self.old_static_root
|
||||||
settings.STATICFILES_URL = self.old_staticfiles_url
|
settings.STATIC_URL = self.old_static_url
|
||||||
settings.STATICFILES_DIRS = self.old_staticfiles_dirs
|
settings.STATICFILES_DIRS = self.old_staticfiles_dirs
|
||||||
settings.STATICFILES_FINDERS = self.old_staticfiles_finders
|
settings.STATICFILES_FINDERS = self.old_staticfiles_finders
|
||||||
settings.INSTALLED_APPS = self.old_installed_apps
|
settings.INSTALLED_APPS = self.old_installed_apps
|
||||||
|
@ -91,13 +92,13 @@ class BuildStaticTestCase(StaticFilesTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(BuildStaticTestCase, self).setUp()
|
super(BuildStaticTestCase, self).setUp()
|
||||||
self.old_staticfiles_storage = settings.STATICFILES_STORAGE
|
self.old_staticfiles_storage = settings.STATICFILES_STORAGE
|
||||||
self.old_root = settings.STATICFILES_ROOT
|
self.old_root = settings.STATIC_ROOT
|
||||||
settings.STATICFILES_ROOT = tempfile.mkdtemp()
|
settings.STATIC_ROOT = tempfile.mkdtemp()
|
||||||
self.run_collectstatic()
|
self.run_collectstatic()
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
shutil.rmtree(settings.STATICFILES_ROOT)
|
shutil.rmtree(settings.STATIC_ROOT)
|
||||||
settings.STATICFILES_ROOT = self.old_root
|
settings.STATIC_ROOT = self.old_root
|
||||||
super(BuildStaticTestCase, self).tearDown()
|
super(BuildStaticTestCase, self).tearDown()
|
||||||
|
|
||||||
def run_collectstatic(self, **kwargs):
|
def run_collectstatic(self, **kwargs):
|
||||||
|
@ -106,7 +107,7 @@ class BuildStaticTestCase(StaticFilesTestCase):
|
||||||
|
|
||||||
def _get_file(self, filepath):
|
def _get_file(self, filepath):
|
||||||
assert filepath, 'filepath is empty.'
|
assert filepath, 'filepath is empty.'
|
||||||
filepath = os.path.join(settings.STATICFILES_ROOT, filepath)
|
filepath = os.path.join(settings.STATIC_ROOT, filepath)
|
||||||
f = open(filepath)
|
f = open(filepath)
|
||||||
try:
|
try:
|
||||||
return f.read()
|
return f.read()
|
||||||
|
@ -231,7 +232,7 @@ class TestBuildStaticDryRun(BuildStaticTestCase):
|
||||||
"""
|
"""
|
||||||
With --dry-run, no files created in destination dir.
|
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':
|
if sys.platform != 'win32':
|
||||||
|
@ -251,7 +252,7 @@ if sys.platform != 'win32':
|
||||||
With ``--link``, symbolic links are created.
|
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):
|
class TestServeStatic(StaticFilesTestCase):
|
||||||
|
@ -262,7 +263,7 @@ class TestServeStatic(StaticFilesTestCase):
|
||||||
|
|
||||||
def _response(self, filepath):
|
def _response(self, filepath):
|
||||||
return self.client.get(
|
return self.client.get(
|
||||||
posixpath.join(settings.STATICFILES_URL, filepath))
|
posixpath.join(settings.STATIC_URL, filepath))
|
||||||
|
|
||||||
def assertFileContains(self, filepath, text):
|
def assertFileContains(self, filepath, text):
|
||||||
self.assertContains(self._response(filepath), text)
|
self.assertContains(self._response(filepath), text)
|
||||||
|
@ -372,24 +373,3 @@ class TestMiscFinder(TestCase):
|
||||||
finders.get_finder, "django.contrib.staticfiles.finders.FooBarFinder")
|
finders.get_finder, "django.contrib.staticfiles.finders.FooBarFinder")
|
||||||
self.assertRaises(ImproperlyConfigured,
|
self.assertRaises(ImproperlyConfigured,
|
||||||
finders.get_finder, "foo.bar.FooBarFinder")
|
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)
|
|
||||||
|
|
|
@ -114,6 +114,16 @@ class UTF8Class:
|
||||||
return u'ŠĐĆŽćžšđ'.encode('utf-8')
|
return u'ŠĐĆŽćžšđ'.encode('utf-8')
|
||||||
|
|
||||||
class Templates(unittest.TestCase):
|
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):
|
def test_loaders_security(self):
|
||||||
ad_loader = app_directories.Loader()
|
ad_loader = app_directories.Loader()
|
||||||
fs_loader = filesystem.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),
|
'autoescape-filtertag01': ("{{ first }}{% filter safe %}{{ first }} x<y{% endfilter %}", {"first": "<a>"}, template.TemplateSyntaxError),
|
||||||
|
|
||||||
# ifqeual compares unescaped vales.
|
# 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.
|
# Arguments to filters are 'safe' and manipulate their input unescaped.
|
||||||
'autoescape-filters01': ('{{ var|cut:"&" }}', { "var": "this & that" }, "this that" ),
|
'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.
|
# Literal strings are safe.
|
||||||
'autoescape-literals01': ('{{ "this & that" }}',{}, "this & that" ),
|
'autoescape-literals01': ('{{ "this & that" }}',{}, "this & that"),
|
||||||
|
|
||||||
# Iterating over strings outputs safe characters.
|
# Iterating over strings outputs safe characters.
|
||||||
'autoescape-stringiterations01': ('{% for l in var %}{{ l }},{% endfor %}', {'var': 'K&R'}, "K,&,R," ),
|
'autoescape-stringiterations01': ('{% for l in var %}{{ l }},{% endfor %}', {'var': 'K&R'}, "K,&,R,"),
|
||||||
|
|
||||||
# Escape requirement survives lookup.
|
# Escape requirement survives lookup.
|
||||||
'autoescape-lookup01': ('{{ var.key }}', { "var": {"key": "this & that" }}, "this & that" ),
|
'autoescape-lookup01': ('{{ var.key }}', { "var": {"key": "this & that" }}, "this & 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):
|
class TemplateTagLoading(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
|
Loading…
Reference in New Issue