Python Django Rest框架:嵌套序列化器动态模型字段
我定义了如下的序列化程序。我正在使用a动态更改显示字段Python Django Rest框架:嵌套序列化器动态模型字段,python,django,django-rest-framework,Python,Django,Django Rest Framework,我定义了如下的序列化程序。我正在使用a动态更改显示字段 class SerializerTwo(serializers.ModelSerializer): class Meta: model = Two fields = ('name', 'contact_number') class SerializerOne(DynamicFieldsModelSerializer, serializers.ModelSerializer): anothe
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所做的那样
我为Rone做这件事的方式是:
# where fields=('status')
SerializerOne(queryset, fields=fields)
是否有一种方法可以将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)
我使用以下方法实现所谓的
嵌套序列化器动态模型字段
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
class DynamicFieldsModelSerializer(serializers.ModelSerializer):
def __init__(self, *args, **kwargs):
super(DynamicFieldsModelSerializer, self).__init__(*args, **kwargs)
if 'request' not in self.context or not self.fields_filter_key:
return
fields = self.context['request'].query_params.get(self.fields_filter_key)
if fields:
fields = fields.split(',')
allowed = set(fields)
existing = set(self.fields.keys())
for field_name in existing - allowed:
self.fields.pop(field_name)
最后一个问题是如何组织url,像这样编写GET
url:
domain/something?一个字段=姓名、联系电话和两个字段=另一个字段
在遇到同样的问题后,我找到了一个解决方案,我希望这将对一些人有所帮助。我根据定义修改了DynamicFieldsModelSerializer
之后,您可以这样使用它:
SerializerOne(queryset, nested={"another_field": ["name"]})
SerializerOne(instance, fields=["another_field__name"])
您可以修改我的解决方案以使用双下划线,而不是另一个带有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)
@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)
类NestedDynamicFieldsModelSerializer(serializers.ModelSerializer):
定义初始化(self,*args,**kwargs):
def parse_嵌套_字段(字段):
字段\对象={“字段”:[]}
对于字段中的f:
obj=字段\对象
嵌套的_字段=f.split(“uu”)
对于嵌套_字段中的v:
如果v不在obj[“字段”]:
obj[“字段”]。追加(v)
如果嵌套_字段。索引(v)
i将fields=kwargs.pop('fields',None)
替换为kwargs.get('context').get('request')。如果是instance(kwargs.get('context'),dict),则查询参数.get('fields'),否则无
和fields=parse_嵌套_字段(fields)
替换为fields=parse_嵌套_字段(fields.split(','))
在之后,如果字段不是None:
块
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)