django orm -子查询中的注解/聚合(avg)

6jjcrrmo  于 2023-05-19  发布在  Go
关注(0)|答案(1)|浏览(194)

我有这个模型:

class UserMovieRel(models.Model):
    user = models.ForeignKey("register.User", on_delete=models.CASCADE)
    movie = models.ForeignKey("Movie", on_delete=models.CASCADE, related_name="users")
    rating = models.PositiveIntegerField(
        validators=[MinValueValidator(1), MaxValueValidator(10)], null=True, blank=True
    )
    advice = models.CharField(max_length=500, null=True, blank=True)
    objects = UserMovieRelManager()

    def __str__(self) -> str:
        return f"{self.user} - {self.movie} (rating: {self.rating or 'n/a'})"

    class Meta:
        constraints = [
            models.UniqueConstraint(fields=["user", "movie"], name="user_movie_unique"),
        ]

我试着用这种方法来获得每部电影的avg评级:

avg_ratings = UserMovieRel.objects.filter(movie_id=OuterRef("movie_id")).exclude(rating__isnull=True).annotate(avg_rating=Avg("rating"))
UserMovieRel.objects.annotate(avg_rating=Subquery(avg_ratings[0]))

但它失败了:

ValueError: This queryset contains a reference to an outer query and may only be used in a subquery.

我也尝试过聚合:

UserMovieRel.objects.annotate(
    avg_rating=Subquery(
        UserMovieRel.objects.filter(
            movie_id=OuterRef("movie_id")
        ).aggregate(
            avg_rating=Avg("rating")
        )["avg_rating"]
    )
)

但我也犯了同样的错误
对此有任何帮助吗?谢谢

nszi6y05

nszi6y051#

您可以使用以下选项进行筛选:

Movie.objects.annotate(avg_rating=Avg('users__rating'))

我们使用users的原因是因为这是related_name='users'的值,但没有多大意义。
您可以将其重命名为:

from django.conf import settings

from django import models

class Review(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    movie = models.ForeignKey(
        'Movie', on_delete=models.CASCADE, related_name='reviews'
    )
    rating = models.PositiveIntegerField(
        validators=[MinValueValidator(1), MaxValueValidator(10)],
        null=True,
        blank=True,
    )
    advice = models.CharField(max_length=500, null=True, blank=True)
    objects = UserMovieRelManager()

因此查询:

Movie.objects.annotate(avg_rating=Avg('reviews__rating'))

:型号通常没有Rel后缀。模型不是关系或表,它作为表存储在关系数据库中,但即使这样,它也有额外的逻辑,如验证器,管理器等。
注意:通常情况下,使用**settings.AUTH_USER_MODEL[Django-doc]引用用户模型比直接使用User**模型[Django-doc]更好。有关详细信息,请参阅文档的 referencing the User model 部分。

相关问题