Fixed #14524, #14582, #14617, #14665 and #14667 -- Tweaked staticfiles app.

* Updated StaticFilesHandler and AdminMediaHandler
  to make use of the 404 handler if needed.

* Updated runserver management command to serve static files
  only in DEBUG mode (or if specified the --insecure option)
  and if the staticfiles app is in INSTALLED_APPS. Also added
  an option to disable serving completely (--nostatic).

* Added check in debug mode if STATICFILES_* settings are
  different to MEDIA_* settings.

* Removed a faulty PendingDeprecationWarning in AdminMediaHandler
  that is triggered every time runserver is used.

* Fixed an issue with the modification time checks when
  running collectstatic.

* Extended and refined documentation.

Thanks to everyone for input, especially to Carl Meyer, Ted Kaemming and
Adam Vandenberg for patches.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@14533 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Jannis Leidel 2010-11-11 21:43:49 +00:00
parent 216fdfab61
commit 8e96584f63
14 changed files with 190 additions and 78 deletions

View File

@ -1,10 +1,10 @@
import os
import urllib import urllib
from urlparse import urlparse from urlparse import urlparse
from django.core.handlers.wsgi import WSGIHandler, STATUS_CODE_TEXT from django.conf import settings
from django.http import Http404 from django.core.handlers.wsgi import WSGIHandler
from django.contrib.staticfiles import utils
from django.contrib.staticfiles.views import serve from django.contrib.staticfiles.views import serve
class StaticFilesHandler(WSGIHandler): class StaticFilesHandler(WSGIHandler):
@ -18,16 +18,28 @@ class StaticFilesHandler(WSGIHandler):
self.media_dir = media_dir self.media_dir = media_dir
else: else:
self.media_dir = self.get_media_dir() self.media_dir = self.get_media_dir()
self.media_url = self.get_media_url() self.media_url = urlparse(self.get_media_url())
if settings.DEBUG:
utils.check_settings()
super(StaticFilesHandler, self).__init__()
def get_media_dir(self): def get_media_dir(self):
from django.conf import settings
return settings.STATICFILES_ROOT return settings.STATICFILES_ROOT
def get_media_url(self): def get_media_url(self):
from django.conf import settings
return settings.STATICFILES_URL return settings.STATICFILES_URL
def _should_handle(self, path):
"""
Checks if the path should be handled. Ignores the path if:
* the host is provided as part of the media_url
* the request's path isn't under the media path (or equal)
* settings.DEBUG isn't True
"""
return (self.media_url[2] != path and
path.startswith(self.media_url[2]) and not self.media_url[1])
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.
@ -37,36 +49,28 @@ class StaticFilesHandler(WSGIHandler):
is raised. is raised.
""" """
# Remove ``media_url``. # Remove ``media_url``.
relative_url = url[len(self.media_url):] relative_url = url[len(self.media_url[2]):]
return urllib.url2pathname(relative_url) return urllib.url2pathname(relative_url)
def serve(self, request, path): def serve(self, request):
from django.contrib.staticfiles import finders """
absolute_path = finders.find(path) Actually serves the request path.
if not absolute_path: """
raise Http404('%r could not be matched to a static file.' % path) return serve(request, self.file_path(request.path), insecure=True)
absolute_path, filename = os.path.split(absolute_path)
return serve(request, path=filename, document_root=absolute_path) def get_response(self, request):
from django.http import Http404
if self._should_handle(request.path):
try:
return self.serve(request)
except Http404, e:
if settings.DEBUG:
from django.views import debug
return debug.technical_404_response(request, e)
return super(StaticFilesHandler, self).get_response(request)
def __call__(self, environ, start_response): def __call__(self, environ, start_response):
media_url_bits = urlparse(self.media_url) if not self._should_handle(environ['PATH_INFO']):
# Ignore all requests if the host is provided as part of the media_url.
# Also ignore requests that aren't under the media path.
if (media_url_bits[1] or
not environ['PATH_INFO'].startswith(media_url_bits[2])):
return self.application(environ, start_response) return self.application(environ, start_response)
request = self.application.request_class(environ) return super(StaticFilesHandler, self).__call__(environ, start_response)
try:
response = self.serve(request, self.file_path(environ['PATH_INFO']))
except Http404:
status = '404 NOT FOUND'
start_response(status, {'Content-type': 'text/plain'}.items())
return [str('Page not found: %s' % environ['PATH_INFO'])]
status_text = STATUS_CODE_TEXT[response.status_code]
status = '%s %s' % (response.status_code, status_text)
response_headers = [(str(k), str(v)) for k, v in response.items()]
for c in response.cookies.values():
response_headers.append(('Set-Cookie', str(c.output(header=''))))
start_response(status, response_headers)
return response

View File

@ -71,6 +71,9 @@ Type 'yes' to continue, or 'no' to cancel: """)
if confirm != 'yes': if confirm != 'yes':
raise CommandError("Static files build cancelled.") raise CommandError("Static files build cancelled.")
# Use ints for file times (ticket #14665)
os.stat_float_times(False)
for finder in finders.get_finders(): for finder in finders.get_finders():
for source, prefix, storage in finder.list(ignore_patterns): for source, prefix, storage in finder.list(ignore_patterns):
self.copy_file(source, prefix, storage, **options) self.copy_file(source, prefix, storage, **options)
@ -126,7 +129,7 @@ Type 'yes' to continue, or 'no' to cancel: """)
else: else:
destination_is_link = os.path.islink( destination_is_link = os.path.islink(
self.destination_storage.path(destination)) self.destination_storage.path(destination))
if destination_last_modified == source_last_modified: if destination_last_modified >= source_last_modified:
if (not symlink and not destination_is_link): if (not symlink and not destination_is_link):
if verbosity >= 2: if verbosity >= 2:
self.stdout.write("Skipping '%s' (not modified)\n" self.stdout.write("Skipping '%s' (not modified)\n"

View File

@ -27,6 +27,8 @@ class StaticFilesStorage(FileSystemStorage):
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 STATICFILES_URL setting. Set it to "
"URL that handles the files served from STATICFILES_ROOT.") "URL that handles the files served from STATICFILES_ROOT.")
if settings.DEBUG:
utils.check_settings()
super(StaticFilesStorage, self).__init__(location, base_url, *args, **kwargs) super(StaticFilesStorage, self).__init__(location, base_url, *args, **kwargs)

View File

