Skip to content

Instantly share code, notes, and snippets.

@minism
Last active December 10, 2015 14:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save minism/4448466 to your computer and use it in GitHub Desktop.
Save minism/4448466 to your computer and use it in GitHub Desktop.
Cached method by related model save
from django.db import models
from django.db.models.signals import post_save
from django.db.models.loading import get_model
from cachemodel.decorators import cached_method
def model2label(obj):
return ("%s.%s" % (obj._meta.app_label, obj._meta.object_name))
class CachePublishRegistry(object):
"""
Tracks dependencies between models/fields for post_save logic.
Register a dependency between methods and models with register_relationship()
When `model` is saved, we call `model`.`field`.publish_method(`method_to_publish`)
"""
def __init__(self):
self._registry = {}
post_save.connect(self.publish_relationships)
def publish_relationships(self, sender, instance, created, **kwargs):
model_label = model2label(sender)
if model_label in self._registry:
for reverse_field, method_to_publish, created_only in self._registry[model_label]:
if created_only and not created:
continue
for arg in reverse_field.split('.'):
instance = getattr(instance, arg)
reverse_object = instance
reverse_object.publish_method(method_to_publish)
print "Publishing %s.%s.%s" % (model_label, reverse_field, method_to_publish)
def register_relationship(self, model_label, reverse_field, method_to_publish, created_only=False):
if not isinstance(model_label, basestring) and issubclass(model_label, models.Model):
model_label = model2label(model_label)
self._registry[model_label] = self._registry.get(model_label, [])
self._registry[model_label].append((reverse_field, method_to_publish, created_only))
registry = CachePublishRegistry()
def cached_by_relationship(related_model, reverse_field, created_only=False):
def decorator(target):
# Use the original cached_method decorator but disable auto publish
wrapper = cached_method(auto_publish=False)(target)
# Register the relationship to generate on save logic
registry.register_relationship(related_model, reverse_field, target.__name__, created_only=created_only)
return wrapper
return decorator
### Usage: blog/models.py
class Post(models.Model):
body = models.CharField()
@cached_by_relationship('blog.Comment', 'post', created_only=True)
def comment_count():
return self.comment_set.count()
class Comment(models.Model):
body = models.CharField()
post = models.ForeignKey(post)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment