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

时间:2015-07-03 03:39:18

标签: 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.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查询。

这可以实现吗?

3 个答案:

答案 0 :(得分:2)

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

答案 1 :(得分:1)

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

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

message_obj.get_read_count()

答案 2 :(得分:0)

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

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

然后我可以使用python中的读取计数来映射消息。 这个解决方案对我来说已经足够了。