
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)
Commenting has been disabled for this entry
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. ( permalink )
David, biologeek
You should share your useful snippets on djangosnippets :)
April 27, 2010 at 4:31 a.m. ( permalink )
providenz
+1 for djangosnippets It would be easier to find them
May 7, 2010 at 2:33 a.m. ( permalink )
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. ( permalink )