class Post models Model comment_count AggregateCacheField CommentNode

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
class Post(models.Model):
...
comment_count = AggregateCacheField(CommentNode, generic=True, filter_cond={'approved': True})
pingback_count = AggregateCacheField(Pingback, generic=True)
class AggregateCacheField(IntegerField):
"""
Field, which will contribute additional column to the model, containing
aggregated value of related model.
"""
def __init__(self, source, rel_name=None, filter_cond=None, generic=False,
ct_field='content_type', fk_field='object_id', *args, **kwargs):
if isinstance(source, basestring):
app_label, model_name = source.split(".")
self.source = get_model(app_label, model_name, False)
else:
self.source = source
self.filter_cond = filter_cond
self.generic = generic
if generic:
self.ct_field = ct_field
self.fk_field = fk_field
else:
self.rel_name = rel_name or source._meta.object_name.lower()
kwargs.update({
'editable': False,
'default': 0,
'blank': True,
})
super(AggregateCacheField, self).__init__(*args, **kwargs)
def contribute_to_class(self, cls, name):
super(AggregateCacheField, self).contribute_to_class(cls, name)
self.cls = cls
self.name = name
dispatcher.connect(self.perform, sender=self.source, signal=signals.post_save)
dispatcher.connect(self.perform, sender=self.source, signal=signals.post_delete)
def perform(self, instance):
if generic:
ctype = ContentType.objects.get_for_model(self.cls)
cond = {
self.ct_field: ctype,
self.fk_field: getattr(instance, self.fk_field),
}
dest = ctype.get_object_for_this_type(pk=getattr(instance, self.fk_field))
else:
dest = getattr(instance, self.rel_name)
cond = {self.rel_name: dest}
qs = self.source._default_manager
if self.filter_cond:
qs = qs.filter(**self.filter_cond)
setattr(dest, self.name, qs.filter(**cond).count())
dest.save()