我正在使用Django和REST框架。在序列化程序中,我想根据视图或请求(request.data['type']
)参数分配字段值,因此我需要在上下文中查看/请求。
我成功了,但只是以一种繁琐的方式,我正在研究简化代码的方法。这是成功的方法(省略不相关的字段):
class TypeDefault(object):
def set_context(self, serializer_field):
view = serializer_field.context['view'] # or context['request']
self.type = view.kwargs['type'].upper()
def __call__(self):
return self.type
class RRsetSerializer(serializers.ModelSerializer):
type = serializers.CharField(read_only=True, default=serializers.CreateOnlyDefault(TypeDefault()))
class Meta:
model = RRset
fields = ('type',)
read_only_fields = ('type',)
为简化起见,我尝试删除TypeDefault
类,并将<{1}}序列化程序字段替换为
type
但是, type = serializers.SerializerMethodField()
def get_type(self, obj):
return self.context.get('view').kwargs['type'].upper() # also tried self._context
会返回context.get('view')
。我不确定为什么视图上下文在这里不可用。我的印象是,应该可以在不诉诸额外课程的情况下获得所需的功能。
作为奖励,最好在字段声明中指定默认值,例如
None
但是,此处未定义 type = serializers.CharField(default=self.context.get('view').kwargs['type'].upper())
,我不确定正确的方法是什么。
此外,如果从视图或请求数据中检索信息有任何不同,我感兴趣。虽然上下文方法应该适用于两者,但是当从请求数据获取值时,可能有一种更简单的方法来获取self
功能,因为序列化程序无论如何都会处理请求数据。
编辑:根据Geotob的请求,以下是调用序列化程序的视图代码:
CreateOnlyDefault
在class RRsetsDetail(generics.ListCreateAPIView):
serializer_class = RRsetSerializer
# permission_classes = ... # some permission constraints
def get_queryset(self):
name = self.kwargs['name']
type = self.kwargs.get('type')
# Note in the following that the RRset model has a `domain` foreign-key field which is referenced here. It is irrelevant for the current problem though.
if type is not None:
return RRset.objects.filter(domain__name=name, domain__owner=self.request.user.pk, type=type)
else:
return RRset.objects.filter(domain__name=name, domain__owner=self.request.user.pk)
,我(其中包括):
urls.py
答案 0 :(得分:3)
SerializerMethodField
是一个只读字段,因此除非您设置默认值,否则我认为它不会起作用...并且您回到与CharField
相同的问题。
简单地说,你可以摆脱serializers.CreateOnlyDefault
:
class RRsetSerializer(serializers.ModelSerializer):
type = serializers.CharField(read_only=True, default=TypeDefault())
如果你想要更有活力的东西,我只能想到这样的东西:
class FromContext(object):
def __init__(self, value_fn):
self.value_fn = value_fn
def set_context(self, serializer_field):
self.value = self.value_fn(serializer_field.context)
def __call__(self):
return self.value
class RRsetSerializer(serializers.ModelSerializer):
type = serializers.CharField(read_only=True,
default=FromContext(lambda context: context.get('view').kwargs['type'].upper()))
FromContext
在实例化期间接受一个函数,用于从上下文中检索所需的值。
答案 1 :(得分:0)
总而言之,您的第二种方法是正确的:
使用serializers.SerializerMethodField
并从序列化程序方法访问self.context
:
class SomeSerializer(serializers.ModelSerializer):
type = serializers.SerializerMethodField()
def get_type(self, obj):
return self.context['view'].kwargs['type'].upper()
所有DRF通用视图(本节末尾的http://www.django-rest-framework.org/api-guide/generic-views/#methods)会自动将view
,request
和format
键添加到序列化程序上下文中。这很好用。
如果您手动创建序列化程序实例,则必须将context=contextDict
作为参数传递,其中contextDict
是您需要的任何内容(http://www.django-rest-framework.org/api-guide/serializers/#including-extra-context)。
正如@Michael在另一个答案中指出的那样,SerializerMethodField
将是只读的。但是按照你的第一个例子(type = serializers.CharField(read_only=True.....
),这似乎就是你想要的。