Back
Jun 25, 2011

Ajax blocks in Django

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

Subscribe for the news and updates

More thoughts
Aug 27, 2024Technology
An Effective Preparation Algorithm for ISTQB Certification

This article offers key insights into the ISTQB certification and shares a proven preparation strategy to help candidates succeed.

Dec 8, 2022Technology
How to create a route finder on an SVG map

In one of our recent projects, we had an interesting case: the whole application was built around an interactive map for a fairly large shopping mall, and the main goal of the system was to plot the closest route to a user-selected destination.

Jun 27, 2018Technology
How to Work With Legacy Code: Code Refactoring Techniques

In this article we'll review general approach to working with the best kind of projects - the ones with old untested and undocumented spaghetti code and a tight schedule. We'll review anger management techniques, coping mechanisms and some refactoring tips that might come in handy.

May 9, 2018Technology
How to Generate PDF Files in Python with Xhtml2pdf, WeasyPrint or Unoconv

Programmatic generation of PDF files is a frequent task when developing applications that can export reports, bills, or questionnaires. In this article, we will consider three common tools for creating PDFs, including their installation and converting principles.

Feb 28, 2017Technology
Passing pieces of markup to components in Angular 2 and problems with dynamic content

In this article we'll research how to pass custom markup to Angular components and how to create different types of dynamic components.

Nov 21, 2016Technology
Crawling FTP server with Scrapy

Welcome all who are reading this article. I was given a task of creating a parser (spider) with the Scrapy library and parsing FTP server with data. The parser had to find lists of files on the server and handle each file separately depending on the requirement to the parser.