@ -19,10 +19,14 @@ def staticfiles_urlpatterns(prefix=None):
return [] return []
if prefix is None: if prefix is None:
prefix = settings.STATICFILES_URL prefix = settings.STATICFILES_URL
if not prefix:
raise ImproperlyConfigured(
"The prefix for the 'staticfiles_urlpatterns' helper is empty. "
"Make sure the STATICFILES_URL setting is set correctly.")
if '://' in prefix: if '://' in prefix:
raise ImproperlyConfigured( raise ImproperlyConfigured(
"The STATICFILES_URL setting is a full URL, not a path and " "The STATICFILES_URL setting is a full URL, not a path and "
"can't be used with the urls.staticfiles_urlpatterns() helper.") "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('',

View File

@ -1,4 +1,6 @@
import fnmatch import fnmatch
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
def get_files(storage, ignore_patterns=[], location=''): def get_files(storage, ignore_patterns=[], location=''):
""" """
@ -28,3 +30,16 @@ def get_files(storage, ignore_patterns=[], location=''):
dir = '/'.join([location, dir]) dir = '/'.join([location, dir])
static_files.extend(get_files(storage, ignore_patterns, dir)) static_files.extend(get_files(storage, ignore_patterns, dir))
return static_files return static_files
def check_settings():
"""
Checks if the MEDIA_(ROOT|URL) and STATICFILES_(ROOT|URL)
settings have the same value.
"""
if settings.MEDIA_URL == settings.STATICFILES_URL:
raise ImproperlyConfigured("The MEDIA_URL and STATICFILES_URL "
"settings must have individual values")
if ((settings.MEDIA_ROOT and settings.STATICFILES_ROOT) and
(settings.MEDIA_ROOT == settings.STATICFILES_ROOT)):
raise ImproperlyConfigured("The MEDIA_ROOT and STATICFILES_ROOT "
"settings must have individual values")

View File

@ -17,10 +17,10 @@ from django.http import Http404, HttpResponse, HttpResponseRedirect, HttpRespons
from django.template import loader, Template, Context, TemplateDoesNotExist from django.template import loader, Template, Context, TemplateDoesNotExist
from django.utils.http import http_date from django.utils.http import http_date
from django.contrib.staticfiles import finders from django.contrib.staticfiles import finders, utils
def serve(request, path, document_root=None, show_indexes=False): def serve(request, path, document_root=None, show_indexes=False, insecure=False):
""" """
Serve static files below a given point in the directory structure or Serve static files below a given point in the directory structure or
from locations inferred from the static files finders. from locations inferred from the static files finders.
@ -41,13 +41,15 @@ def serve(request, path, document_root=None, show_indexes=False):
template hardcoded below, but if you'd like to override it, you can create template hardcoded below, but if you'd like to override it, you can create
a template called ``static/directory_index.html``. a template called ``static/directory_index.html``.
""" """
if not settings.DEBUG: if not settings.DEBUG and not insecure:
raise ImproperlyConfigured("The view to serve static files can only " raise ImproperlyConfigured("The view to serve static files can only "
"be used if the DEBUG setting is True") "be used if the DEBUG setting is True or "
"the --insecure option of 'runserver' is "
"used")
if not document_root: if not document_root:
absolute_path = finders.find(path) absolute_path = finders.find(path)
if not absolute_path: if not absolute_path:
raise Http404("%r could not be matched to a static file." % path) raise Http404('"%s" could not be found' % path)
document_root, path = os.path.split(absolute_path) document_root, path = os.path.split(absolute_path)
# Clean up given path to only allow serving files below document_root. # Clean up given path to only allow serving files below document_root.
path = posixpath.normpath(urllib.unquote(path)) path = posixpath.normpath(urllib.unquote(path))

View File

@ -9,6 +9,10 @@ class Command(BaseCommand):
option_list = BaseCommand.option_list + ( option_list = BaseCommand.option_list + (
make_option('--noreload', action='store_false', dest='use_reloader', default=True, make_option('--noreload', action='store_false', dest='use_reloader', default=True,
help='Tells Django to NOT use the auto-reloader.'), help='Tells Django to NOT use the auto-reloader.'),
make_option('--nostatic', action="store_false", dest='use_static_handler', default=True,
help='Tells Django to NOT automatically serve static files at STATICFILES_URL.'),
make_option('--insecure', action="store_true", dest='insecure_serving', default=False,
help='Allows serving static files even if DEBUG is True.'),
make_option('--adminmedia', dest='admin_media_path', default='', make_option('--adminmedia', dest='admin_media_path', default='',
help='Specifies the directory from which to serve admin media.'), help='Specifies the directory from which to serve admin media.'),
) )
@ -42,6 +46,8 @@ class Command(BaseCommand):
use_reloader = options.get('use_reloader', True) use_reloader = options.get('use_reloader', True)
admin_media_path = options.get('admin_media_path', '') admin_media_path = options.get('admin_media_path', '')
shutdown_message = options.get('shutdown_message', '') shutdown_message = options.get('shutdown_message', '')
use_static_handler = options.get('use_static_handler', True)
insecure_serving = options.get('insecure_serving', False)
quit_command = (sys.platform == 'win32') and 'CTRL-BREAK' or 'CONTROL-C' quit_command = (sys.platform == 'win32') and 'CTRL-BREAK' or 'CONTROL-C'
def inner_run(): def inner_run():
@ -60,7 +66,11 @@ class Command(BaseCommand):
try: try:
handler = WSGIHandler() handler = WSGIHandler()
handler = StaticFilesHandler(handler) allow_serving = (settings.DEBUG and use_static_handler or
(use_static_handler and insecure_serving))
if (allow_serving and
"django.contrib.staticfiles" in settings.INSTALLED_APPS):
handler = StaticFilesHandler(handler)
# serve admin media like old-school (deprecation pending) # serve admin media like old-school (deprecation pending)
handler = AdminMediaHandler(handler, admin_media_path) handler = AdminMediaHandler(handler, admin_media_path)
run(addr, int(port), handler) run(addr, int(port), handler)

View File

@ -651,12 +651,6 @@ class AdminMediaHandler(StaticFilesHandler):
from django.conf import settings from django.conf import settings
return settings.ADMIN_MEDIA_PREFIX return settings.ADMIN_MEDIA_PREFIX
def __init__(self, application, media_dir=None):
warnings.warn('The AdminMediaHandler handler is deprecated; use the '
'`django.contrib.staticfiles.handlers.StaticFilesHandler` instead.',
PendingDeprecationWarning)
super(AdminMediaHandler, self).__init__(application, media_dir)
def file_path(self, url): def file_path(self, url):
""" """
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.
@ -666,13 +660,23 @@ class AdminMediaHandler(StaticFilesHandler):
is raised. is raised.
""" """
# Remove ``media_url``. # Remove ``media_url``.
relative_url = url[len(self.media_url):] 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.media_dir, relative_path)
def serve(self, request, path): def serve(self, request):
document_root, path = os.path.split(path) document_root, path = os.path.split(self.file_path(request.path))
return static.serve(request, path, document_root=document_root) return static.serve(request, path,
document_root=document_root, insecure=True)
def _should_handle(self, path):
"""
Checks if the path should be handled. Ignores the path if:
* the host is provided as part of the media_url
* the request's path isn't under the media path
"""
return path.startswith(self.media_url[2]) and not self.media_url[1]
def run(addr, port, wsgi_handler): def run(addr, port, wsgi_handler):

