在Django序列化程序中,从上下文(或请求数据)中获取默认字段值

时间:2017-05-10 14:50:44

标签: python django serialization django-rest-framework

我正在使用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

2 个答案:

答案 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)会自动将viewrequestformat键添加到序列化程序上下文中。这很好用。

如果您手动创建序列化程序实例,则必须将context=contextDict作为参数传递,其中contextDict是您需要的任何内容(http://www.django-rest-framework.org/api-guide/serializers/#including-extra-context)。

正如@Michael在另一个答案中指出的那样,SerializerMethodField将是只读的。但是按照你的第一个例子(type = serializers.CharField(read_only=True.....),这似乎就是你想要的。