我想在Django REST框架中将请求上下文添加到我的序列化程序中。特别是对于嵌套序列化程序,我(成功)尝试使用SerializerMethodField(作为我的解决方案:context in nested serializers django rest framework)。这是我使用的设置:
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) 但是现在我因为SerializerMethodField是只读的而无法处理POST。我无法通过以下方式将对象发布到/ api / v1 / vehicle:
{
"vehiclename": "test",
"vehicledescription": "test"
"vehicletype": "1" <---- get's ignored since SerializerMethodField is read-only
}
问题:有人能指出我正确的方向将请求上下文(尤其是用户信息)添加到我可以写入的嵌套序列化程序中吗?
我需要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
答案 0 :(得分: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__
样式应该是首选。
因此,更好的选择是覆盖__init__()
的{{1}}方法,并将ParentSerializer
传递给子/嵌套序列化程序。
(来源:在Github上查看此相关的ticket。)
答案 1 :(得分:0)
如果需要将上下文传递给Serializer类。您可以使用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'})
...
编辑:
如果您需要在另一个Serializer类中使用此上下文,请传递给传递给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)