【问题标题】:How to get an extra count field with Django ORM?如何使用 Django ORM 获得额外的计数字段?
【发布时间】:2015-09-20 18:39:51
【问题描述】:

我的 Django 模型是这样的:

class User(models.Model):
    username = models.CharField(max_length=32)
class Message(models.Model):
    content = models.TextField()
class UserMessageRel(models.Model):
    user = models.ForeignKey(User)
    message = models.ForeignKey(Message)
    is_read = models.BooleanField()

现在我想获取所有消息,对于每条消息,我需要知道有多少收到它的用户阅读了它。

天真的做法是:

msgs = Message.objects.all()
messages = []
for msg in msgs:
    reads = UserMessageRel.objects.filter(message=msg, is_read=True).count()
    messages.append((msg, reads))

但是这样效率很低,用 SQL 查询来获取每条消息的读取次数。

我不确定这是否可以通过 ORM 中的注释或聚合来完成?

我想要的是这样的:

msgs_with_reads = Message.objects.all().annotate(
    number_of_reads=Count("user_message_rel_with_is_read_true"))

可以翻译成一个很好的 SQL 查询。

这可以实现吗?

【问题讨论】:

  • 我认为是,但是当我尝试它时,它也相当复杂。我现在没有代码 sn-p,但我认为它涉及 group() 并且可能使用反向关系。最后的手段总是使用.sql()

标签: python django django-models orm


【解决方案1】:

我将您的问题解释为您希望缩短此计数的查询时间。不幸的是,在当前设置下,需要进行全表扫描。有很多方法可以改进它,最简单的是索引。您可以在 UserMessageRel 中的 Message id 列上添加索引,这将加快读取时间(当然是以空间为代价的)。不过,访问此计数的最易读的方法是 Pieter 的回答。

【讨论】:

    【解决方案2】:

    您可以从 Message 对象中进行相关查找,我会像这样在 Message 模型上放置一个辅助函数,然后您就可以从对象中调用该函数。

    def get_read_count(self):
        return self.usermessagerel_set.filter(is_read=True).count()
    
    message_obj.get_read_count()
    

    【讨论】:

    • 我相信这相当于我的幼稚解决方案。
    【解决方案3】:

    我没有找到使用 Django ORM 为我的需求生成一个 SQL 查询的方法,但是下面的代码可以生成 2 个查询:

    messages = Message.objects.all()
    messageReads = UserMessageRel.objects.filter(isRead=True).
        values("message_id").annotate(cnt=Count("user"))
    

    然后我可以将消息与它们在 python 中的读取计数进行映射。 这个解决方案对我来说已经足够了。

    【讨论】:

      猜你喜欢
      • 2018-06-03
      • 1970-01-01
      • 2020-01-30
      • 1970-01-01
      • 2020-11-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-24
      相关资源
      最近更新 更多