Django REST Framework:我想在 SerializerMethodField 中解析 n+1
Django REST Framework: I want to resolve n+1 in SerializerMethodField
我正在尝试创建一个 returns 布尔值的查询集,该查询集来自 SerializerMethodField
预取的反向引用,如下面的代码所示。
我正在创建一个确定当前用户和 returns 布尔值是否存在对象的对象。
但是,当我使用预取的查询集按当前用户进行过滤时,如下所示,发出了一个新的查询集而不是预取的查询集,出现了n+1问题。
在下面的代码中,我们如何保存queryset和return
布尔兰?
# serializers.py
class VideoSerializer(serializers.ModelSerializer):
is_viewed = serializers.SerializerMethodField()
is_favorited = serializers.SerializerMethodField()
is_wl = serializers.SerializerMethodField()
class Meta:
model = Video
fields = (
"pk",
"is_viewed",
"is_favorited",
"is_wl",
)
@staticmethod
def setup_eager_loading(queryset):
queryset.prefetch_related('history_set', 'favorite_set')
def get_is_viewed(self, obj):
user = self.context["request"].user
if user.is_authenticated:
try:
obj.history_set.get(user=user) # <- here
return True
except History.DoesNotExist:
pass
return False
def get_is_favorited(self, obj):
user = self.context["request"].user
if user.is_authenticated:
try:
obj.favorite_set.get(user=user) # <- here
return True
except Favorite.DoesNotExist:
pass
return False
def get_is_wl(self, obj):
user = self.context["request"].user
if user.is_authenticated:
try:
Track.objects.get(playlist__user=user, playlist__is_wl=True, video=obj)
return True
except Track.DoesNotExist:
pass
return False
A large number of query sets were issued.
#models.py
class Video(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
title = models.CharField("title", max_length=300)
def __str__(self):
return self.title
class History(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
user = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE
)
video = models.ForeignKey(Video, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return f"{self.user}, {self.video.title}"
class Favorite(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
user = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE
)
video = models.ForeignKey(Video, on_delete=models.CASCADE)
created_at = models.DateTimeField(default=timezone.now)
def __str__(self):
return f"{self.user}, {self.video.title}"
您可以使用 Exists 子查询。
Video.objects.annotate(is_favorite=Exists(Favorite.objects.filter(user=self.request.user, video=OuterRef("id"))))
之后您可以访问 is_favorite
属性。
class VideoSerializer(serializers.ModelSerializer):
is_viewed = serializers.SerializerMethodField()
is_favorited = serializers.SerializerMethodField()
is_wl = serializers.SerializerMethodField()
class Meta:
model = Video
fields = (
"pk",
"is_viewed",
"is_favorited",
"is_wl",
)
@staticmethod
def setup_eager_loading(queryset):
queryset.prefetch_related('history_set', 'favorite_set')
def get_is_viewed(self, obj):
user = self.context["request"].user
if user.is_authenticated:
try:
obj.history_set.get(user=user) # <- here
return True
except History.DoesNotExist:
pass
return False
def get_is_favorited(self, obj):
return obj.is_favorite
def get_is_wl(self, obj):
user = self.context["request"].user
if user.is_authenticated:
try:
Track.objects.get(playlist__user=user, playlist__is_wl=True, video=obj)
return True
except Track.DoesNotExist:
pass
return False
您可以为其他字段添加注释(is_views、is_wl)
正在使用 prefetch_related 方法调用进行另一个查询。你可以在 django 文档中找到它。
https://docs.djangoproject.com/en/3.2/ref/models/querysets/
我认为使用子查询和 Exists 查询表达式是更好的选择,正如 'Utkucan Bıyıklı' 建议的那样。
通过反向引用,我知道你指的是一个外键字段,在这种情况下你需要使用select_related
,prefetch_related
用于许多字段。
基于它,您可以使用以下任何代码。确保 return 查询集。
@staticmethod
def setup_eager_loading(queryset):
#prefetch_related for 'to-many' relationships
queryset.prefetch_related('history_set', 'favorite_set')
return quesyset
@staticmethod
def setup_eager_loading(queryset):
#select_related for 'foreign key' relationships
queryset = queryset.select_related('history_set', 'favorite_set')
return queryset
我正在尝试创建一个 returns 布尔值的查询集,该查询集来自 SerializerMethodField
预取的反向引用,如下面的代码所示。
我正在创建一个确定当前用户和 returns 布尔值是否存在对象的对象。
但是,当我使用预取的查询集按当前用户进行过滤时,如下所示,发出了一个新的查询集而不是预取的查询集,出现了n+1问题。
在下面的代码中,我们如何保存queryset和return 布尔兰?
# serializers.py
class VideoSerializer(serializers.ModelSerializer):
is_viewed = serializers.SerializerMethodField()
is_favorited = serializers.SerializerMethodField()
is_wl = serializers.SerializerMethodField()
class Meta:
model = Video
fields = (
"pk",
"is_viewed",
"is_favorited",
"is_wl",
)
@staticmethod
def setup_eager_loading(queryset):
queryset.prefetch_related('history_set', 'favorite_set')
def get_is_viewed(self, obj):
user = self.context["request"].user
if user.is_authenticated:
try:
obj.history_set.get(user=user) # <- here
return True
except History.DoesNotExist:
pass
return False
def get_is_favorited(self, obj):
user = self.context["request"].user
if user.is_authenticated:
try:
obj.favorite_set.get(user=user) # <- here
return True
except Favorite.DoesNotExist:
pass
return False
def get_is_wl(self, obj):
user = self.context["request"].user
if user.is_authenticated:
try:
Track.objects.get(playlist__user=user, playlist__is_wl=True, video=obj)
return True
except Track.DoesNotExist:
pass
return False
A large number of query sets were issued.
#models.py
class Video(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
title = models.CharField("title", max_length=300)
def __str__(self):
return self.title
class History(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
user = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE
)
video = models.ForeignKey(Video, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return f"{self.user}, {self.video.title}"
class Favorite(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
user = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE
)
video = models.ForeignKey(Video, on_delete=models.CASCADE)
created_at = models.DateTimeField(default=timezone.now)
def __str__(self):
return f"{self.user}, {self.video.title}"
您可以使用 Exists 子查询。
Video.objects.annotate(is_favorite=Exists(Favorite.objects.filter(user=self.request.user, video=OuterRef("id"))))
之后您可以访问 is_favorite
属性。
class VideoSerializer(serializers.ModelSerializer):
is_viewed = serializers.SerializerMethodField()
is_favorited = serializers.SerializerMethodField()
is_wl = serializers.SerializerMethodField()
class Meta:
model = Video
fields = (
"pk",
"is_viewed",
"is_favorited",
"is_wl",
)
@staticmethod
def setup_eager_loading(queryset):
queryset.prefetch_related('history_set', 'favorite_set')
def get_is_viewed(self, obj):
user = self.context["request"].user
if user.is_authenticated:
try:
obj.history_set.get(user=user) # <- here
return True
except History.DoesNotExist:
pass
return False
def get_is_favorited(self, obj):
return obj.is_favorite
def get_is_wl(self, obj):
user = self.context["request"].user
if user.is_authenticated:
try:
Track.objects.get(playlist__user=user, playlist__is_wl=True, video=obj)
return True
except Track.DoesNotExist:
pass
return False
您可以为其他字段添加注释(is_views、is_wl)
正在使用 prefetch_related 方法调用进行另一个查询。你可以在 django 文档中找到它。
https://docs.djangoproject.com/en/3.2/ref/models/querysets/
我认为使用子查询和 Exists 查询表达式是更好的选择,正如 'Utkucan Bıyıklı' 建议的那样。
通过反向引用,我知道你指的是一个外键字段,在这种情况下你需要使用select_related
,prefetch_related
用于许多字段。
基于它,您可以使用以下任何代码。确保 return 查询集。
@staticmethod
def setup_eager_loading(queryset):
#prefetch_related for 'to-many' relationships
queryset.prefetch_related('history_set', 'favorite_set')
return quesyset
@staticmethod
def setup_eager_loading(queryset):
#select_related for 'foreign key' relationships
queryset = queryset.select_related('history_set', 'favorite_set')
return queryset