django-cached-field

Cached fields for Django
Download

django-cached-field Ranking & Summary

Advertisement

  • Rating:
  • License:
  • BSD License
  • Price:
  • FREE
  • Publisher Name:
  • Martin Chase
  • Publisher web site:
  • http://github.com/outofculture

django-cached-field Tags


django-cached-field Description

django-cached-field is a Django app that provides Celery-deferred, cached fields on Django ORM for expensive-to-calculate dataUsing Django ORM and Celery, keep expensive-to-calculate attributes up-to-date.ExampleSay you have a slow method on one of your models:class Lamppost(models.Model): @property def slow_full_name(self): sleep(30) return 'The %s %s of %s' % (self.weight, self.first_name, self.country)Ugh; too slow. Let's cache that. We'll want a few tools. Celery < http://celeryproject.org/ > with django-celery < http://github.com/ask/django-celery > will need to be set up and humming along smoothly. Then we'll add in our cached field, inherit from the model mixin and rename our method appropriately:from django_cached_field import CachedIntegerField, ModelWithCachedFieldsclass Lamppost(models.Model, ModelWithCachedFields): slow_full_name = CachedTextField(null=True) def calculate_slow_full_name(self): sleep(30) return 'The %s %s of %s' % (self.weight, self.first_name, self.country)(Yeah, calculate_ is just a convention. I clearly haven't given up the rails ghost...)Next, migrate your db schema to include the new cached field using south, or roll your own. Note that two fields will be added to this table, cached_slow_full_name of type text and slow_full_name_recalculation_needed of type boolean, probably defaulting to true.Already that's kinda better. lamppost.slow_full_name may take 30 seconds the first time it gets called for a given record, but from then on, it'll be nigh instant. Of course, at this point, it will never change after that first call.The remaining important piece of the puzzle is to invalidate our cache. Thoses constituent fields are probably changed in some views.py (this could be more clever about noticing if the relevant values are updated):@render_to("lamppost/edit.html")def edit(request, lamppost_id): lamppost = Lamppost.objects.get(pk=lamppost_id) if request.METHOD == 'POST': form = LamppostForm(request.POST) if form.is_valid(): form.save() form.instance.flag_slow_full_name_as_stale() else: form = LamppostForm() return {'form': form, 'lamppost': lamppost}This is the hardest part as the developer! Caching requires you hunt down every place the value could be changed and calling that flag_slow_full_name_as_stale method. Is country assigned a random new value every morning at cron'o'clock? That flag had best be stale by cron'o'one. Do you calculate weight based on the sum of all associated pigeons? Hook into the pigeons landing. And takeoff. And everything that changes an individual pigeon's weight. As Abraham Lincoln said, "There are only two hard problems in programming: naming, cache invalidation and off-by-one errors."InstallationYou can make things easy on yourself:pip install django-cached-fieldOr, for a manual installation, you can clone the repo and install it using python and setup.py:git clone git://github.com/aquameta/django-cached-field.gitcd django-cached-field/python setup.py installTested with django 1.3.1, celery 2.3.1, django-celery 2.3.3.ConfigurationTwo settings changes are pretty much required for things to work: make sure it's a registered app, make sure celery sees its tasks file:INSTALLED_APPS += CELERY_IMPORTS += One change is optional: whether recalculation should happen when flagged as stale (default) or be left to the next time the attribute is accessed. This is useful for testing environments where you don't care that your cached values are invalid. Note that in this situation, you wouldn't need celery.CACHED_FIELD_EAGER_RECALCULATION = True # or False for testing environmentsThis is a global option, so individual exceptions should instead be handled by passing the and_recalculate argument to the flag_FIELD_as_stale call.Caveats- Race condition if you flag a field as stale in a db transaction that takes longer to complete than the celery job takes to be called.- All ORM methods (e.g. order_by, filter) need to use cached_FIELD.- recalculate_FIELD uses .update(cached_FIELD= to set the value.- flag_FIELD_as_stale uses .update, as well.Product's homepage


django-cached-field Related Software