3 回答

TA貢獻(xiàn)1802條經(jīng)驗(yàn) 獲得超4個(gè)贊
Django REST Framework無法像Django本身一樣為您自動(dòng)優(yōu)化查詢。您可以在一些地方找到提示,包括Django文檔。它已經(jīng)提到的是Django的REST框架應(yīng)該自動(dòng),雖然有與之相關(guān)的一些挑戰(zhàn)。
這個(gè)問題是非常特定于您的情況的,在這種情況下,您正在使用一個(gè)自定義項(xiàng)SerializerMethodField,該自定義項(xiàng)要求返回的每個(gè)對(duì)象。由于您正在(使用Friends.objects管理器)發(fā)出新請(qǐng)求,因此優(yōu)化查詢非常困難。
但是,您可以通過不創(chuàng)建新的查詢集,而從其他位置獲取好友計(jì)數(shù)來使問題更好。這將需要在Friendship模型上創(chuàng)建向后關(guān)系,很可能是通過related_name字段上的參數(shù)創(chuàng)建的,因此您可以預(yù)取所有Friendship對(duì)象。但這僅在需要完整的對(duì)象而不僅僅是對(duì)象的數(shù)量時(shí)才有用。
這將導(dǎo)致視圖和序列化器類似于以下內(nèi)容:
class Friendship(models.Model):
from_friend = models.ForeignKey(User, related_name="friends")
to_friend = models.ForeignKey(User)
class GetAllUsers(generics.ListAPIView):
...
def get_queryset(self):
return User.objects.all().prefetch_related("friends")
class GetAllUsersSerializer(serializers.ModelSerializer):
...
def get_is_friend_already(self, obj):
request = self.context.get('request', None)
friends = set(friend.from_friend_id for friend in obj.friends)
if request.user != obj and request.user.id in friends:
return True
else:
return False
如果只需要計(jì)數(shù)對(duì)象(類似于使用queryset.count()或queryset.exists()),則可以在查詢集中對(duì)行添加反向關(guān)系計(jì)數(shù)。這可以在您的get_queryset方法中完成,方法.annotate(friends_count=Count("friends"))是在末尾添加(如果related_name是friends),這會(huì)將friends_count每個(gè)對(duì)象的屬性設(shè)置為好友數(shù)。
這將導(dǎo)致視圖和序列化器類似于以下內(nèi)容:
class Friendship(models.Model):
from_friend = models.ForeignKey(User, related_name="friends")
to_friend = models.ForeignKey(User)
class GetAllUsers(generics.ListAPIView):
...
def get_queryset(self):
from django.db.models import Count
return User.objects.all().annotate(friends_count=Count("friends"))
class GetAllUsersSerializer(serializers.ModelSerializer):
...
def get_is_friend_already(self, obj):
request = self.context.get('request', None)
if request.user != obj and obj.friends_count > 0:
return True
else:
return False
這兩種解決方案都可以避免N + 1個(gè)查詢,但是您選擇的查詢?nèi)Q于您要實(shí)現(xiàn)的目標(biāo)。

TA貢獻(xiàn)1831條經(jīng)驗(yàn) 獲得超10個(gè)贊
述N + 1個(gè)問題是在一個(gè)首要問題Django的REST框架性能優(yōu)化,所以從各種觀點(diǎn),它需要更多的固體的方法,然后直接prefetch_related()或select_related()在get_queryset()視圖的方法。
根據(jù)收集的信息,這是一個(gè)消除N + 1的強(qiáng)大解決方案(以O(shè)P的代碼為例)。它基于裝飾器,耦合較小,適合大型應(yīng)用程序。
序列化器:
class GetAllUsersSerializer(serializers.ModelSerializer):
friends = FriendSerializer(read_only=True, many=True)
# ...
@staticmethod
def setup_eager_loading(queryset):
queryset = queryset.prefetch_related("friends")
return queryset
在這里,我們使用靜態(tài)類方法來構(gòu)建特定的查詢集。
裝飾器:
def setup_eager_loading(get_queryset):
def decorator(self):
queryset = get_queryset(self)
queryset = self.get_serializer_class().setup_eager_loading(queryset)
return queryset
return decorator
此函數(shù)修改返回的查詢集,以獲取setup_eager_loading序列化器方法中定義的模型的相關(guān)記錄。
視圖:
class GetAllUsers(generics.ListAPIView):
serializer_class = GetAllUsersSerializer
@setup_eager_loading
def get_queryset(self):
return User.objects.all()
這種模式可能看起來像是一個(gè)過大的殺手,但是它肯定更干了,并且比直接在視圖內(nèi)部修改查詢集更具優(yōu)勢(shì),因?yàn)樗梢愿玫乜刂葡嚓P(guān)實(shí)體,并消除了不必要的相關(guān)對(duì)象嵌套。

TA貢獻(xiàn)1799條經(jīng)驗(yàn) 獲得超8個(gè)贊
您可以將視圖分為兩個(gè)查詢。
首先,僅獲取“用戶”列表(無is_friend_already字段)。這僅需要一個(gè)查詢。
其次,獲取request.user的好友列表。
第三,根據(jù)用戶是否在request.user的朋友列表中來修改結(jié)果。
class GetAllUsersSerializer(serializers.ModelSerializer):
...
class UserListView(ListView):
def get(self, request):
friends = request.user.friends
data = []
for user in self.get_queryset():
user_data = GetAllUsersSerializer(user).data
if user in friends:
user_data['is_friend_already'] = True
else:
user_data['is_friend_already'] = False
data.append(user_data)
return Response(status=200, data=data)
添加回答
舉報(bào)