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
Dec 13, 2022Technology
How to create a timelapse video from frames

We’ll tell you how to create a video timelapse from a sequence of snapshots and provide customers with video playlists optimized for browser playback.

May 26, 2017Technology
Tutorial: Django User Registration and Authentication

In this beginners friends article I'll explain how to make authentication with Google account on your Django site and how to make authentication for you REST API.

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.

Dec 1, 2016Technology
How to Use Django & PostgreSQL for Full Text Search

For any project there may be a need to use a database full-text search. We expect high speed and relevant results from this search. When we face such problem, we usually think about Solr, ElasticSearch, Sphinx, AWS CloudSearch, etc. But in this article we will talk about PostgreSQL. Starting from version 8.3, a full-text search support in PostgreSQL is available. Let's look at how it is implemented in the DBMS itself.

Sep 22, 2016Technology
Angular Form Validation

In this article, we will describe some useful scripts and directives we use with angular form validation in our projects.

Feb 18, 2010Technology
User profiles with inheritance in Django

Usually users' profiles are stored in single model. When there are multiple user types, separation is made by some field like user_type.Situation is a little more complicated when different data is needed for each user type.In this article I'll describe how I solve this task.