Python 如何使用Django ORM获取额外的计数字段?

Python 如何使用Django ORM获取额外的计数字段?,python,django,django-models,orm,Python,Django,Django Models,Orm,我的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.ForeignKe

我的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_with_reads = Message.objects.all().annotate(
    number_of_reads=Count("user_message_rel_with_is_read_true"))
现在我想获取所有消息,对于每条消息,我需要知道有多少收到它的用户已经阅读了它

天真的做法是:

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中的注释或聚合来实现

我想要的是这样的东西:

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_with_reads = Message.objects.all().annotate(
    number_of_reads=Count("user_message_rel_with_is_read_true"))
可以将其转换为一个漂亮的SQL查询


这是可以实现的吗?

您可以从消息对象执行相关的查找,我会像这样在消息模型上放置一个helper函数,然后您可以从对象调用该函数

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

message_obj.get_read_count()

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

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

messages = Message.objects.all()
messageReads = UserMessageRel.objects.filter(isRead=True).
    values("message_id").annotate(cnt=Count("user"))
然后,我可以在python中将消息映射为它们的读取计数。
这个解决方案对我来说已经足够好了。

我相信这与我天真的解决方案是一样的。我想是的,但当我尝试它时,它也相当复杂。我现在没有代码片段,但我认为它涉及
group()
,可能使用了相反的关系。最后的办法是始终使用
.sql()