Below I've grouped together some snippets I've made use of recently.

Problem: you want to limit posts to a view

This can be accomplished with a view decorator that stores hits by IP in memcached, incrementing the cached value and returning 403's when the cached value exceeds a certain threshold for a given IP.

from django.utils.cache import cache
from django.http import HttpResponseForbidden

def throttle_post(func, limit=3, duration=900):
    def inner(request, *args, **kwargs):
        if request.method == 'POST':
            remote_addr = request.META.get('HTTP_X_FORWARDED_FOR') or \
                          request.META.get('REMOTE_ADDR')
            if cache.get(remote_addr) == limit:
                return HttpResponseForbidden('Try slowing down a little.')
            elif not cache.get(remote_addr):
                cache.set(remote_addr, 1, duration)
            else:
                cache.incr(remote_addr)
        return func(request, *args, **kwargs)
    return inner

Problem: you want to get the latest model instances into a template easily

Writing templatetags is obnoxious. Say you have a small blurb on all your pages that shows the latest 5 comments posted to the site -- using this filter, you could write the following:

{% for comment in "comments.comment"|latest:5 %}...display comment here...{% endfor %}

from django.db.models.loading import get_model
from django.db.models.query import QuerySet
from django.db.models.fields import DateTimeField, DateField

register = template.Library()

@register.filter
def latest(model_or_obj, num=5):
    # load up the model if we were given a string
    if isinstance(model_or_obj, basestring):
        model_or_obj = get_model(*model_or_obj.split('.'))

    # figure out the manager to query
    if isinstance(model_or_obj, QuerySet):
        manager = model_or_obj
        model_or_obj = model_or_obj.model
    else:
        manager = model_or_obj._default_manager

    # get a field to order by, defaulting to the primary key
    field_name = model_or_obj._meta.pk.name
    for field in model_or_obj._meta.fields:
        if isinstance(field, (DateTimeField, DateField)):
            field_name = field.name
            break
    return manager.all().order_by('-%s' % field_name)[:num]

Problem: you have a template filter you'd like to cache

Say you'd like to cache one of your template filters. This decorator acts sort of like memoize, caching a result set based on the arguments passed in (which are used as the cache key).

from django.conf import settings
from django.db.models.query import QuerySet
from django.utils.cache import cache
from django.utils.hashcompat import md5_constructor

def stringify_object(obj):
    if isinstance(obj, QuerySet):
        return obj.query.as_sql()
    return unicode(obj)

def key_from_args(*args, **kwargs):
    strings = []
    for arg in args:
        strings.append(stringify_object(arg))
    for k,v in kwargs.items():
        strings.append('%s=%s' % (k, stringify_object(v)))
    return md5_constructor(''.join(strings).hexdigest())

def cached_filter(func, timeout=300):
    def inner(*args, **kwargs):
        if settings.DEBUG:
            return func(*args, **kwargs)
        cache_key = key_from_args(*args, **kwargs)
        result = cache.get(cache_key)
        if not result:
            result = func(*args, **kwargs)
            cache.set(cache_key, result, timeout)
        return result
    inner._decorated_function = func
    return inner

Comments (4)

bayo opadeyi

Nice, thanks for sharing. I am not clear why you would like to cache a template filter though?

April 27, 2010 at 1:58 a.m. ( )

David, biologeek

You should share your useful snippets on djangosnippets :)

April 27, 2010 at 4:31 a.m. ( )

providenz

+1 for djangosnippets It would be easier to find them

May 7, 2010 at 2:33 a.m. ( )

Charles Leifer

Yo, I was having trouble registering with djangosnippets but it turns out the message was just getting sent to my spam folder. Will post there -- thanks for the tip.

May 8, 2010 at 7:49 p.m. ( )

Commenting has been disabled for this entry