第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

為了賬號(hào)安全,請(qǐng)及時(shí)綁定郵箱和手機(jī)立即綁定
已解決430363個(gè)問題,去搜搜看,總會(huì)有你想問的

在Django REST框架中優(yōu)化數(shù)據(jù)庫(kù)查詢

在Django REST框架中優(yōu)化數(shù)據(jù)庫(kù)查詢

有只小跳蛙 2019-12-26 11:02:53
我有以下型號(hào):class User(models.Model):    name = models.Charfield()    email = models.EmailField()class Friendship(models.Model):    from_friend = models.ForeignKey(User)    to_friend = models.ForeignKey(User)這些模型用在以下視圖和序列化器中:class GetAllUsers(generics.ListAPIView):    authentication_classes = (SessionAuthentication, TokenAuthentication)    permission_classes = (permissions.IsAuthenticated,)    serializer_class = GetAllUsersSerializer    model = User    def get_queryset(self):        return User.objects.all()class GetAllUsersSerializer(serializers.ModelSerializer):    is_friend_already = serializers.SerializerMethodField('get_is_friend_already')    class Meta:        model = User        fields = ('id', 'name', 'email', 'is_friend_already',)    def get_is_friend_already(self, obj):        request = self.context.get('request', None)        if request.user != obj and Friendship.objects.filter(from_friend = user):            return True        else:            return False因此,基本上,對(duì)于GetAllUsers視圖返回的每個(gè)用戶,我想打印出該用戶是否是請(qǐng)求者的朋友(實(shí)際上,我應(yīng)該同時(shí)檢查from_和to_friend,但對(duì)于該問題并不重要)我看到的是,對(duì)于數(shù)據(jù)庫(kù)中的N個(gè)用戶,有1個(gè)查詢可獲取所有N個(gè)用戶,然后在序列化程序的查詢中有1xN個(gè)查詢 get_is_friend_already有沒有辦法避免這種情況?也許就像將select_related包含的查詢傳遞給具有相關(guān)Friendship行的序列化程序一樣?
查看完整描述

3 回答

?
慕虎7371278

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)。


查看完整回答
反對(duì) 回復(fù) 2019-12-26
?
慕哥6287543

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ì)象嵌套。


查看完整回答
反對(duì) 回復(fù) 2019-12-26
?
守著星空守著你

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)


查看完整回答
反對(duì) 回復(fù) 2019-12-26
  • 3 回答
  • 0 關(guān)注
  • 1165 瀏覽
慕課專欄
更多

添加回答

舉報(bào)

0/150
提交
取消
微信客服

購(gòu)課補(bǔ)貼
聯(lián)系客服咨詢優(yōu)惠詳情

幫助反饋 APP下載

慕課網(wǎng)APP
您的移動(dòng)學(xué)習(xí)伙伴

公眾號(hào)

掃描二維碼
關(guān)注慕課網(wǎng)微信公眾號(hào)