Fixed #925 -- Added TEMPLATE_CONTEXT_PROCESSORS, which lets you specify processesors for DjangoContext. Thanks, Luke Plant

git-svn-id: http://code.djangoproject.com/svn/django/trunk@1773 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Adrian Holovaty 2005-12-24 04:39:59 +00:00
parent d9269055c9
commit 49fd163a95
9 changed files with 212 additions and 86 deletions

View File

@ -100,6 +100,15 @@ TEMPLATE_LOADERS = (
# 'django.core.template.loaders.eggs.load_template_source',
)
# List of processors used by DjangoContext to populate the context.
# Each one should be a callable that takes the request object as its
# only parameter and returns a dictionary to add to the context.
TEMPLATE_CONTEXT_PROCESSORS = (
'django.core.context_processors.auth',
'django.core.context_processors.debug',
'django.core.context_processors.i18n',
)
# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
# trailing slash.
# Examples: "http://foo.com/media/", "/media/".

View File

@ -2,11 +2,34 @@
# of MVC. In other words, these functions/classes introduce controlled coupling
# for convenience's sake.
from django.core.exceptions import Http404, ObjectDoesNotExist
from django.core.exceptions import Http404, ImproperlyConfigured, ObjectDoesNotExist
from django.core.template import Context, loader
from django.conf.settings import DEBUG, INTERNAL_IPS
from django.conf.settings import TEMPLATE_CONTEXT_PROCESSORS
from django.utils.httpwrappers import HttpResponse
_standard_context_processors = None
# This is a function rather than module-level procedural code because we only
# want it to execute if somebody uses DjangoContext.
def get_standard_processors():
global _standard_context_processors
if _standard_context_processors is None:
processors = []
for path in TEMPLATE_CONTEXT_PROCESSORS:
i = path.rfind('.')
module, attr = path[:i], path[i+1:]
try:
mod = __import__(module, '', '', [attr])
except ImportError, e:
raise ImproperlyConfigured, 'Error importing request processor module %s: "%s"' % (module, e)
try:
func = getattr(mod, attr)
except AttributeError:
raise ImproperlyConfigured, 'Module "%s" does not define a "%s" callable request processor' % (module, attr)
processors.append(func)
_standard_context_processors = tuple(processors)
return _standard_context_processors
def render_to_response(*args, **kwargs):
return HttpResponse(loader.render_to_string(*args, **kwargs))
load_and_render = render_to_response # For backwards compatibility.
@ -25,24 +48,19 @@ def get_list_or_404(mod, **kwargs):
class DjangoContext(Context):
"""
This subclass of template.Context automatically populates 'user' and
'messages' in the context.
This subclass of template.Context automatically populates itself using
the processors defined in TEMPLATE_CONTEXT_PROCESSORS.
Additional processors can be specified as a list of callables
using the "processors" keyword argument.
"""
def __init__(self, request, dict=None):
def __init__(self, request, dict=None, processors=None):
Context.__init__(self, dict)
self['user'] = request.user
self['messages'] = request.user.get_and_delete_messages()
self['perms'] = PermWrapper(request.user)
from django.conf import settings
self['LANGUAGES'] = settings.LANGUAGES
if hasattr(request, 'LANGUAGE_CODE'):
self['LANGUAGE_CODE'] = request.LANGUAGE_CODE
if processors is None:
processors = ()
else:
self['LANGUAGE_CODE'] = settings.LANGUAGE_CODE
if DEBUG and request.META.get('REMOTE_ADDR') in INTERNAL_IPS:
self['debug'] = True
from django.core import db
self['sql_queries'] = db.db.queries
processors = tuple(processors)
for processor in get_standard_processors() + processors:
self.update(processor(request))
# PermWrapper and PermLookupDict proxy the permissions system into objects that
# the template system can understand.

View File

