用一般关系在Django中注释

时间:2010-11-19 03:55:41

标签: django annotations count generic-relations

我使用django-hitcount来计算我的数据库对象。我想按对象计算命中数,以确定哪个对象在给定时间范围内的命中率最高。该应用程序有两个感兴趣的模型:

class Hit(models.Model):
    created         = models.DateTimeField(editable=False)
    ip              = models.CharField(max_length=40, editable=False)
    session         = models.CharField(max_length=40, editable=False)
    user_agent      = models.CharField(max_length=255, editable=False)
    user            = models.ForeignKey(User,null=True, editable=False)
    hitcount        = models.ForeignKey(HitCount, editable=False)

class HitCount(models.Model):
    hits            = models.PositiveIntegerField(default=0)
    modified        = models.DateTimeField(default=datetime.datetime.utcnow)
    content_type    = models.ForeignKey(ContentType,
                        verbose_name="content cype",
                        related_name="content_type_set_for_%(class)s",)
    object_pk       = models.TextField('object ID')
    content_object  = generic.GenericForeignKey('content_type', 'object_pk')

“点击”使用时间戳记录每个匹配,而HitCount存储总点击次数。要按对象在一个时间范围内获得命中,我需要执行以下操作:

按创建日期过滤命中 计算每个content_object的点击次数(在上面过滤的时间范围内) 按上面计算的计数顺序 return content_object and count

这可能非常昂贵,所以我计划每天进行一次calcing / caching。

作为第一步,我想计算每个content_object的点击次数,而不考虑时间范围。

limited_hc = Hit.objects.all().values('hitcount__content_object').annotate(count = Count('hitcount__object_pk'))

我立即遇到了一个问题:

无法将关键字'hitcount__content_object'解析为字段。选项包括:created,hitcount,id,ip,session,user,user_agent

经过一番挖掘后,我发现annotation and generic relations do not work well together。如果我使用object_pk而不是content_object,它可以正常工作,但是我没有对象的名称。

所以我的问题:获得相同结果的替代方案是什么?如何按对象分组但也保留名称?

我有模型(content_type)和id(object_pk),所以我总是可以单独拉出这些,但这看起来不够优雅。 。 。

1 个答案:

答案 0 :(得分:1)

为了您的目的,向Hit模型添加通用关系可能会更有效:

class Hit(models.Model):
    ...
    object_id = models.PositiveIntegerField()
    content_type = models.ForeignKey(ContentType)
    content_object = generic.GenericForeignKey('content_type', 'object_id')

然后直接在Hit上运行count()查询:

t = ContentType.objects.get_for_model(the_object_being_hit)
id = the_object_being_hit.id
count = Hit.objects.filter(
                   created__range=(from_timestamp, to_timestamp),
                   content_type = t,
                   object_id = id
                 ).count()

您可以使用Django South迁移系统来修改命中计数的模型。您也可以尝试在修补它的Meta类之后对Hit进行子类化,或者只是定义更适合您需求的模型。

编辑如果您想计算整个类对象或多个类的匹配数,那么您可以拥有:

count = Hit.objects.filter(
                     created__range = myrange,
                     content_type__in = set_of_types
                   ).count()

set_of_types可以是使用get_for_model调用构建的列表,也可以是通过直接过滤ContentType表获得的查询集。

count()方法的一个好处是它使计数发生在数据库中,这要快得多。

要按content_type获取细分,请尝试以下操作:

counts = Hit.objects.filter(
                   created__range = myrange
                ).values(
                   'content_type'
                ).annotate(
                   Count('content_type')
                )

这应该返回一个计数与内容类型id的字典,非常接近你想要的。