Django Rest Framework: Campos de modelo dynamic de serializador nesteds

He definido los serializadores como abajo. Estoy usando una mezcla para cambiar los campos de visualización sobre la marcha.

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',) 

Ahora lo que quiero hacer es pasar dinámicamente (sobre la marcha) todos los campos que se usarán desde SerializerTwo, como lo estoy haciendo para SerializerOne.

La forma en que lo estoy haciendo para SerializerOne es:

 # where fields=('status') SerializerOne(queryset, fields=fields) 

¿Hay alguna forma, mediante la cual pueda agregar campos de SerializerTwo a la inicialización de Serializer anterior?

 # 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) 

Después de tener el mismo problema, encontré una solución, espero que esto sea útil para algunas personas. Modifiqué DynamicFieldsModelSerializer como se define aquí

 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) 

Después de eso, puedes usarlo así:

 SerializerOne(queryset, nested={"another_field": ["name"]}) 

Puede modificar mi solución para usar el subrayado doble en lugar de otro kewyord con un dict, pero quería separar los campos regulares del serializador nested.

También se puede mejorar para ser recursivo, aquí solo estoy tratando con una profundidad de un serializador nested

EDITAR Modifiqué mi código para usar la syntax de subrayado doble después de todo:

 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) 

Puedes usarlo así:

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

Utilizo la siguiente forma de implementar los denominados 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 

y hacemos algunas modificaciones a 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) 

así que el último problema es cómo organizar la url, escribe la url GET esta manera:

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

La respuesta de @ Lotram no funciona en campos que devuelven valores múltiples (a través de many=True ).

El siguiente código mejora la solución de @Lotram que funciona en campos que devuelven múltiples valores:

 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)