@ -3,14 +3,14 @@ from django.core.xheaders import populate_xheaders
from django.core.template import loader
from django.core import formfields, meta
from django.views.auth.login import redirect_to_login
from django.core.extensions import DjangoContext as Context
from django.core.extensions import DjangoContext
from django.core.paginator import ObjectPaginator, InvalidPage
from django.utils.httpwrappers import HttpResponse, HttpResponseRedirect
from django.core.exceptions import Http404, ObjectDoesNotExist, ImproperlyConfigured
def create_object(request, app_label, module_name, template_name=None,
template_loader=loader, extra_context={},
post_save_redirect=None, login_required=False, follow=None):
template_loader=loader, extra_context={}, post_save_redirect=None,
login_required=False, follow=None, context_processors=None):
"""
Generic object-creation function.
@ -60,9 +60,9 @@ def create_object(request, app_label, module_name, template_name=None,
if not template_name:
template_name = "%s/%s_form" % (app_label, module_name)
t = template_loader.get_template(template_name)
c = Context(request, {
'form' : form,
})
c = DjangoContext(request, {
'form': form,
}, context_processors)
for key, value in extra_context.items():
if callable(value):
c[key] = value()
@ -71,9 +71,9 @@ def create_object(request, app_label, module_name, template_name=None,
return HttpResponse(t.render(c))
def update_object(request, app_label, module_name, object_id=None, slug=None,
slug_field=None, template_name=None, template_loader=loader,
extra_lookup_kwargs={}, extra_context={}, post_save_redirect=None,
login_required=False, follow=None):
slug_field=None, template_name=None, template_loader=loader,
extra_lookup_kwargs={}, extra_context={}, post_save_redirect=None,
login_required=False, follow=None, context_processors=None):
"""
Generic object-update function.
@ -131,10 +131,10 @@ def update_object(request, app_label, module_name, object_id=None, slug=None,
if not template_name:
template_name = "%s/%s_form" % (app_label, module_name)
t = template_loader.get_template(template_name)
c = Context(request, {
'form' : form,
'object' : object,
})
c = DjangoContext(request, {
'form': form,
'object': object,
}, context_processors)
for key, value in extra_context.items():
if callable(value):
c[key] = value()
@ -145,9 +145,9 @@ def update_object(request, app_label, module_name, object_id=None, slug=None,
return response
def delete_object(request, app_label, module_name, post_delete_redirect,
object_id=None, slug=None, slug_field=None, template_name=None,
template_loader=loader, extra_lookup_kwargs={},
extra_context={}, login_required=False):
object_id=None, slug=None, slug_field=None, template_name=None,
template_loader=loader, extra_lookup_kwargs={}, extra_context={},
login_required=False, context_processors=None):
"""
Generic object-delete function.
@ -188,9 +188,9 @@ def delete_object(request, app_label, module_name, post_delete_redirect,
if not template_name:
template_name = "%s/%s_confirm_delete" % (app_label, module_name)
t = template_loader.get_template(template_name)
c = Context(request, {
'object' : object,
})
c = DjangoContext(request, {
'object': object,
}, context_processors)
for key, value in extra_context.items():
if callable(value):
c[key] = value()
@ -199,4 +199,3 @@ def delete_object(request, app_label, module_name, post_delete_redirect,
response = HttpResponse(t.render(c))
populate_xheaders(request, response, app_label, module_name, getattr(object, object._meta.pk.name))
return response

View File

@ -1,14 +1,14 @@
from django.core.template import loader
from django.core.exceptions import Http404, ObjectDoesNotExist
from django.core.extensions import DjangoContext as Context
from django.core.extensions import DjangoContext
from django.core.xheaders import populate_xheaders
from django.models import get_module
from django.utils.httpwrappers import HttpResponse
import datetime, time
def archive_index(request, app_label, module_name, date_field, num_latest=15,
template_name=None, template_loader=loader,
extra_lookup_kwargs={}, extra_context={}, allow_empty=False):
template_name=None, template_loader=loader, extra_lookup_kwargs={},
extra_context={}, allow_empty=False, context_processors=None):
"""
Generic top-level archive of date-based objects.
@ -38,10 +38,10 @@ def archive_index(request, app_label, module_name, date_field, num_latest=15,
if not template_name:
template_name = "%s/%s_archive" % (app_label, module_name)
t = template_loader.get_template(template_name)
c = Context(request, {
c = DjangoContext(request, {
'date_list' : date_list,
'latest' : latest,
})
}, context_processors)
for key, value in extra_context.items():
if callable(value):
c[key] = value()
@ -50,8 +50,8 @@ def archive_index(request, app_label, module_name, date_field, num_latest=15,
return HttpResponse(t.render(c))
def archive_year(request, year, app_label, module_name, date_field,
template_name=None, template_loader=loader,
extra_lookup_kwargs={}, extra_context={}):
template_name=None, template_loader=loader, extra_lookup_kwargs={},
extra_context={}, context_processors=None):
"""
Generic yearly archive view.
@ -75,10 +75,10 @@ def archive_year(request, year, app_label, module_name, date_field,
if not template_name:
template_name = "%s/%s_archive_year" % (app_label, module_name)
t = template_loader.get_template(template_name)
c = Context(request, {
c = DjangoContext(request, {
'date_list': date_list,
'year': year,
})
}, context_processors)
for key, value in extra_context.items():
if callable(value):
c[key] = value()
@ -87,8 +87,8 @@ def archive_year(request, year, app_label, module_name, date_field,
return HttpResponse(t.render(c))
def archive_month(request, year, month, app_label, module_name, date_field,
month_format='%b', template_name=None, template_loader=loader,
extra_lookup_kwargs={}, extra_context={}):
month_format='%b', template_name=None, template_loader=loader,
extra_lookup_kwargs={}, extra_context={}, context_processors=None):
"""
Generic monthly archive view.
@ -123,10 +123,10 @@ def archive_month(request, year, month, app_label, module_name, date_field,
if not template_name:
template_name = "%s/%s_archive_month" % (app_label, module_name)
t = template_loader.get_template(template_name)
c = Context(request, {
c = DjangoContext(request, {
'object_list': object_list,
'month': date,
})
}, context_processors)
for key, value in extra_context.items():
if callable(value):
c[key] = value()
@ -135,9 +135,9 @@ def archive_month(request, year, month, app_label, module_name, date_field,
return HttpResponse(t.render(c))
def archive_day(request, year, month, day, app_label, module_name, date_field,
month_format='%b', day_format='%d', template_name=None,
template_loader=loader, extra_lookup_kwargs={},
extra_context={}, allow_empty=False):
month_format='%b', day_format='%d', template_name=None,
template_loader=loader, extra_lookup_kwargs={}, extra_context={},
allow_empty=False, context_processors=None):
"""
Generic daily archive view.
@ -172,12 +172,12 @@ def archive_day(request, year, month, day, app_label, module_name, date_field,
if not template_name:
template_name = "%s/%s_archive_day" % (app_label, module_name)
t = template_loader.get_template(template_name)
c = Context(request, {
c = DjangoContext(request, {
'object_list': object_list,
'day': date,
'previous_day': date - datetime.timedelta(days=1),
'next_day': (date < datetime.date.today()) and (date + datetime.timedelta(days=1)) or None,
})
}, context_processors)
for key, value in extra_context.items():
if callable(value):
c[key] = value()
@ -198,10 +198,10 @@ def archive_today(request, **kwargs):
return archive_day(request, **kwargs)
def object_detail(request, year, month, day, app_label, module_name, date_field,
month_format='%b', day_format='%d', object_id=None, slug=None,
slug_field=None, template_name=None, template_name_field=None,
template_loader=loader, extra_lookup_kwargs={},
extra_context={}):
month_format='%b', day_format='%d', object_id=None, slug=None,
slug_field=None, template_name=None, template_name_field=None,
template_loader=loader, extra_lookup_kwargs={}, extra_context={},
context_processors=None):
"""
Generic detail view from year/month/day/slug or year/month/day/id structure.
@ -241,9 +241,9 @@ def object_detail(request, year, month, day, app_label, module_name, date_field,
t = template_loader.select_template(template_name_list)
else:
t = template_loader.get_template(template_name)
c = Context(request, {
c = DjangoContext(request, {
'object': object,
})
}, context_processors)
for key, value in extra_context.items():
if callable(value):
c[key] = value()

View File

@ -2,13 +2,13 @@ from django import models
from django.core.template import loader
from django.utils.httpwrappers import HttpResponse
from django.core.xheaders import populate_xheaders
from django.core.extensions import DjangoContext as Context
from django.core.extensions import DjangoContext
from django.core.paginator import ObjectPaginator, InvalidPage
from django.core.exceptions import Http404, ObjectDoesNotExist
def object_list(request, app_label, module_name, paginate_by=None, allow_empty=False,
template_name=None, template_loader=loader,
extra_lookup_kwargs={}, extra_context={}):
template_name=None, template_loader=loader, extra_lookup_kwargs={},
extra_context={}, context_processors=None):
"""
Generic list of objects.
@ -48,7 +48,7 @@ def object_list(request, app_label, module_name, paginate_by=None, allow_empty=F
else:
raise Http404
page = int(page)
c = Context(request, {
c = DjangoContext(request, {
'object_list': object_list,
'is_paginated': paginator.pages > 1,
'results_per_page': paginate_by,
@ -59,13 +59,13 @@ def object_list(request, app_label, module_name, paginate_by=None, allow_empty=F
'previous': page - 1,
'pages': paginator.pages,
'hits' : paginator.hits,
})
}, context_processors)
else:
object_list = mod.get_list(**lookup_kwargs)
c = Context(request, {
c = DjangoContext(request, {
'object_list': object_list,
'is_paginated': False
})
}, context_processors)
if len(object_list) == 0 and not allow_empty:
raise Http404
for key, value in extra_context.items():
@ -79,9 +79,9 @@ def object_list(request, app_label, module_name, paginate_by=None, allow_empty=F
return HttpResponse(t.render(c))
def object_detail(request, app_label, module_name, object_id=None, slug=None,
slug_field=None, template_name=None, template_name_field=None,
template_loader=loader, extra_lookup_kwargs={},
extra_context={}):
slug_field=None, template_name=None, template_name_field=None,
template_loader=loader, extra_lookup_kwargs={}, extra_context={},
context_processors=None):
"""
Generic list of objects.
@ -110,9 +110,9 @@ def object_detail(request, app_label, module_name, object_id=None, slug=None,
t = template_loader.select_template(template_name_list)
else:
t = template_loader.get_template(template_name)
c = Context(request, {
c = DjangoContext(request, {
'object': object,
})
}, context_processors)
for key, value in extra_context.items():
if callable(value):
c[key] = value()

View File

@ -438,6 +438,15 @@ Authentication data in templates
The currently logged-in user and his/her permissions are made available in the
`template context`_ when you use ``DjangoContext``.
.. admonition:: Technicality
Technically, these variables are only made available in the template context
if you use ``DjangoContext`` *and* your ``TEMPLATE_CONTEXT_PROCESSORS``
setting contains ``"django.core.context_processors.auth"``, which is default.
For more, see the `DjangoContext docs`_.
.. _DjangoContext docs: http://www.djangoproject.com/documentation/templates_python/#subclassing-context-djangocontext
Users
-----
@ -454,7 +463,7 @@ Permissions
-----------
The currently logged-in user's permissions are stored in the template variable
``{{ perms }}``. This is an instance of ``django.core.extensions.PermWrapper``,
``{{ perms }}``. This is an instance of ``django.core.context_processors.PermWrapper``,
which is a template-friendly proxy of permissions.
In the ``{{ perms }}`` object, single-attribute lookup is a proxy to

View File

@ -128,9 +128,14 @@ arguments:
``extra_context`` A dictionary of extra data to put into the
template's context.
``processors`` **New in Django development version.** A tuple of
processors to apply to the ``DjangoContext`` of
this view's template. See the `DjangoContext docs`_
======================= ==================================================
.. _`database API docs`: http://www.djangoproject.com/documentation/db_api/
.. _database API docs: http://www.djangoproject.com/documentation/db_api/
.. _DjangoContext docs: http://www.djangoproject.com/documentation/templates_python/#subclassing-context-djangocontext
The date-based generic functions are:
@ -247,7 +252,7 @@ The list-detail generic-view framework (in the
except the former simply has two views: a list of objects and an individual
object page.
All these views take the same three optional arguments as the date-based ones
All these views take the same four optional arguments as the date-based ones
-- and, clearly, they don't accept the ``date_field`` argument.
Individual views are:
@ -371,4 +376,3 @@ The create/update/delete views are:
object
The object about to be deleted

View File

@ -547,6 +547,21 @@ The ID, as an integer, of the current site in the ``sites`` database. This is
used so that application data can hook into specific site(s) and a single
database can manage content for multiple sites.
TEMPLATE_CONTEXT_PROCESSORS
---------------------------
Default::
("django.core.context_processors.auth",
"django.core.context_processors.debug",
"django.core.context_processors.i18n")
**Only available in Django development version.**
A tuple of callables that are used to populate the context in ``DjangoContext``.
These callables take a request object as their argument and return a dictionary
of items to be merged into the context.
TEMPLATE_DEBUG
--------------

View File

@ -240,21 +240,77 @@ Subclassing Context: DjangoContext
Django comes with a special ``Context`` class,
``django.core.extensions.DjangoContext``, that acts slightly differently than
the normal ``django.core.template.Context``. It takes an ``HttpRequest`` object
as its first argument, and it automatically populates the context with a few
variables:
the normal ``django.core.template.Context``. The first difference is that takes
an `HttpRequest object`_ as its first argument. For example::
c = DjangoContext(request, {
'foo': 'bar',
}
The second difference is that it automatically populates the context with a few
variables, according to your `TEMPLATE_CONTEXT_PROCESSORS setting`_.
The ``TEMPLATE_CONTEXT_PROCESSORS`` setting is a tuple of callables that take a
request object as their argument and return a dictionary of items to be merged
into the context. By default, ``TEMPLATE_CONTEXT_PROCESSORS`` is set to::
("django.core.context_processors.auth",
"django.core.context_processors.debug",
"django.core.context_processors.i18n")
Each processor is applied in order. That means, if one processor adds a
variable to the context and a second processor adds a variable with the same
name, the second will override the first. The default processors are explained
below.
Also, you can give ``DjangoContext`` a list of additional processors, using the
optional, third positional argument, ``processors``. In this example, the
``DjangoContext`` instance gets a ``ip_address`` variable::
def ip_address_processor(request):
return {'ip_address': request.META['REMOTE_ADDR']}
def some_view(request):
# ...
return DjangoContext({
'foo': 'bar',
}, [ip_address_processor])
Note: The concept of template-context processors is new in the Django
development version. In Django 0.90, ``DjangoContext`` automatically populates
the context with all of the values explained below, but it's not possible to
add and remove processors.
Here's what each of the default processors does:
.. _HttpRequest object: http://www.djangoproject.com/documentation/request_response/#httprequest-objects
.. _TEMPLATE_CONTEXT_PROCESSORS setting: http://www.djangoproject.com/documentation/settings/#template-context_processors
django.core.context_processors.auth
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If ``TEMPLATE_CONTEXT_PROCESSORS`` contains this processor, every
``DjangoContext`` will contain these three variables:
* ``user`` -- An ``auth.User`` instance representing the currently
logged-in user (or an ``AnonymousUser`` instance, if the client isn't
logged in). See the `user authentication docs`.
* ``messages`` -- A list of ``auth.Message`` objects for the currently
logged-in user.
* ``perms`` -- An instance of ``django.core.extensions.PermWrapper``,
* ``perms`` -- An instance of ``django.core.context_processors.PermWrapper``,
representing the permissions that the currently logged-in user has. See
the `permissions docs`_.
Also, if your ``DEBUG`` setting is set to ``True``, every ``DjangoContext``
instance has the following two extra variables:
.. _user authentication docs: http://www.djangoproject.com/documentation/models/authentication/#users
.. _permissions docs: http://www.djangoproject.com/documentation/models/authentication/#permissions
django.core.context_processors.debug
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If ``TEMPLATE_CONTEXT_PROCESSORS`` contains this processor, every
``DjangoContext`` will contain these two variables -- but only if your
``DEBUG`` setting is set to ``True`` and the request's IP address
(``request.META['REMOTE_ADDR']``) is in the ``INTERNAL_IPS`` setting:
* ``debug`` -- ``True``. You can use this in templates to test whether
you're in ``DEBUG`` mode.
@ -262,6 +318,25 @@ instance has the following two extra variables:
representing every SQL query that has happened so far during the request
and how long it took. The list is in order by query.
django.core.context_processors.i18n
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If ``TEMPLATE_CONTEXT_PROCESSORS`` contains this processor, every
``DjangoContext`` will contain these two variables:
* ``LANGUAGES`` -- The value of the `LANGUAGES setting`_.
* ``LANGUAGE_CODE`` -- ``request.LANGUAGE_CODE``, if it exists. Otherwise,
the value of the `LANGUAGE_CODE setting`_.
See the `internationalization docs`_ for more.
.. _LANGUAGES setting: http://www.djangoproject.com/documentation/settings/#languages
.. _LANGUAGE_CODE setting: http://www.djangoproject.com/documentation/settings/#language-code
.. _internationalization docs: http://www.djangoproject.com/documentation/i18n/
Subclassing Context: Custom subclasses
--------------------------------------
Feel free to subclass ``Context`` yourself if you find yourself wanting to give
each template something "automatically." For instance, if you want to give
every template automatic access to the current time, use something like this::
@ -281,9 +356,6 @@ This technique has two caveats:
* You'll have to be careful not to set the variable ``current_time`` when
you populate this context. If you do, you'll override the other one.
.. _user authentication docs: http://www.djangoproject.com/documentation/models/authentication/#users
.. _permissions docs: http://www.djangoproject.com/documentation/models/authentication/#permissions
Loading templates
-----------------