View File

@ -21,7 +21,7 @@ from django.contrib.staticfiles.views import \
directory_index, was_modified_since, serve as staticfiles_serve directory_index, was_modified_since, serve as staticfiles_serve
def serve(request, path, document_root=None, show_indexes=False): def serve(request, path, document_root=None, show_indexes=False, insecure=False):
""" """
Serve static files below a given point in the directory structure. Serve static files below a given point in the directory structure.
@ -38,4 +38,4 @@ def serve(request, path, document_root=None, show_indexes=False):
warnings.warn("The view at `django.views.static.serve` is deprecated; " warnings.warn("The view at `django.views.static.serve` is deprecated; "
"use the path `django.contrib.staticfiles.views.serve` " "use the path `django.contrib.staticfiles.views.serve` "
"instead.", PendingDeprecationWarning) "instead.", PendingDeprecationWarning)
return staticfiles_serve(request, path, document_root, show_indexes) return staticfiles_serve(request, path, document_root, show_indexes, insecure)

View File

@ -37,7 +37,7 @@ Using ``django.contrib.staticfiles``
Here's the basic usage in a nutshell: Here's the basic usage in a nutshell:
1. Put your media somewhere that staticfiles will find it.. 1. Put your media somewhere that staticfiles will find it.
Most of the time this place will be in a ``static`` directory within your Most of the time this place will be in a ``static`` directory within your
application, but it could also be a specific directory you've put into application, but it could also be a specific directory you've put into
@ -69,12 +69,19 @@ 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`. directory given by :setting:`STATICFILES_ROOT`. (This is not necessary
in local development if you are using :djadmin:`runserver` or adding
``staticfiles_urlpatterns`` to your URLconf; see below).
4. Deploy that media. 4. Deploy that media.
If you're using the built-in development server, you can quickly If you're using the built-in development server (the
serve static media locally by adding:: :djadmin:`runserver` management command) and have the :setting:`DEBUG`
setting set to ``True``, your staticfiles will automatically be served
from :setting:`STATICFILES_URL` in development.
If you are using some other server for local development, you can
quickly serve static media locally by adding::
from django.contrib.staticfiles.urls import staticfiles_urlpatterns from django.contrib.staticfiles.urls import staticfiles_urlpatterns
urlpatterns += staticfiles_urlpatterns() urlpatterns += staticfiles_urlpatterns()
@ -100,6 +107,18 @@ Those are the basics. For more details on common configuration options, read on;
for a detailed reference of the settings, commands, and other bits included with for a detailed reference of the settings, commands, and other bits included with
the framework see :doc:`the staticfiles reference </ref/contrib/staticfiles>`. the framework see :doc:`the staticfiles reference </ref/contrib/staticfiles>`.
.. note::
In previous versions of Django, it was common to place static assets in
:setting:`MEDIA_ROOT` along with user-uploaded files, and serve them both at
:setting:`MEDIA_URL`. Part of the purpose of introducing the ``staticfiles``
app is to make it easier to keep static files separate from user-uploaded
files. For this reason, you will probably want to make your
:setting:`MEDIA_ROOT` and :setting:`MEDIA_URL` different from your
:setting:`STATICFILES_ROOT` and :setting:`STATICFILES_URL`. You will need to
arrange for serving of files in :setting:`MEDIA_ROOT` yourself;
``staticfiles`` does not deal with user-uploaded media at all.
.. _staticfiles-in-templates: .. _staticfiles-in-templates:
Referring to static files in templates Referring to static files in templates
@ -192,8 +211,12 @@ media server, which is a lot of overhead to mess with when developing locally.
Thus, the ``staticfiles`` app ships with a quick and dirty helper view that you 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.
To enable this view, you'll add a couple of lines to your URLconf. The first This view is automatically enabled and will serve your static files at
line goes at the top of the file, and the last line at the bottom:: :setting:`STATICFILES_URL` when you use the built-in :djadmin:`runserver`.
To enable this view if you are using some other server for local development,
you'll add a couple of lines to your URLconf. The first line goes at the top of
the file, and the last line at the bottom::
from django.contrib.staticfiles.urls import staticfiles_urlpatterns from django.contrib.staticfiles.urls import staticfiles_urlpatterns
@ -242,7 +265,7 @@ app, the basic outline gets modified to look something like:
* 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:`STATICFILES_ROOT`.
* Point your web server at :setting:`STATICFILES_ROOT`. For example, here's * Point your web server at :setting:`STATICFILES_ROOT`. For example, here's
of :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
web servers. There's any number of ways to do this automation, but one option web servers. There's any number of ways to do this automation, but one option
@ -393,6 +416,10 @@ you'll need to make a few changes:
``staticfiles.storage.StaticFileStorage`` to ``staticfiles.storage.StaticFileStorage`` to
``staticfiles.storage.StaticFilesStorage`` ``staticfiles.storage.StaticFilesStorage``
* If using :djadmin:`runserver` for local development (and the
:setting:`DEBUG` setting is ``True``), you no longer need to add
anything to your URLconf for serving static files in development.
Learn more Learn more
========== ==========

View File

@ -75,7 +75,7 @@ Runs this project as a FastCGI application. Requires flup. Use
.B runfcgi help .B runfcgi help
for help on the KEY=val pairs. for help on the KEY=val pairs.
.TP .TP
.BI "runserver [" "\-\-noreload" "] [" "\-\-adminmedia=ADMIN_MEDIA_PATH" "] [" "port|ipaddr:port" "]" .BI "runserver [" "\-\-noreload" "] [" "\-\-nostatic" "] [" "\-\-insecure" "] [" "\-\-adminmedia=ADMIN_MEDIA_PATH" "] [" "port|ipaddr:port" "]"
Starts a lightweight Web server for development. Starts a lightweight Web server for development.
.TP .TP
.BI "shell [" "\-\-plain" "]" .BI "shell [" "\-\-plain" "]"

View File

@ -34,13 +34,21 @@ STATICFILES_ROOT
Default: ``''`` (Empty string) Default: ``''`` (Empty string)
The absolute path to the directory that holds static files:: 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/" STATICFILES_ROOT = "/home/example.com/static/"
This is a **required setting** unless you've overridden This is a **required setting** unless you've overridden
:setting:`STATICFILES_STORAGE` and are using a custom storage backend. :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 .. setting:: STATICFILES_URL
STATICFILES_URL STATICFILES_URL
@ -271,8 +279,10 @@ This view function serves static files in development.
**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**.
To use the view, add the following snippet to the end of your primary URL This view is automatically enabled by :djadmin:`runserver` (with a
configuration:: :setting:`DEBUG` setting set to ``True``). To use the view with a different
local development server, add the following snippet to the end of your
primary URL configuration::
from django.conf import settings from django.conf import settings

View File

@ -681,6 +681,31 @@ 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.
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 acknoledge the fact that it's
**grossly inefficient** and probably **insecure**. This is only intended for
local development, and should **never be used in production**.
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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -53,7 +53,7 @@ Default: ``'/media/'``
The URL prefix for admin media -- CSS, JavaScript and images used by The URL prefix for admin media -- CSS, JavaScript and images used by
the Django administrative interface. Make sure to use a trailing the Django administrative interface. Make sure to use a trailing
slash, and to have this be different from the ``MEDIA_URL`` setting slash, and to have this be different from the :setting:`MEDIA_URL` setting
(since the same URL cannot be mapped onto two different sets of (since the same URL cannot be mapped onto two different sets of
files). files).
@ -1104,8 +1104,12 @@ MEDIA_ROOT
Default: ``''`` (Empty string) Default: ``''`` (Empty string)
Absolute path to the directory that holds media for this installation. Absolute path to the directory that holds media for this installation, used
Example: ``"/home/media/media.lawrence.com/"`` See also ``MEDIA_URL``. for :doc:`managing stored files </topics/files>`.
Example: ``"/home/media/media.lawrence.com/"``
See also :setting:`MEDIA_URL`.
.. setting:: MEDIA_URL .. setting:: MEDIA_URL
@ -1114,15 +1118,15 @@ MEDIA_URL
Default: ``''`` (Empty string) Default: ``''`` (Empty string)
URL that handles the media served from ``MEDIA_ROOT``. URL that handles the media served from :setting:`MEDIA_ROOT`, used
for :doc:`managing stored files </topics/files>`.
Example: ``"http://media.lawrence.com"`` Example: ``"http://media.lawrence.com"``
Note that this should have a trailing slash if it has a path component. 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/static/"``
Bad: ``"http://www.example.com/static"`` * Bad: ``"http://www.example.com/static"``
.. setting:: MIDDLEWARE_CLASSES
MESSAGE_LEVEL MESSAGE_LEVEL
------------- -------------
@ -1161,6 +1165,8 @@ Default::
Sets the mapping of message levels to message tags. See the Sets the mapping of message levels to message tags. See the
:doc:`messages documentation </ref/contrib/messages>` for more details. :doc:`messages documentation </ref/contrib/messages>` for more details.
.. setting:: MIDDLEWARE_CLASSES
MIDDLEWARE_CLASSES MIDDLEWARE_CLASSES
------------------ ------------------