Skip to main content
 首页 » 编程设计

python之在 Django Rest Framework 中列出具有动态字段的序列化程序

2024年09月07日33TianFang

我正在尝试通过覆盖 __init__ 方法将字段动态添加到 Django Rest Framework 的序列化程序。该方法类似于此处描述的方法:http://masnun.com/2015/10/21/django-rest-framework-dynamic-fields-in-serializers.html

我这样做的原因是,因为我想将字段类型动态更改为一个类型,由要序列化的实例的属性 type_ 确定。如果我一次序列化一个实例,这非常有效:

from rest_framework import serializers 
from rest_framework.fields import empty 
 
 
class VariableDetails: 
    def __init__(self, name, type_, value): 
        self.name = name 
        self.type_ = type_ 
        self.value = value 
 
 
class VariableDetailSerializer(serializers.Serializer): 
    TYPE_FIELD_MAP = { 
        'string': serializers.CharField, 
        'integer': serializers.IntegerField, 
        'float': serializers.FloatField, 
    } 
 
    name = serializers.CharField() 
    type_ = serializers.CharField() 
 
    def __init__(self, instance=None, data=empty, **kwargs): 
        # this is where the magic happens 
        super().__init__(instance, data, **kwargs) 
        if instance is not None: 
            field_type = self.TYPE_FIELD_MAP[instance.type_] 
            self.fields['value'] = field_type() 
 
string_details = VariableDetails('character value', 'string', 'hello world') 
integer_details = VariableDetails('integer value', 'integer', 123) 
 
print(VariableDetailSerializer(string_details).data) 
# {'name': 'character value', 'type_': 'string', 'value': 'hello world'} 
 
print(VariableDetailSerializer(integer_details).data) 
# {'name': 'integer value', 'type_': 'integer', 'value': 123} 

如果我想序列化与父实例相关的 VariableDetails 的多个实例(例如将其称为 Parent),则缺少值字段:

class Parent: 
    def __init__(self, variable_details): 
        self.variable_details = variable_details 
 
 
class ParentSerializer(serializers.Serializer): 
    variable_details = VariableDetailSerializer(many=True) 
 
 
parent = Parent(variable_details=[string_details, integer_details]) 
print(ParentSerializer(parent).data) 
# { 
#     'variable_details': [ 
#         { 
#             'name': 'character_value', 
#             'type_': 'string' 
#             # value is missing 
#         }, 
#         { 
#             'name': 'integer_value', 
#             'type_': 'integer' 
#         }, 
#     ] 
# } 

显然 VariableDetailSerializer.__init__ 仅在创建 ParentSerializer 期间调用一次,并且在初始化 ParentSerializer 的新实例时调用一次。在这两种情况下,instance 都是 None。因此,不会为每个 VariableDetails 调用它。

有人知道如何将字段动态添加到同时序列化列表实例的 Serializer 吗?

为了方便运行代码,我创建了一个要点:https://gist.github.com/larsrinn/861f8d50bf5bb0626d73321b546d8cb3 如果安装了 Django Rest Framework,代码应该可以复制并粘贴到 python repl 中。但是,您不需要创建 Django 项目

请您参考如下方法:

因为 VariableDetailSerializer 嵌套在 ParentSerializer 中,它的行为就像一个字段本身。在这种情况下,您可以覆盖 to_representation() 方法来执行动态类型切换。

def to_representation(self, instance): 
    field_type = self.TYPE_FIELD_MAP[instance.type_] 
    self.fields['value'] = field_type() 
    try: 
        del self._readable_fields  # Clear the cache 
    except AttributeError: 
        pass 
    return super().to_representation(instance) 

这里的主要问题是序列化程序缓存了字段,因为它不希望它们在实例之间发生变化。因此,有必要使用 del self._readable_fields 清除缓存。

当我将以上内容添加到 VariableDetailSerializer 并运行您的示例时,我得到:

{ 
    'variable_details': [ 
        OrderedDict([('name', u'character value'),  
                     ('type_', u'string'),  
                     ('value', u'hello world')]),  
        OrderedDict([('name', u'integer value'),  
                     ('type_', u'integer'),  
                     ('value', 123)]) 
    ] 
}