On Thu, 24 Nov 2005 19:46:54 +0000 Robert Wittams wrote:
> and then 'generic views' would take a sequence of them as a kwarg,
>
> (r'/blog/$', 'object_list', { 'app_label' : my_blog,
> 'module_name': entries,
> 'processors': (hitcounter.processor,
> paginator.processor)
> } )
>
...
> In fact, all the DjangoContext stuff could be done using this model as
> well, AFAICT. ( separated into three processors, auth, i18n, and
> debug, which would be used by default in generic views, maybe the
> default set of processors to merge in would be in settings).
A patch implementing this is attached. It modifies most of the
generic views to use the 'processors' argument, but this is done simply
by handing the processors to DjangoContext.__init__() , which minimizes
any repetition and any changes to those views. The DjangoContext
constructor uses any processors defined in 'REQUEST_PROCESSORS', plus
any passed in.
Doing this inside DjangoContext makes it easier to move from generic
views to custom views. If your templates were using the stuff
DjangoContext provided, you can get all same context as before simply
by creating the DjangoContext object yourself in the view code.
I put the processor functions in a separate file 'processors.py'
because I couldn't find anywhere good to put them. If you put them
inside extensions.py, you have to make sure they come before the code
that parses REQUEST_PROCESSORS, which seems a bit nasty.
The patch is against 1436, and is backwards compatible. It overlaps
partly with http://code.djangoproject.com/ticket/667 -- the changes
made to extra_context would be made redundant by this patch. It also
makes http://code.djangoproject.com/ticket/195 redundant, but reverting
that would break compatibility.
Luke
--
"Pretension: The downside of being better than everyone else is that
people tend to assume you're pretentious." (despair.com)
Luke Plant || L.Plant.98 (at) cantab.net || http://lukeplant.me.uk/
Index: django/conf/global_settings.py
===================================================================
--- django/conf/global_settings.py (revision 1436)
+++ django/conf/global_settings.py (working copy)
@@ -174,6 +174,15 @@
# http://www.djangoproject.com/documentation/templates/#now
TIME_FORMAT = 'P'
+# List of processors used by DjangoContext to populate the context.
+# Each one should be a callable that takes the request object as it's
+# only parameter and returns a dictionary to add to the context.
+REQUEST_PROCESSORS = (
+ "django.core.processors.auth",
+ "django.core.processors.debug",
+ "django.core.processors.i18n",
+)
+
##############
# MIDDLEWARE #
##############
Index: django/core/extensions.py
===================================================================
--- django/core/extensions.py (revision 1436)
+++ django/core/extensions.py (working copy)
@@ -2,11 +2,27 @@
# 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, ObjectDoesNotExist, ImproperlyConfigured
from django.core.template import Context, loader
-from django.conf.settings import DEBUG, INTERNAL_IPS
+from django.conf.settings import REQUEST_PROCESSORS
from django.utils.httpwrappers import HttpResponse
+# Load standard request processors
+_standard_request_processors = []
+
+for path in REQUEST_PROCESSORS:
+ i = path.rfind('.')
+ module, attr = path[:i], path[i+1:]
+ try:
+ mod = __import__(module, globals(), locals(), [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)
+ _standard_request_processors.append(func)
+
def render_to_response(*args, **kwargs):
return HttpResponse(loader.render_to_string(*args, **kwargs))
load_and_render = render_to_response # For backwards compatibility.
@@ -25,40 +41,21 @@
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
+ request processors. If keyword argument 'use_standard_processors' is True
+ (default), then processors defined in REQUEST_PROCESSORS will be
+ used. Additional processors can be specified as a list of callables
+ using keyword argument 'processors'
+
"""
- def __init__(self, request, dict=None):
+ def __init__(self, request, dict=None, processors=None,
+ use_standard_processors=True):
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
- 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
+ if use_standard_processors:
+ for processor in _standard_request_processors:
+ self.update(processor(request))
+ if processors is not None:
+ for processor in processors:
+ self.update(processor(request))
+
-# PermWrapper and PermLookupDict proxy the permissions system into objects that
-# the template system can understand.
-
-class PermLookupDict:
- def __init__(self, user, module_name):
- self.user, self.module_name = user, module_name
- def __repr__(self):
- return str(self.user.get_permission_list())
- def __getitem__(self, perm_name):
- return self.user.has_perm("%s.%s" % (self.module_name, perm_name))
- def __nonzero__(self):
- return self.user.has_module_perms(self.module_name)
-
-class PermWrapper:
- def __init__(self, user):
- self.user = user
- def __getitem__(self, module_name):
- return PermLookupDict(self.user, module_name)
Index: django/core/processors.py
===================================================================
--- django/core/processors.py (revision 0)
+++ django/core/processors.py (revision 0)
@@ -0,0 +1,60 @@
+"""
+A set of request processors that return dictionaries to be merged
+into the context for templates. Each function takes
+
+These are referenced from the setting REQUEST_PROCESSORS and used
+in turn by DjangoContext
+"""
+
+from django.conf.settings import DEBUG, INTERNAL_IPS, LANGUAGES, LANGUAGE_CODE
+
+def auth(request):
+ """
+ Returns a dictionary of context variables required by the
+ admin app and other apps that use Django's authentication system.
+ """
+ return {
+ 'user': request.user,
+ 'messages': request.user.get_and_delete_messages(),
+ 'perms': PermWrapper(request.user),
+ }
+
+def debug(request):
+ """
+ Returns a dictionary of context variables required
+ for debugging.
+ """
+ context_extras = {}
+ if DEBUG and request.META.get('REMOTE_ADDR') in INTERNAL_IPS:
+ context_extras['debug'] = True
+ from django.core import db
+ context_extras['sql_queries'] = db.db.queries
+ return context_extras
+
+def i18n(request):
+ context_extras = {}
+ context_extras['LANGUAGES'] = LANGUAGES
+ if hasattr(request, 'LANGUAGE_CODE'):
+ context_extras['LANGUAGE_CODE'] = request.LANGUAGE_CODE
+ else:
+ context_extras['LANGUAGE_CODE'] = LANGUAGE_CODE
+ return context_extras
+
+# PermWrapper and PermLookupDict proxy the permissions system into objects that
+# the template system can understand.
+
+class PermLookupDict:
+ def __init__(self, user, module_name):
+ self.user, self.module_name = user, module_name
+ def __repr__(self):
+ return str(self.user.get_permission_list())
+ def __getitem__(self, perm_name):
+ return self.user.has_perm("%s.%s" % (self.module_name, perm_name))
+ def __nonzero__(self):
+ return self.user.has_module_perms(self.module_name)
+
+class PermWrapper:
+ def __init__(self, user):
+ self.user = user
+ def __getitem__(self, module_name):
+ return PermLookupDict(self.user, module_name)
Index: django/views/generic/date_based.py
===================================================================
--- django/views/generic/date_based.py (revision 1436)
+++ django/views/generic/date_based.py (working copy)
@@ -8,7 +8,7 @@
def archive_index(request, app_label, module_name, date_field, num_latest=15,
template_name=None, template_loader=template_loader,
- extra_lookup_kwargs={}, extra_context={}):
+ extra_lookup_kwargs={}, extra_context={}, processors=None):
"""
Generic top-level archive of date-based objects.
@@ -41,7 +41,7 @@
c = Context(request, {
'date_list' : date_list,
'latest' : latest,
- })
+ }, processors)
for key, value in extra_context.items():
if callable(value):
c[key] = value()
@@ -51,7 +51,7 @@
def archive_year(request, year, app_label, module_name, date_field,
template_name=None, template_loader=template_loader,
- extra_lookup_kwargs={}, extra_context={}):
+ extra_lookup_kwargs={}, extra_context={}, processors=None):
"""
Generic yearly archive view.
@@ -78,7 +78,7 @@
c = Context(request, {
'date_list': date_list,
'year': year,
- })
+ }, processors)
for key, value in extra_context.items():
if callable(value):
c[key] = value()
@@ -88,7 +88,7 @@
def archive_month(request, year, month, app_label, module_name, date_field,
month_format='%b', template_name=None, template_loader=template_loader,
- extra_lookup_kwargs={}, extra_context={}):
+ extra_lookup_kwargs={}, extra_context={}, processors=None):
"""
Generic monthly archive view.
@@ -130,7 +130,7 @@
c = Context(request, {
'object_list': object_list,
'month': date,
- })
+ }, processors)
for key, value in extra_context.items():
if callable(value):
c[key] = value()
@@ -141,7 +141,7 @@
def archive_day(request, year, month, day, app_label, module_name, date_field,
month_format='%b', day_format='%d', template_name=None,
template_loader=template_loader, extra_lookup_kwargs={},
- extra_context={}, allow_empty=False):
+ extra_context={}, allow_empty=False, processors=None):
"""
Generic daily archive view.
@@ -181,7 +181,7 @@
'day': date,
'previous_day': date - datetime.timedelta(days=1),
'next_day': (date < datetime.date.today()) and (date + datetime.timedelta(days=1)) or None,
- })
+ }, processors)
for key, value in extra_context.items():
if callable(value):
c[key] = value()
@@ -205,7 +205,7 @@
month_format='%b', day_format='%d', object_id=None, slug=None,
slug_field=None, template_name=None, template_name_field=None,
template_loader=template_loader, extra_lookup_kwargs={},
- extra_context={}):
+ extra_context={}, processors=None):
"""
Generic detail view from year/month/day/slug or year/month/day/id structure.
@@ -247,7 +247,7 @@
t = template_loader.get_template(template_name)
c = Context(request, {
'object': object,
- })
+ }, processors)
for key, value in extra_context.items():
if callable(value):
c[key] = value()
Index: django/views/generic/list_detail.py
===================================================================
--- django/views/generic/list_detail.py (revision 1436)
+++ django/views/generic/list_detail.py (working copy)
@@ -8,7 +8,7 @@
def object_list(request, app_label, module_name, paginate_by=None, allow_empty=False,
template_name=None, template_loader=template_loader,
- extra_lookup_kwargs={}, extra_context={}):
+ extra_lookup_kwargs={}, extra_context={}, processors=None):
"""
Generic list of objects.
@@ -59,13 +59,13 @@
'previous': page - 1,
'pages': paginator.pages,
'hits' : paginator.hits,
- })
+ }, processors)
else:
object_list = mod.get_list(**lookup_kwargs)
c = Context(request, {
'object_list': object_list,
'is_paginated': False
- })
+ }, processors)
if len(object_list) == 0 and not allow_empty:
raise Http404
for key, value in extra_context.items():
@@ -81,7 +81,7 @@
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=template_loader, extra_lookup_kwargs={},
- extra_context={}):
+ extra_context={}, processors=None):
"""
Generic list of objects.
@@ -112,7 +112,7 @@
t = template_loader.get_template(template_name)
c = Context(request, {
'object': object,
- })
+ }, processors)
for key, value in extra_context.items():
if callable(value):
c[key] = value()
Index: django/views/generic/create_update.py
===================================================================
--- django/views/generic/create_update.py (revision 1436)
+++ django/views/generic/create_update.py (working copy)
@@ -9,7 +9,8 @@
def create_object(request, app_label, module_name, template_name=None,
template_loader=template_loader, extra_context={},
- post_save_redirect=None, login_required=False, follow=None):
+ post_save_redirect=None, login_required=False, follow=None,
+ processors=None):
"""
Generic object-creation function.
@@ -58,7 +59,7 @@
t = template_loader.get_template(template_name)
c = Context(request, {
'form' : form,
- })
+ }, processors)
for key, value in extra_context.items():
if callable(value):
c[key] = value()
@@ -69,7 +70,7 @@
def update_object(request, app_label, module_name, object_id=None, slug=None,
slug_field=None, template_name=None, template_loader=template_loader,
extra_lookup_kwargs={}, extra_context={}, post_save_redirect=None,
- login_required=False, follow=None):
+ login_required=False, follow=None, processors=None):
"""
Generic object-update function.
@@ -130,7 +131,7 @@
c = Context(request, {
'form' : form,
'object' : object,
- })
+ }, processors)
for key, value in extra_context.items():
if callable(value):
c[key] = value()
@@ -143,7 +144,7 @@
def delete_object(request, app_label, module_name, post_delete_redirect,
object_id=None, slug=None, slug_field=None, template_name=None,
template_loader=template_loader, extra_lookup_kwargs={},
- extra_context={}, login_required=False):
+ extra_context={}, login_required=False, processors=None):
"""
Generic object-delete function.
@@ -186,7 +187,7 @@
t = template_loader.get_template(template_name)
c = Context(request, {
'object' : object,
- })
+ }, processors)
for key, value in extra_context.items():
if callable(value):
c[key] = value()