Django-Rest框架:嵌套序列化程序动态模型字段

2024-10-01 22:30:18 发布

您现在位置:Python中文网/ 问答频道 /正文

我定义了下面这样的序列化程序。我正在使用mixin动态更改显示字段。在

class SerializerTwo(serializers.ModelSerializer):

    class Meta:
        model = Two
        fields = ('name', 'contact_number')

class SerializerOne(DynamicFieldsModelSerializer, serializers.ModelSerializer):
    another_field = SerializerTwo()

    class Meta:
        lookup_field = 'uuid'
        model = One
        fields = ('status', 'another_field',)

现在我想做的是,动态传递(动态)serializerwo中所有字段将被使用的内容,就像我为SerializerOne所做的那样。在

我为SerializerOne做这件事的方式是:

^{pr2}$

有没有一种方法,可以用来将serializerwo中的字段添加到上面的序列化程序初始化中。在

# where fields=('status', 'name') name from SerializerTwo
# the double underscore notation does not work here for fields, so another_field__name cannot be used as well
SerializerOne(queryset, fields=fields)

Tags: name程序fieldfieldsmodel序列化statusanother
3条回答

我使用以下方法来实现所谓的Nested Serializer Dynamic Model Fields。在

class SerializerTwo(serializers.ModelSerializer):
    fields_filter_key = 'two_fields'
    class Meta:
        model = Two
        fields = ('name', 'contact_number')

class SerializerOne(DynamicFieldsModelSerializer, serializers.ModelSerializer):
    fields_filter_key = 'one_fields'
    another_field = serializers.SerializerMethodField()

    class Meta:
        lookup_field = 'uuid'
        model = One
        fields = ('status', 'another_field',)

    def get_another_field(self, obj):
        another_filed_serializer = SerializerTwo(obj.another_field, 
                                                 context=self.context)
        return another_filed_serializer.data 

我们对DynamicFieldsModelSerializer做了一些修改

^{pr2}$

所以最后一个问题是如何组织url,将GETurl写成这样:

domain/something?one_fields=name,contact_number&two_fields=another_field

@Lotram的答案对返回多个值的字段无效(通过many=True)。在

以下代码改进了@Lotram的解决方案,该方案适用于返回多个值的字段:

class NestedDynamicFieldsModelSerializer(serializers.ModelSerializer):

def __init__(self, *args, **kwargs):

    def parse_nested_fields(fields):
        field_object = {"fields": []}
        for f in fields:
            obj = field_object
            nested_fields = f.split("__")
            for v in nested_fields:
                if v not in obj["fields"]:
                    obj["fields"].append(v)
                if nested_fields.index(v) < len(nested_fields) - 1:
                    obj[v] = obj.get(v, {"fields": []})
                    obj = obj[v]
        return field_object

    def select_nested_fields(serializer, fields):
        for k in fields:
            if k == "fields":
                fields_to_include(serializer, fields[k])
            else:
                select_nested_fields(serializer.fields[k], fields[k])

    def fields_to_include(serializer, fields):
        # Drop any fields that are not specified in the `fields` argument.
        allowed = set(fields)
        if isinstance(serializer, serializers.ListSerializer):
            existing = set(serializer.child.fields.keys())
            for field_name in existing - allowed:
                serializer.child.fields.pop(field_name)
        else:
            existing = set(serializer.fields.keys())
            for field_name in existing - allowed:
                serializer.fields.pop(field_name)

    # Don't pass the 'fields' arg up to the superclass
    fields = kwargs.pop('fields', None)
    # Instantiate the superclass normally
    super(NestedDynamicFieldsModelSerializer, self).__init__(*args, **kwargs)

    if fields is not None:
        # import pdb; pdb.set_trace()
        fields = parse_nested_fields(fields)
        # Drop any fields that are not specified in the `fields` argument.
        select_nested_fields(self, fields)

在遇到同样的问题后,我找到了一个解决办法,希望这能对一些人有所帮助。我修改了定义为here的DynamicFieldsModelSerializer

def __init__(self, *args, **kwargs):
# Don't pass the 'fields' arg up to the superclass
fields = kwargs.pop('fields', None)
nested = kwargs.pop('nested', None)
# Instantiate the superclass normally
super(DynamicFieldsModelSerializer, self).__init__(*args, **kwargs)

if fields is not None:
    # Drop any fields that are not specified in the `fields` argument.
    allowed = set(fields)
    existing = set(self.fields.keys())
    for field_name in existing - allowed:
        self.fields.pop(field_name)

if nested is not None:
    for serializer in nested:
        try:
            nested_serializer = self.fields[serializer]
        except:
            logger.warning("Wrong nested serializer name")
            continue

        allowed = set(nested[serializer])
        existing = set(nested_serializer.fields.keys())
        for field_name in existing - allowed:
            nested_serializer.fields.pop(field_name)

之后,您可以这样使用它:

^{pr2}$

您可以修改我的解决方案以使用双下划线,而不是另一个带有dict的kewyord,但我希望将常规字段与嵌套序列化程序分开。在

它也可以改进为递归的,这里我只处理一个嵌套序列化器的深度

编辑 我修改了代码,使用了双下划线语法:

def __init__(self, *args, **kwargs):

    def parse_nested_fields(fields):
        field_object = {"fields": []}
        for f in fields:
            obj = field_object
            nested_fields = f.split("__")
            for v in nested_fields:
                if v not in obj["fields"]:
                    obj["fields"].append(v)
                if nested_fields.index(v) < len(nested_fields) - 1:
                    obj[v] = obj.get(v, {"fields": []})
                    obj = obj[v]
        return field_object

    def select_nested_fields(serializer, fields):
        for k in fields:
            if k == "fields":
                fields_to_include(serializer, fields[k])
            else:
                select_nested_fields(serializer.fields[k], fields[k])

    def fields_to_include(serializer, fields):
        # Drop any fields that are not specified in the `fields` argument.
        allowed = set(fields)
        existing = set(serializer.fields.keys())
        for field_name in existing - allowed:
            serializer.fields.pop(field_name)

    # Don't pass the 'fields' arg up to the superclass
    fields = kwargs.pop('fields', None)
    # Instantiate the superclass normally
    super(DynamicFieldsModelSerializer, self).__init__(*args, **kwargs)

    if fields is not None:
        fields = parse_nested_fields(fields)
        # Drop any fields that are not specified in the `fields` argument.
        select_nested_fields(self, fields)

然后您可以这样使用它:

SerializerOne(instance, fields=["another_field__name"])

相关问题 更多 >

    热门问题