def get(self, *args, **kwargs): if len(kwargs) == 1 and kwargs.keys()[0] in ('id', 'id__exact', 'pk', 'pk__exact'): # Generate the cache key directly, since we have the id/pk. pk_key = model_cache_key(self.model, kwargs.values()[0]) lookup_key = None else: # This lookup is not simply an id/pk lookup. # Get the cache key for this lookup. # Handle related managers, which automatically use core_filters # to filter querysets using the related object's ID. core_filters = getattr(self, 'core_filters', None) if isinstance(core_filters, dict): # Combine the core filters and the kwargs because that is # basically what the related manager will do when building # the queryset. lookup_kwargs = dict(core_filters) lookup_kwargs.update(kwargs) lookup_key = lookup_cache_key(self.model, **lookup_kwargs) else: lookup_key = lookup_cache_key(self.model, **kwargs) # Try to get the cached pk_key. object_pk = self.cache_backend.get(lookup_key) pk_key = object_pk and model_cache_key(self.model, object_pk) # Try to get a cached result if the pk_key is known. result = pk_key and self.cache_backend.get(pk_key) if not result: # The result was not cached, so get it from the database. result = super(RowCacheManager, self).get(*args, **kwargs) object_pk = result.pk # And cache the result against the pk_key for next time. pk_key = model_cache_key(result, object_pk) self.cache_backend[pk_key] = result # If a lookup was used, then cache the pk against it. Next time # the same lookup is requested, it will find the relevent pk and # be able to get the cached object using that. if lookup_key: self.cache_backend[lookup_key] = object_pk # Return the cache-protected object. return result
def test_that_it_works(self): """ Check that this whole thing works. It's a bit complicated. """ gallery = PhotoGallery.objects.all().filter(sites__isnull=False)[0] self.assertTrue(isinstance(gallery, ModelWithCaching), 'This test needs to test a cached model.') for gallery in PhotoGallery.objects.filter(slug=gallery.slug).exclude(pk=gallery.pk): gallery.delete() pk_key = model_cache_key(gallery) lookup_key = lookup_cache_key(PhotoGallery, slug=gallery.slug) # Accessing with a pk should add a cached version of the object # to the cache without any fuss. del lazymodel_cache[pk_key] self.assertEqual(PhotoGallery.objects.get(pk=gallery.pk), gallery) self.assertEqual(lazymodel_cache[pk_key], gallery) del lazymodel_cache[pk_key] self.assertEqual(LazyModel(PhotoGallery, gallery.pk), gallery) self.assertEqual(lazymodel_cache[pk_key], gallery) # Accessing via ORM lookups, such as using the slug, should cache a # lookup_key that is only a reference to the object pk. The cache key # using that object pk should then contain the cached object as usual. del lazymodel_cache[lookup_key] del lazymodel_cache[pk_key] self.assertEqual(PhotoGallery.objects.get(slug=gallery.slug), gallery) self.assertEqual(lazymodel_cache[lookup_key], gallery.pk) self.assertEqual(lazymodel_cache[pk_key], gallery) # Removing a site from the gallery should trigger the m2m signals that # delete the cached object. gallery.sites.remove(gallery.sites.all()[0]) self.assertUncached(pk_key, 'M2M changes did not delete the cached value!') # The lookup key will still be there. self.assertEqual(lazymodel_cache[lookup_key], gallery.pk) # Saving the object should also delete the cached object. # First access the object to add it back to the cache. self.assertEqual(PhotoGallery.objects.get(pk=gallery.pk), gallery) self.assertEqual(lazymodel_cache[pk_key], gallery) gallery.save() self.assertUncached(pk_key, 'Saving did not delete the cached value!') # Deleting the object should also delete from the cache. self.assertEqual(PhotoGallery.objects.get(pk=gallery.pk), gallery) self.assertEqual(lazymodel_cache[pk_key], gallery) gallery.delete() self.assertUncached(pk_key, 'Deleting did not delete the cached value!')