如何在 Django 中的特定链式 prefetch_related() 上应用任意过滤器?
How to apply an arbitrary filter on a specific chained prefetch_related() within Django?
我正在尝试优化 API 的触发查询。我有四个模型,即 User、Content、Rating 和 UserRating,它们之间存在某种关系。我想要相应的 API returns 所有现有内容及其评级计数以及特定用户给出的分数。
我曾经做过这样的事情:Content.objects.all()
作为查询集,但我意识到在拥有大量数据的情况下,将触发大量查询。所以我做了一些努力来优化使用 select_related()
和 prefetch_related()
触发的查询。但是,我正在处理一个额外的 python 搜索,我希望使用受控的 prefetch_related()
删除它 - 仅针对嵌套 prefetch
中的特定 prefetch
应用过滤器=] 和 select
.
这是我的模型:
from django.db import models
from django.conf import settings
class Content(models.Model):
title = models.CharField(max_length=50)
class Rating(models.Model):
count = models.PositiveBigIntegerField(default=0)
content = models.OneToOneField(Content, on_delete=models.CASCADE)
class UserRating(models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL, blank=True, null=True, on_delete=models.CASCADE
)
score = models.PositiveSmallIntegerField()
rating = models.ForeignKey(
Rating, related_name="user_ratings", on_delete=models.CASCADE
)
class Meta:
unique_together = ["user", "rating"]
这是我到目前为止所做的:
contents = (
Content.objects.select_related("rating")
.prefetch_related("rating__user_ratings")
.prefetch_related("rating__user_ratings__user")
)
for c in contents: # serializer like
user_rating = c.rating.user_ratings.all()
for u in user_rating: # how to remove this dummy search?
if u.user_id == 1:
print(u.score)
查询:
(1) SELECT "bitpin_content"."id", "bitpin_content"."title", "bitpin_rating"."id", "bitpin_rating"."count", "bitpin_rating"."content_id" FROM "bitpin_content" LEFT OUTER JOIN "bitpin_rating" ON ("bitpin_content"."id" = "bitpin_rating"."content_id"); args=(); alias=default
(2) SELECT "bitpin_userrating"."id", "bitpin_userrating"."user_id", "bitpin_userrating"."score", "bitpin_userrating"."rating_id" FROM "bitpin_userrating" WHERE "bitpin_userrating"."rating_id" IN (1, 2); args=(1, 2); alias=default
(3) SELECT "users_user"."id", "users_user"."password", "users_user"."last_login", "users_user"."is_superuser", "users_user"."first_name", "users_user"."last_name", "users_user"."email", "users_user"."is_staff", "users_user"."is_active", "users_user"."date_joined", "users_user"."user_name" FROM "users_user" WHERE "users_user"."id" IN (1, 4); args=(1, 4); alias=default
正如您在上面触发的查询中看到的那样,我只有三个查询,而不是过去发生的太多查询。但是,我想我可以删除 python 搜索(第二个 for
循环),而不是在我的最新查询中使用过滤器 — users_user"."id" IN (1,)
。据此post and my efforts, I couldn't apply a .filter(rating__user_ratings__user_id=1)
on the third query. Actually, I couldn't match my problem using Prefetch(..., queryset=...)
instance given in this answer.
我认为您正在寻找 Prefetch 对象:
https://docs.djangoproject.com/en/4.0/ref/models/querysets/#prefetch-objects
试试这个:
from django.db.models import Prefetch
contents = Content.objects.select_related("rating").prefetch_related(
Prefetch(
"rating__user_ratings",
queryset=UserRating.objects.filter(user__id=1),
to_attr="user_rating_number_1",
)
)
for c in contents: # serializer like
print(c.rating.user_rating_number_1[0].score)
我正在尝试优化 API 的触发查询。我有四个模型,即 User、Content、Rating 和 UserRating,它们之间存在某种关系。我想要相应的 API returns 所有现有内容及其评级计数以及特定用户给出的分数。
我曾经做过这样的事情:Content.objects.all()
作为查询集,但我意识到在拥有大量数据的情况下,将触发大量查询。所以我做了一些努力来优化使用 select_related()
和 prefetch_related()
触发的查询。但是,我正在处理一个额外的 python 搜索,我希望使用受控的 prefetch_related()
删除它 - 仅针对嵌套 prefetch
中的特定 prefetch
应用过滤器=] 和 select
.
这是我的模型:
from django.db import models
from django.conf import settings
class Content(models.Model):
title = models.CharField(max_length=50)
class Rating(models.Model):
count = models.PositiveBigIntegerField(default=0)
content = models.OneToOneField(Content, on_delete=models.CASCADE)
class UserRating(models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL, blank=True, null=True, on_delete=models.CASCADE
)
score = models.PositiveSmallIntegerField()
rating = models.ForeignKey(
Rating, related_name="user_ratings", on_delete=models.CASCADE
)
class Meta:
unique_together = ["user", "rating"]
这是我到目前为止所做的:
contents = (
Content.objects.select_related("rating")
.prefetch_related("rating__user_ratings")
.prefetch_related("rating__user_ratings__user")
)
for c in contents: # serializer like
user_rating = c.rating.user_ratings.all()
for u in user_rating: # how to remove this dummy search?
if u.user_id == 1:
print(u.score)
查询:
(1) SELECT "bitpin_content"."id", "bitpin_content"."title", "bitpin_rating"."id", "bitpin_rating"."count", "bitpin_rating"."content_id" FROM "bitpin_content" LEFT OUTER JOIN "bitpin_rating" ON ("bitpin_content"."id" = "bitpin_rating"."content_id"); args=(); alias=default
(2) SELECT "bitpin_userrating"."id", "bitpin_userrating"."user_id", "bitpin_userrating"."score", "bitpin_userrating"."rating_id" FROM "bitpin_userrating" WHERE "bitpin_userrating"."rating_id" IN (1, 2); args=(1, 2); alias=default
(3) SELECT "users_user"."id", "users_user"."password", "users_user"."last_login", "users_user"."is_superuser", "users_user"."first_name", "users_user"."last_name", "users_user"."email", "users_user"."is_staff", "users_user"."is_active", "users_user"."date_joined", "users_user"."user_name" FROM "users_user" WHERE "users_user"."id" IN (1, 4); args=(1, 4); alias=default
正如您在上面触发的查询中看到的那样,我只有三个查询,而不是过去发生的太多查询。但是,我想我可以删除 python 搜索(第二个 for
循环),而不是在我的最新查询中使用过滤器 — users_user"."id" IN (1,)
。据此post and my efforts, I couldn't apply a .filter(rating__user_ratings__user_id=1)
on the third query. Actually, I couldn't match my problem using Prefetch(..., queryset=...)
instance given in this answer.
我认为您正在寻找 Prefetch 对象: https://docs.djangoproject.com/en/4.0/ref/models/querysets/#prefetch-objects
试试这个:
from django.db.models import Prefetch
contents = Content.objects.select_related("rating").prefetch_related(
Prefetch(
"rating__user_ratings",
queryset=UserRating.objects.filter(user__id=1),
to_attr="user_rating_number_1",
)
)
for c in contents: # serializer like
print(c.rating.user_rating_number_1[0].score)