Django 按最新评分列出所有作者

3 投票
3 回答
502 浏览
提问于 2025-04-16 23:05

根据Django文档中的简化模型,我想要得到一个作者列表,这个列表是按照他们最近一次发布的作品的评分来分组的,或者是某个过去日期之前的最近作品。

 class Author(models.Model):
     name = models.CharField(max_length=50)
     email = models.EmailField()

 class Entry(models.Model):
     headline = models.CharField(max_length=255)
     pub_date = models.DateTimeField()
     mod_date = models.DateTimeField()
     authors = models.ForeignKey(Author)
     rating = models.IntegerField()

最后,我希望把这个结果变成一个Python字典,像这样:{1星:(作者1, 作者2), 2星:(作者3, 作者4, 作者5)...}。

我想到的一个办法是先获取所有的作品,然后用itertools.groupby来处理这个大数据集。有没有人能推荐一个更简单的方法呢?

3 个回答

1

其实你可以完全在模板里做到这一点。像这样应该就可以了:

**Views.py**
authors = Author.objects.all()

**Template**
{% regroup authors by rating_set.all|last as rating_list %}

{% for rating in rating_list %}
    <b>{{ rating.grouper }}</b><br>
    {% for author in rating.list %}
        {{ author.name }}<br>
    {% endfor %}
{% endfor %}

基本上,这个方法是通过使用 regroup 模板标签,把所有作者按照评分分组。last 过滤器会给你每个作者评分列表中最新的评分。之后就是一个简单的分组练习,把它按评分拆分开来,并显示每个评分下的所有作者。

https://docs.djangoproject.com/en/dev/ref/templates/builtins/?from=olddocs#regroup

https://docs.djangoproject.com/en/dev/ref/templates/builtins/?from=olddocs#last

2

在普通的Django中,要实现最近的功能其实挺麻烦的,需要做很多工作(像最大值、平均值这些都可以通过注解和聚合来完成)。

我用一个自定义管理器来实现,差不多是这样的:

class AuthorManager(models.Manager):
  def with_recent_rating(self):
    return super(AuthorManager, self).get_query_set().extra(
        select={
            'recent_rating': '''
                SELECT e.rating
                FROM myapp_entry e
                WHERE e.authors_id = myapp_author.id
                ORDER BY e.pub_date DESC
                LIMIT 1
                ''',
        })

然后在作者模型中添加以下内容:

class Author():
    ...
    objects = AuthorManager()

接着,当你想要获取带有评分的作者时,只需查询一下:

authors = Author.objects.with_recent_rating().filter(...)

这样做的速度几乎和其他获取数据的方式一样快,只不过现在作者多了一个recent_rating字段。

for author in authors:
    print author.recent_rating
2

另一种方法

from collections import defaultdict
from datetime import datetime, timedelta

week_ago = datetime.now() - timedelta(days=7)

author_recent_ratings = dict(Entry.objects.filter(pub_date__gt=week_ago)
                                          .order_by('pub_date')
                                          .select_related()
                                          .values_list('author', 'rating'))

recent_by_rating = defaultdict(list)
for author, rating in author_recent_ratings.iteritems():
    recent_by_rating[rating].append(author)

这是一种可以实现的方法。简单来说,你可以先按照最近的记录排序(在这个例子中是过去一周的记录),然后再按照最旧的记录排在前面。接着,你把返回的值列表转换成一个字典。这样做的结果是,当转换成字典时,新记录会覆盖旧记录,最终你得到的字典中,作者是键,他们的评分是值。

撰写回答