Skip to main content
 首页 » 编程设计

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

2024年08月20日3052php

我将 Django 与 REST 框架结合使用。在序列化程序中,我想根据 View 或请求 (request.data['type']) 参数分配字段值,因此我需要上下文中的 View /请求。

我成功了,但方法很麻烦,我正在研究简化代码的方法。这是成功的方法(省略不相关的字段):

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 类,并将 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。我不确定为什么 View 上下文在这里不可用。我的印象是应该可以在不求助于额外类的情况下获得所需的功能。

作为奖励,最好在字段声明中指定默认值,例如

    type = serializers.CharField(default=self.context.get('view').kwargs['type'].upper()) 

但是,self 没有在这里定义,我不确定正确的方法是什么。


此外,如果从 View 或请求数据中检索信息有任何差异,我也很感兴趣。虽然上下文方法应该适用于两者,但当从请求数据中获取值时,也许有一种更简单的方法来获取 CreateOnlyDefault 功能,因为序列化程序无论如何都会处理请求数据。


编辑:根据 Geotob 的要求,这里是调用序列化程序的 View 代码:

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 中,我有(除其他外):

url(r'^domains/(?P<name>[a-zA-Z\.\-_0-9]+)/rrsets/$', RRsetsDetail.as_view(), name='rrsets'), 
url(r'^domains/(?P<name>[a-zA-Z\.\-_0-9]+)/rrsets/(?P<type>[A-Z]+)/$', RRsetsDetail.as_view(), name='rrsets-type'), 

请您参考如下方法:

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 在实例化期间采用一个函数,该函数将用于从上下文中检索您想要的值。