This is round #8 for ideas on how to improve caching mechanisms :)

The way the code works is that it caches all requests to this
ForeignKey in a single dictionary. It then does a lookup on the
primary key (doesn't support multiple pks) and returns it.

This works great when you would need to join on a table w/ 10 rows,
but you don't want to make SQL suck anymore than it already does (we
have a table that adds 200ms to join on 15 rows..). This resembles
what you would do in some situations with sharding.

It currently has one problem, and that's invalidation, the same
problem that always exists. The best solution, would be to have a
CachedModel (not a CachedForeignKey) and that Model would have
different save/delete methods to invalidate data in the cache
immediately, and all requests to that model (whether its a ForeignKey,
ManyToManyField, or whatever) would use the cache. I'm going to look
into a possibly solution for this (beyond the "CacheManager" attempt)
sometime next week

Here's to hoping Google Groups doesn't mess up the code ;)

----

from django.core.cache import cache
from django.db.models.fields import Field
from django.db.models.fields.related import ForeignKey,
ReverseSingleRelatedObjectDescriptor

CACHE_TIME = 60*60

class
CachedReverseSingleRelatedObjectDescriptor(ReverseSingleRelatedObjectDescriptor):
    # This class provides the functionality that makes the related-
object
    # managers available as attributes on a model class, for fields
that have
    # a single "remote" value, on the class that defines the related
field.
    # In the example "choice.poll", the poll attribute is a
    # ReverseSingleRelatedObjectDescriptor instance.
    def __get__(self, instance, instance_type=None):
        if instance is None:
            raise AttributeError, "%s must be accessed via instance" %
self.field.name
        cache_name = self.field.get_cache_name()
        try:
            return getattr(instance, cache_name)
        except AttributeError:
            val = getattr(instance, self.field.attname)
            if val is None:
                # If NULL is an allowed value, return it.
                if self.field.null:
                    return None
                raise self.field.rel.to.DoesNotExist
            rel_obj = self.field.rel.to.get_cached_instance(val)
            if rel_obj is None:
                cache_name = 'fk_%s_%s' %
(self.field.rel.to._meta.module_name,
self.field.rel.to._meta.app_label)
                objs = cache.get(cache_name)
                if objs is None:
                    objs = dict((f.id, f) for f in
self.field.rel.to._default_manager.all())
                    cache.set(cache_name, objs, CACHE_TIME)
                try:
                    rel_obj = objs[val]
                except KeyError:
                    raise self.field.rel.to.DoesNotExist
            setattr(instance, cache_name, rel_obj)
            return rel_obj

class CachedForeignKey(ForeignKey):
    def contribute_to_class(self, cls, name):
        super(CachedForeignKey, self).contribute_to_class(cls, name)
        setattr(cls, self.name,
CachedReverseSingleRelatedObjectDescriptor(self))


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Django developers" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/django-developers?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to