Django:将计算应用于查询集

时间:2010-05-09 21:14:04

标签: python django algorithm sorting

我有一个QuerySet,我希望将其传递给分页的通用视图:

links = Link.objects.annotate(votes=Count('vote')).order_by('-created')[:300]

这是我的“热门”页面,其中列出了我最近的300份提交内容(每页30个链接10页)。我现在想用HackerNews使用的算法对这个QuerySet进行排序:

(p - 1) / (t + 2)^1.5
p = votes minus submitter's initial vote
t = age of submission in hours

现在因为在整个数据库上应用这个算法会非常昂贵,我只满足于最后300个提交。我的网站不太可能是下一个digg / reddit,所以虽然可扩展性是一个加号,但它是必需的。

现在我的问题是如何迭代我的QuerySet并按上述算法对其进行排序?

有关详细信息,请参阅以下适用型号:

class Link(models.Model):
    category = models.ForeignKey(Category, blank=False, default=1)
    user = models.ForeignKey(User)
    created = models.DateTimeField(auto_now_add=True)
    modified = models.DateTimeField(auto_now=True)
    url = models.URLField(max_length=1024, unique=True, verify_exists=True)
    name = models.CharField(max_length=512)

    def __unicode__(self):
        return u'%s (%s)' % (self.name, self.url)

class Vote(models.Model):
    link = models.ForeignKey(Link)
    user = models.ForeignKey(User)
    created = models.DateTimeField(auto_now_add=True)

    def __unicode__(self):
        return u'%s vote for %s' % (self.user, self.link)

注意:

  1. 我没有“downvotes”所以只是投票行的存在是特定用户投票或特定链接的指示。
  2. 修改

    我认为我过度复杂化并发现了一小段代码:

    links = Link.objects.annotate(votes=Count('vote')).order_by('-created')[:300]
    for link in links:
        link.popularity = ((link.votes - 1) / (2 + 2)**1.5)
    

    但是对于我的生活,我无法将其翻译成我的模板:

    {% for link in object_list %}
        Popularity: {{ link.popularity }}
    {% endfor %}
    

    为什么不出现?我知道人气很有效,因为:

    print 'LinkID: %s - Votes: %s - Popularity: %s' % (link.id, link.votes, link.popularity)
    

    返回我在控制台中所期望的内容。

3 个答案:

答案 0 :(得分:2)

如果可能,您可以从QuerySet中创建值dict或值列表,并将排序算法应用于获得的dict(列表)。 参见

http://docs.djangoproject.com/en/dev/ref/models/querysets/#values-fields

http://docs.djangoproject.com/en/dev/ref/models/querysets/#values-list-fields

示例

# select links
links = Link.objects.annotate(votes=Count('vote')).order_by('-created')[:300]
# make a values list:
links = links.values_list('id', 'votes', 'created')
# now sort 
# TODO: you need to properly format your created date (x[2]) here
list(links).sort(key = lambda x: (x[1] - 1) / (x[2] + 2)^1.5)

答案 1 :(得分:1)

qs = [obj1, obj2, obj3] # queryset
s = [] # will hold the sorted items
for obj in qs:
    s.append(((obj.votes-1)/pow((obj.submision_age+2), 1.5), obj))
s.sort()
s.reverse()

s应该从最高计算的重要性排在最后的最低位置,并且看起来像:

[(calculated importancy, obj), (calculated importancy, obj), ...]

答案 2 :(得分:0)

虽然无法通过QuerySet进行计算,但我必须转换为排序列表

links = Link.objects.select_related().annotate(votes=Count('vote'))
for link in links:
    delta_in_hours = (int(datetime.now().strftime("%s")) - int(link.created.strftime("%s"))) / 3600
    link.popularity = ((link.votes - 1) / (delta_in_hours + 2)**1.5)

links = sorted(links, key=lambda x: x.popularity, reverse=True)

不是最佳但是有效。我不能使用我可爱的object_list通用视图,它会自动分页并且必须手动操作但是对于有工作视图这是一个公平的妥协......