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 22, 2024Technology
Python and the Point Rush in DeFi

This article demonstrates how to use Python to automate yield calculations in decentralized finance (DeFi), focusing on the Renzo and Pendle platforms. It guides readers through estimating potential rewards based on factors like token prices, liquidity, and reward distribution rules, emphasizing the importance of regular data updates and informed decision-making in DeFi investments.

Jun 8, 2022Technology
How to Use MongoDB in Python: Gearheart`s Experience

In this article, we have prepared a quick tutorial on how to use MongoDB in Python and listed top ORM.

Jun 1, 2018Technology
Site search organization: basic concepts

Now it's time to get acquainted with Elasticsearch. This NoSQL database is used to store logs, analyze information and - most importantly - search.

Mar 12, 2017Technology
Creating a chat with Django Channels

Nowadays, when every second large company has developed its own instant messenger, in the era of iMessages, Slack, Hipchat, Messager, Google Allo, Zulip and others, I will tell you how to keep up with the trend and write your own chat, using django-channels 0.17.3, django 1.10.x, python 3.5.x.

Oct 3, 2016Technology
How to include JQuery plugins in Angular 2 running via webpack

Learn more about how to include jquery plugins in angular 2 running via webpack. Our tutorial is perfect for Angular beginners.

Aug 8, 2016TechnologyBusiness
How To Add HTML5 Geolocation To Your Web App?

In this article I will describe how to integrate geolocation HTML5 function to a web app so you can then easily implement it in your apps or websites. As an example we are going to create small web app which will be able to calculate the shortest route between detected user’s location and predefined destination using Google Maps API.