【问题标题】:Django list all authors by most recent ratingDjango 按最新评级列出所有作者
【发布时间】:2011-08-08 00:28:20
【问题描述】:

鉴于 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字典,比如:{1star:(author1,author2),2star:(author3,author4,author5)...}。

一种想法是返回所有条目,然后使用 itertools.groupby 来操作大型数据集。任何人都可以提出更清洁的替代方案吗?

【问题讨论】:

    标签: python django


    【解决方案1】:

    另一种方法

    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)
    

    这是您可以做到的一种方式。基本上,您按最近的条目(在本例中为上周的条目)排序,然后按最旧的第一个排序,然后将值列表返回的列表转换为字典。发生的情况是,当它被转换为字典时,较新的条目会破坏较旧的条目,因此您最终会得到一个以作者为键、以他们的评分为值的字典。

    【讨论】:

    • 这非常接近。我必须添加一些内容才能将字典转换为以下形式:{1star:[author1,author2],2star:[author3,author4,author5]...}。基本上我将初始字典转换为元组列表,然后再次使用 defaultdict 将其转换回原始字典的“反向”。谢谢。
    【解决方案2】:

    如果没有大量工作(最大值、平均值等都可以通过注释和聚合来完成),在 vanilla 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
                    ''',
            })
    

    然后将以下内容添加到 Author 模型中:

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

    然后,当您想要具有您刚刚查询的评级的作者时:

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

    除了现在作者有一个 recent_rating 字段之外,它几乎与任何其他提取速度相同。:

    for author in authors:
        print author.recent_rating
    

    【讨论】:

    • 感谢罗布的建议。这看起来需要每个作者的数据库命中。不幸的是,我认为这不适用于我的情况。作者模型是我工作的代理,但在某些情况下,我预计会有超过 5000 位作者。
    • 不,实际上,自定义管理器扩展了 ORM 提取,几乎没有额外的开销。
    • 感谢您的澄清,我从未使用过自定义管理器。我刚刚完成了 t3hmas 的建议。这看起来比较容易实现,我想如果将最近的评分存储在数据库中,它应该也会更快。
    • 我只是把一个简单的测试放在一起,有 10 个作者,每个人都有 1 或 2 个条目。 'Author.objects.with_recent_rating().all()' 生成的 sql 为:'SELECT (SELECT e.rating FROM toys_entry e WHERE e.authors_id = Toys_author.id ORDER BY e.pub_date DESC LIMIT 1) AS "recent_rating" , "toys_author"."id", "toys_author"."name", "toys_author"."email" FROM "toys_author" LIMIT 21' 这是一条 SQL 语句,但是使用 EXPLAIN 显示它包含一个 DEPENDENT SUBQUERY,这意味着它将针对外部查询中的每一行进行评估。对于许多作者来说,这将变得异常缓慢。
    【解决方案3】:

    您实际上可以完全在您的模板中执行此操作。这样的事情应该可以工作:

    **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

    【讨论】:

      猜你喜欢
      • 2015-12-02
      • 2011-04-18
      • 1970-01-01
      • 1970-01-01
      • 2022-08-11
      • 1970-01-01
      • 1970-01-01
      • 2018-06-25
      • 1970-01-01
      相关资源
      最近更新 更多