在Django

时间:2019-01-31 14:02:34

标签: python django django-rest-framework

我与3个模型的关系非常简单,并且在构造查询集以返回所需方式的结果时遇到问题。

型号:


class Book(models.Model):
    name = models.CharField()

class CategoryLabel(models.Model):
    name = models.CharField()

class Review(models.Model):
    user = models.ForeignKey(User)
    book = models.ForeignKey(Book)
    labels = models.ManyToManyField(CategoryLabel)
    rating = models.IntegerField()

现在,查询集结果应该是特定用户撰写过评论的“图书”列表。

books = Book.objects.filter(review__user=user)

很容易实现

棘手的部分是,我需要输出(通过django rest框架序列化器)一本书的列表,每本书包含针对指定用户的 only 的评论列表,以及汇总的平均评分(再次) ,仅适用于指定用户)和评论标记的一组不同标签。

在JSON中是这样的:

[
    "name": "Book1",
    "reviews": [
        "review_id1", "review_id2"
    ],
    "average_rating": 3,
    "labels": [
        "label_id1", "label_id2"
    ]
]

到目前为止,我已经尝试过:

Book.objects.filter(reviews__user=user)
            .annotate(average_rating=Avg("reviews__rating"))
            .annotate(labels=ArrayAgg("reviews__labels", distinct=True))

这确实产生了我需要的格式,但它汇总了所有所有评论的平均评分,不仅包括user的评论,还包括带有标签的同一故事,它仅包括所有标签都应用在所有评论中... 编辑:这是不正确的。

有什么想法可以尽可能有效地做到这一点?数据库是PostgreSQL,因此可以选择pg特定功能。

2 个答案:

答案 0 :(得分:0)

根据您的模型和问题描述,这听起来像您想要按已过滤特定用户评论的书进行小组评论:

Review.objects.filter(user=user).values('book__name').annotate(
    average_rating=Avg(F('rating'))
)

>>> <QuerySet [{'book__name': 'The ultimate code', 'average_rating': 7.0}]>

对不起,我尚未使用ArrayAgg进行测试,ArrayAgg是一种PostgresSQL特定的聚合函数。

上面的代码是针对包含2条评论的数据库执行的,其中1条评论的评分为10,其他评论的评分为4。

答案 1 :(得分:0)

Silly,但是原始问题中的代码确实有效。我遇到的问题在其他地方,与本示例无关...:/

原始问题中的陈述有误:

This does produce the format I need but it aggregates average rating over all reviews, not only the ones by the user and the same story with labels, it just includes all labels applied in all reviews...

实际上不是这样,这似乎可以正常工作:

Book.objects.filter(reviews__user=user)
            .annotate(average_rating=Avg("reviews__rating"))
            .annotate(labels=ArrayAgg("reviews__labels", distinct=True))