Back
Jun 25, 2011

Ajax blocks in Django

Vladimir Sidorenko
Vladimir Sidorenko

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:

  • inclusion tag, that generates context for tag
  • view, that parses request params and them launches the same tag to create context

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

More thoughts

Jan 22, 2017Technology
Django vs Rails Performance

This article is aimed for beginners, who are trying to choose between Ruby on Rails and Django. Let’s see which is fastest and why.

Vladimir Sidorenko
Vladimir Sidorenko
Aug 27, 2020Technology
5 tips for designing database architecture

Designing database architecture is a challenging task, and it gets even more difficult when your app keeps getting bigger. Here are several tips on how to manage your data structure in a more efficient way.

Yurii Mironov
Yurii Mironov
Feb 12, 2020Technology
5 Best Payment Gateways For 2020

We reviewed the best payment gateways in 2020. Here’s our comparison of their features, advantages, and disadvantages.

Vladimir Sidorenko
Vladimir Sidorenko
Feb 18, 2010Technology
Absolute urls in models

Everybody knows about permalink, but it's usually used only in get_absolute_url. I prefer to use it for all related model urls.class Event(models.Model):# [email protected] edit_url(self):return ('event_edit', (self.pk, ))And then in template:<a href="{{ event.edit_url }}">Редактировать событие</a>

Vladimir Sidorenko
Vladimir Sidorenko
Mar 4, 2011Technology
Css sprite generation

I've created this small sprite to create css sprites. It glues images from directory directory into single file and generates corresponding css.

Vladimir Sidorenko
Vladimir Sidorenko
Aug 31, 2016Technology
Angular vs React Comparison

In this article, we will compare two most popular JS Libraries (Angular vs React). Both of them were created by professionals and have been used in famous big projects.

Vladimir Sidorenko
Vladimir Sidorenko