2015-09-23 22 views
0

我想將請求上下文添加到Django REST框架中的序列化器中。特別是嵌套的序列化程序,我(成功)試圖用SerializerMethodField來做到這一點(作爲我的解決方案:context in nested serializers django rest framework)。這是我使用的設置:將請求/上下文添加到可寫序列化器中的django中

class VehicleTypeSerializer(RsModelSerializer): 

    class Meta: 
     model = VehicleType 


class VehicleSerializer(RsModelSerializer): 

    vehicletype = SerializerMethodField() 

    class Meta: 
     model = Vehicle 
     fields = ('vehiclename', 'vehicledescription', 'vehicletype') 

    def get_vehicletype(self, obj): 
     return self.get_serializermethodfield_data(obj, VehicleType, VehicleTypeSerializer, 'vehicle') 


    def get_serializermethodfield_data(self, obj, model_class, serializer_class, filter_field): 
     filter = {filter_field: obj} 
     objs = model_class.objects.all().filter(**filter) 

     # We need the request-context for checking field permissions in the serializer 
     s = serializer_class(objs, many=True, context={'request': self.context.get('request')}) 
     return s.data 

問題:我需要一個SerializerMethodField將請求傳遞上下文到嵌套串器(VehicleTypeSerializer) 但現在我堅持處理POST的,因爲SerializerMethodField讀-只要。

{ 
    "vehiclename": "test", 
    "vehicledescription": "test" 
    "vehicletype": "1" <---- get's ignored since SerializerMethodField is read-only 
} 

問題:我不能發佈一個對象/ API/V1 /車輛可有人點我在正確的方向請求上下文(尤其是用戶信息)加入到嵌套序列化器,我可以寫信給?

我需要VehicleSerializer以及VechileTypeSerializer中的請求上下文(request.user),因爲在我定義的RsModelSerializer中,我檢查每個字段的基礎,如果用戶正在執行請求有權讀取或更新字段。

在RsModelSerializer:

def __init__(self, *args, **kwargs): 
    super().__init__(*args, **kwargs) 

    # Make sure that there is a user mapped in the context (we need a user 
    # for checking permissions on a field). If there is no user, we set 
    # the user to None. 
    if not self.context: 
     self._context = getattr(self.Meta, 'context', {}) 
    try: 
     self.user = self.context['request'].user 
    except (KeyError, AttributeError): 
     print('No request') 
     self.user = None 


def get_fields(self): 
    """ 
    Override get_fields to ensure only fields that are allowed 
    by model-field-permissions are returned to the serializer 
    :return: Dict with allowed fields 
    """ 

    ret = OrderedDict() 
    fields = super().get_fields() 

    # If no user is associated with the serializer, return no fields 
    if self.user == None: 
     return None 

    # A superuser bypasses the permissions-check and gets all 
    # available fields 
    if self.user.is_superuser: 
     print_without_test("user is superuser, bypassing permissions") 
     return fields 

    # Walk through all available fields and check if a user has permission for 
    # it. If he does, add them to a return-array. This way all fields that 
    # are not allowed to 'read' will be dropped. Note: this is only used 
    # for read access. Write access is handled in the views (modelviewsets). 
    for f in fields: 
     if has_permission(user=self.user, app_label=self.Meta.model._meta.app_label, 
          table=self.Meta.model.__name__.lower(), 
          field=f, 
          permission='read'): 
      ret[f] = fields[f] 

    return ret 

回答

1

方法1:覆蓋父串行的__init__()方法

您能在父母串行的__init__()方法的上下文添加到嵌套/子串。

class RsModelSerializer(serializers.ModelSerializer): 

    def __init__(self, *args, **kwargs): 
     super(RsModelSerializer, self).__init__(*args, **kwargs) 
     request_obj = self.context.get('request') # get the request from parent serializer's context 
     # assign request object to nested serializer context 
     self.fields['nested_serializer_field'].context['request'] = request_obj 

我們不能在他們__init__()的時間通過上下文嵌套的串行器,因爲它們會在聲明父串行的時間進行初始化。

class SomeParentSerializer(serializers.Serializer): 

    some_child = SomeChildSerializer() # gets initialized here 

方法2:傳遞context當孩子串行被綁定到其父

另一種選擇是,當一個孩子/嵌套串行被綁定到父添加上下文。

class SomeChildSerializer(Serializer): 

    def bind(self, field_name, parent): 
     super(SomeChildSerializer, self).bind(field_name, parent) # child gets binded to parent 
     request_obj = parent.context.get('request') # get the request from parent serializer context 
     self.context['request'] = request_obj 

引述DRF筆者建議選擇在相關機票:

這應該被視爲私有API,以及上面列出的父 __init__風格應該是首選。

所以,更好的選擇是重寫的ParentSerializer__init__()方法並傳遞context兒童/嵌套串。

來源:檢查在Github此相關ticket

0

如果你需要傳遞一個上下文序列化器類。您可以使用Serializer's context

而且你將能夠使用它在SerializerMethodField

class MySerializer(serializer.Serializer) 

    field = serializer.SerializerMethodField() 

    def get_field(self, obj): 
     return self.context.get('my_key') 

你從視圖中調用它:

... 
s = MySerializer(data=data, context={'my_key': 'my_value'}) 
... 

編輯:

如果需要使用此上下文在另一個串行器類中傳遞給第一個串行器傳遞給nexted串行器:

# views.py 
... 
s = MySerializer(data=data, context={'my_key': 'my_value'}) 
... 

# serializers.py 

class MySerializer(serializer.Serializer): 
    field = serializer.SerializerMethodField() 

    def get_field(self, obj): 
     return MySecondSerializer(..., context=self.context) 
+0

這的確是一個解決方案,而之前我已經想通了(見鏈接),但SerializerMethodField不允許POST請求,它是隻讀。我如何寫一個嵌套字段的序列化程序*和*能夠將reuqest-context傳遞給它? – RvL

+0

我想你需要request.data,這是一個字典,你可以通過Serializer調用中的上下文attr來傳遞它。或者請稍微解釋一下。 – Gocht

+0

我更新了我的問題來描述我遇到的問題以及一些示例和代碼 – RvL

相關問題