2013-05-29 56 views
53

在我的應用我有以下型號:如何將過濾器應用於Django REST框架中的嵌套資源?

class Zone(models.Model): 
    name = models.SlugField() 

class ZonePermission(models.Model): 
    zone = models.ForeignKey('Zone') 
    user = models.ForeignKey(User) 
    is_administrator = models.BooleanField() 
    is_active = models.BooleanField() 

我使用Django的REST框架來創建一個返回區的詳細信息以及顯示該區域的驗證用戶的權限嵌套資源的資源。輸出應該是這樣的:

{ 
    "name": "test", 
    "current_user_zone_permission": { 
     "is_administrator": true, 
     "is_active": true 
    } 
} 

我創建序列化,像這樣:

class ZonePermissionSerializer(serializers.ModelSerializer): 
    class Meta: 
     model = ZonePermission 
     fields = ('is_administrator', 'is_active') 

class ZoneSerializer(serializers.HyperlinkedModelSerializer): 
    current_user_zone_permission = ZonePermissionSerializer(source='zonepermission_set') 

    class Meta: 
     model = Zone 
     fields = ('name', 'current_user_zone_permission') 

這樣做的問題是,當我要求一個特定的區域,嵌套的資源返回ZonePermission記錄對於全部爲的用戶具有該區域的權限。有什麼方法可以將request.user上的過濾器應用於嵌套資源?

順便說一句我不想使用HyperlinkedIdentityField(爲了最小化http請求)。

解決方案

這是我基於下面的答案實施的解決方案。我將以下代碼添加到我的序列化程序類中:

current_user_zone_permission = serializers.SerializerMethodField('get_user_zone_permission') 

def get_user_zone_permission(self, obj): 
    user = self.context['request'].user 
    zone_permission = ZonePermission.objects.get(zone=obj, user=user) 
    serializer = ZonePermissionSerializer(zone_permission) 
    return serializer.data 

非常感謝您的解決方案!

回答

27

我面臨着同樣的情況。我找到的最佳解決方案是使用SerializerMethodField並讓該方法查詢並返回所需的值。您可以通過self.context['request'].user訪問該方法中的request.user

儘管如此,這似乎有點破解。我對DRF相當陌生,所以也許有人有更多的經驗可以參加。

+0

感謝您的建議。 「SerializerMethodField」可以返回一個結構還是隻是一個平坦的字段? –

+0

它可以返回一個結構。 – user2437225

+0

我會採取這種方法 - 謝謝。如果沒有其他「官方」建議通過,我會接受這個答案。 –

6

你必須使用過濾器而不是get,否則如果多個記錄返回,你將得到Exception。

current_user_zone_permission = serializers.SerializerMethodField('get_user_zone_permission') 

def get_user_zone_permission(self, obj): 
    user = self.context['request'].user 
    zone_permission = ZonePermission.objects.filter(zone=obj, user=user) 
    serializer = ZonePermissionSerializer(zone_permission,many=True) 
    return serializer.data 
5

現在你也可以繼承的ListSerializer,利用我在這裏介紹的方法:https://stackoverflow.com/a/28354281/3246023

你也可以繼承的ListSerializer並覆蓋to_representation方法。

默認情況下,to_representation方法調用嵌套查詢集上的data.all()。因此,在調用方法之前,您需要有效地創建data = data.filter(** your_filters)。然後,您需要在嵌套序列化程序的元素上添加您的子類ListSerializer作爲list_serializer_class。

  1. 子ListSerializer,覆蓋to_representation然後調用嵌套串行
+0

我也更喜歡這種方法,因爲如果需要的話,它仍然可以保持字段可寫。 – jgiralt

4

子類ListSerializer作爲元list_serializer_class超級

  • 添加如果您在多個地方使用的QuerySet /過濾器,你可以在你的模型上使用getter函數,然後甚至放棄串行器/字段的'source'kwarg。當使用get_attribute函數時,DRF自動調用函數/可調用函數

    class Zone(models.Model): 
        name = models.SlugField() 
    
        def current_user_zone_permission(self): 
         return ZonePermission.objects.get(zone=self, user=user) 
    

    我喜歡這種方法,因爲它讓你的API與API通過HTTP引擎蓋下是一致的。

    class ZoneSerializer(serializers.HyperlinkedModelSerializer): 
        current_user_zone_permission = ZonePermissionSerializer() 
    
        class Meta: 
         model = Zone 
         fields = ('name', 'current_user_zone_permission') 
    

    希望這可以幫助一些人!

    注意:名稱不需要需要匹配,如果需要/想要的話,仍然可以使用源kwarg。

    編輯:我只是意識到模型上的功能不能訪問用戶或請求。所以也許一個自定義模型字段/ ListSerializer會更適合這個任務。

  • 2

    我會用兩種方法之一來做。

    1)無論是做通過預取您的看法:

    serializer = ZoneSerializer(Zone.objects.prefetch_related(
         Prefetch('zone_permission_set', 
          queryset=ZonePermission.objects.filter(user=request.user), 
          to_attr='current_user_zone_permission')) 
         .get(id=pk)) 
    

    2)或做雖然.to_representation:

    class ZoneSerializer(serializers.HyperlinkedModelSerializer): 
    
        class Meta: 
         model = Zone 
         fields = ('name',) 
    
        def to_representation(self, obj): 
         data = super(ZoneSerializer, self).to_representation(obj) 
         data['current_user_zone_permission'] = ZonePermissionSerializer(ZonePermission.objects.filter(zone=obj, user=self.context['request'].user)).data 
         return data