mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	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:
		| @@ -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" | ||||||
|   | |||||||
							
								
								
									
										84
									
								
								django/templatetags/static.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								django/templatetags/static.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,84 @@ | |||||||
|  | from django import template | ||||||
|  | from django.utils.encoding import iri_to_uri | ||||||
|  |  | ||||||
|  | register = template.Library() | ||||||
|  |  | ||||||
|  | class PrefixNode(template.Node): | ||||||
|  |  | ||||||
|  |     def __repr__(self): | ||||||
|  |         return "<PrefixNode for %r>" % self.name | ||||||
|  |  | ||||||
|  |     def __init__(self, varname=None, name=None): | ||||||
|  |         if name is None: | ||||||
|  |             raise template.TemplateSyntaxError( | ||||||
|  |                 "Prefix nodes must be given a name to return.") | ||||||
|  |         self.varname = varname | ||||||
|  |         self.name = name | ||||||
|  |  | ||||||
|  |     @classmethod | ||||||
|  |     def handle_token(cls, parser, token, name): | ||||||
|  |         """ | ||||||
|  |         Class method to parse prefix node and return a Node. | ||||||
|  |         """ | ||||||
|  |         tokens = token.contents.split() | ||||||
|  |         if len(tokens) > 1 and tokens[1] != 'as': | ||||||
|  |             raise template.TemplateSyntaxError( | ||||||
|  |                 "First argument in '%s' must be 'as'" % tokens[0]) | ||||||
|  |         if len(tokens) > 1: | ||||||
|  |             varname = tokens[2] | ||||||
|  |         else: | ||||||
|  |             varname = None | ||||||
|  |         return cls(varname, name) | ||||||
|  |  | ||||||
|  |     @classmethod | ||||||
|  |     def handle_simple(cls, name): | ||||||
|  |         try: | ||||||
|  |             from django.conf import settings | ||||||
|  |         except ImportError: | ||||||
|  |             prefix = '' | ||||||
|  |         else: | ||||||
|  |             prefix = iri_to_uri(getattr(settings, name, '')) | ||||||
|  |         return prefix | ||||||
|  |  | ||||||
|  |     def render(self, context): | ||||||
|  |         prefix = self.handle_simple(self.name) | ||||||
|  |         if self.varname is None: | ||||||
|  |             return prefix | ||||||
|  |         context[self.varname] = prefix | ||||||
|  |         return '' | ||||||
|  |  | ||||||
|  | @register.tag | ||||||
|  | def get_static_prefix(parser, token): | ||||||
|  |     """ | ||||||
|  |     Populates a template variable with the static prefix, | ||||||
|  |     ``settings.STATIC_URL``. | ||||||
|  |  | ||||||
|  |     Usage:: | ||||||
|  |  | ||||||
|  |         {% get_static_prefix [as varname] %} | ||||||
|  |  | ||||||
|  |     Examples:: | ||||||
|  |  | ||||||
|  |         {% get_static_prefix %} | ||||||
|  |         {% get_static_prefix as static_prefix %} | ||||||
|  |  | ||||||
|  |     """ | ||||||
|  |     return PrefixNode.handle_token(parser, token, "STATIC_URL") | ||||||
|  |  | ||||||
|  | @register.tag | ||||||
|  | def get_media_prefix(parser, token): | ||||||
|  |     """ | ||||||
|  |     Populates a template variable with the static prefix, | ||||||
|  |     ``settings.MEDIA_URL``. | ||||||
|  |  | ||||||
|  |     Usage:: | ||||||
|  |  | ||||||
|  |         {% get_media_prefix [as varname] %} | ||||||
|  |  | ||||||
|  |     Examples:: | ||||||
|  |  | ||||||
|  |         {% get_media_prefix %} | ||||||
|  |         {% get_media_prefix as media_prefix %} | ||||||
|  |  | ||||||
|  |     """ | ||||||
|  |     return PrefixNode.handle_token(parser, token, "MEDIA_URL") | ||||||
| @@ -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): | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user