Quite often we have to write paginated or filtered blocks of information on page. For example, list of similar houses. It's rendered on server first, and later pagination is done with ajax.
In order to make, I used following setup:
So in order to edit one such block, we have to edit three files (apart from template) - tag, view, url.
I got annoyed by this and decided to create a decorator that would automate this process.
With this decorator, you just have to write one function that creates context. Tag and view are generated and registered automatically. Url params like ?obj_id=1
are declared in decorator and are processed automatically.
'''
This module introduces viewtag concept:
funcions that act both as template tags and as views.
Example usage:
First in urls (once):
url(r'^viewtag/([-\w]+)/$', 'viewtags.view', name='viewtag'),
and
viewtags.autodiscover()
Then in app/viewtags.py:
from placeforpeople.viewtags import viewtag, model_arg
@viewtag('viewtag_test.html', [model_arg('obj', Object)]) # you can use querysets too
def test_viewtag(request, obj, page=0):
return {
'obj': obj,
'page': page,
}
Later in templates:
{% vt:test_viewtag campaign %}
or
$.get('{% url viewtag "test_viewtag" %}', {page: 2, object: 5}, function(data) {
$('body').append(data);
});
'''
import inspect
from django import template
from django.template.loader import get_template
from django.http import Http404
from django.shortcuts import render_to_response
from django.utils.importlib import import_module
from django.conf import settings
register = template.Library()
template.builtins.append(register)
_viewtags_registry = {}
def viewtag(template, args=[]):
def decorator(func):
_register_tag(func, template)
_viewtags_registry[func.__name__] = (func, template, args)
return func
return decorator
def view(request, viewtag_name):
try:
func, template_, arg_processors = _viewtags_registry[viewtag_name]
except KeyError:
raise Http404()
argnames = inspect.getargspec(func).args
kwargs = {}
for argname in argnames:
if argname in request.REQUEST and argname != 'request':
kwargs[argname] = request.REQUEST[argname]
for proc in arg_processors:
kwargs = proc(request, kwargs)
return render_to_response(template_, func(request, **kwargs), template.RequestContext(request))
def model_arg(key, qs_or_model):
from django.db import models
if isinstance(qs_or_model, models.base.ModelBase):
qs = qs_or_model._default_manager.all()
else:
qs = qs_or_model
def processor(request, kwargs):
id = request.REQUEST.get(key) or request.REQUEST.get(key + '_id')
if not id is None:
kwargs[key] = qs.get(id=id)
return kwargs
return processor
def autodiscover():
for app in settings.INSTALLED_APPS:
try:
import_module('%s.viewtags' % app)
except ImportError:
pass
def _register_tag(func, template):
def tag(parser, token):
parts = token.split_contents()
args, kwargs = [], []
for part in parts[1:]:
if '=' in part:
kwargs.append(part.split('=', 1))
else:
args.append(part)
return TempNode(func, template, args, dict(kwargs))
register.tag('vt:' + func.__name__, tag)
class TempNode(template.Node):
def __init__(self, func, template_, args, kwargs):
self.func = func
self.template = get_template(template_)
self.args = [template.Variable(arg) for arg in args]
self.kwargs = dict([(k, template.Variable(v)) for (k,v) in kwargs.items()])
def render(self, context):
args = [context['request']] + [arg.resolve(context) for arg in self.args]
kwargs = dict([
(key, val.resolve(context)) for (key, val) in self.kwargs.items()
])
context.update(self.func(*args, **kwargs))
res = self.template.render(context)
context.pop()
return res