在Django中用一般关系注释

问题描述:

我正在使用django-hitcount来达到我的数据库对象。我想通过对象计算命中数来确定哪个对象在给定时间范围内命中最多。该应用程序有兴趣在这里两种型号:在Django中用一般关系注释

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命中 计数次数(时间范围内上述过滤) 秩序content_object和计数

这可能非常昂贵,所以我计划每天进行一次调取/缓存。

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

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

我立刻碰到一个问题:

无法解析关键字 'hitcount__content_object' 到现场。选择是:创建,hitcount,id,ip,会话,用户,user_agent

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

所以我的问题:什么是取得同样结果的替代方法?如何可以按对象分组,但也保留名称?

我确实有model(content_type)和id(object_pk),所以我总是可以单独拉这些,但看起来不够雅致。 。 。

+0

现在没有可能尝试这种方式,但是您是否尝试过'limited_hc = Hit.objects.all()。values('hitcount__content_type','hitcount__object_pk')。annotate(count = Count('hitcount__object_pk'))'? – 2010-11-19 11:57:32

+0

这有效,但我没有得到实际对象的链接。所以我不能得到名字,只有PK。然后,我必须再次ping数据库以从相应表中提取名称数据。 – 2010-11-19 16:41:12

它可能会更有效地为你的目的的通用关系添加到Hit型号:

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

,然后运行计数()查询上直接命中:

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迁移系统修改命中数模型。你也可以在Monkey修补它的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') 
       ) 

应该返回计数VS内容类型ID,相当接近你想要什么的字典,。

+0

我使用此解决方案。我不确定它给了我什么,但我想要的。而不是一个对象的命中数(给​​定一个时间范围,content_type和object_id),我想要所有对象的命中数(给​​定一个时间范围和content_type),以便我可以对它们进行排序。看来,即使按照您描述的方式重构models.py,我也不能同时使用annotate和content_object。 – 2010-11-19 18:14:25

+0

更新了我的帖子。好吧,我不会在这种情况下使用注释,因为你的查询会做太多的工作,而且我认为对于大集合来说,它很慢。海事组织在这种情况下计算懒惰的方法可能会更好。 – Evgeny 2010-11-19 18:35:45

+0

谢谢你的跟进。这仍然不能完成我的目标。有了这个,我仍然得到一个数字。我希望有一个可迭代的列表。假设我们在最后一块代码中忽略了.count(),我们会得到5个对象:一个是A类型的3个,另一个是B类型的2个。我想让查询给我2个对象,分别计数:A型(计数= 3)和B型(计数= 2)。不仅仅是5(这是我从上面的代码中得到的)。 – 2010-11-19 20:53:22