Cómo cambiar el nombre de campo en Django REST Framework

98

Estoy tratando de cambiar el nombre del campo del modelo en el serializador DRF como alias en SQL. He probado diferentes métodos pero no he tenido éxito.

modelos.py

class Park(models.Model):
    name = models.CharField(max_length=256)
    alternate_name = models.CharField(max_length=256, blank=True)
    objects = models.GeoManager()

    class Meta:
        db_table = u'p_park'

    def __unicode__(self):
        return '%s' % self.name

    def alias_alternate_name(self):
        return self.alternate_name

serializers.py

class ParkSerializer(serializers.ModelSerializer):

    location = serializers.Field(source='alias_alternate_name')
    #location = serializers.SerializerMethodField(source='alias_alternate_name')

    #alternate_name as location


    class Meta:
        model = Park
        fields = ('id', 'name', 'location')

También intenté agregar un alias en Django Queryset pero no puedo cambiar.

Actualizado

Esta es la excepción a la que me enfrento

AttributeError at / ViewName / 'module' object no tiene atributo 'Field'

¿Cómo puedo hacer esto?

Shoaib Ijaz
fuente
1
¿Está utilizando una implementación correcta del serializers.SerializerMethodFieldenfoque? Quiero decir esto: serializers.SerializerMethodField('get_location')ydef get_location(self, obj): ...
erthalion
¿Podemos ver las importaciones de serializers.py?
joerick
rechazará la pregunta porque OP aceptó una respuesta parcialmente incorrecta y confusa en lugar de las mejores a continuación ...
NeuronQ

Respuestas:

59

Puede utilizar serializers.SerializerMethodField:

Aquí está el modelo Park, que tiene campos de nombre y nombre_alternativo.

class Park(models.Model):
    name = models.CharField(max_length=256)
    alternate_name = models.CharField(max_length=256, blank=True)
    objects = models.GeoManager()

    class Meta:
        db_table = u'p_park'

    def __unicode__(self):
        return '%s' % self.name

Aquí está el serializador para el modelo Park, ParkSerializer. Esto cambia el nombre de alternate_name a location.

class ParkSerializer(serializers.ModelSerializer):
    location = serializers.SerializerMethodField('get_alternate_name')

    class Meta:
        model = Park
        fields = ('other_fields', 'location')

    def get_alternate_name(self, obj):
        return obj.alternate_name

Además, puede usar serializers.CharFieldcon sourceatributo:

class ParkSerializer(serializers.ModelSerializer):
    location = serializers.CharField(source='other_fields')

    class Meta:
        model = Park
        fields = ('other_fields', 'location')

La __notación de Django para atravesar la clave externa también funciona:

location = serializers.CharField(source='OtherModel__other_fields')

El mismo principio se aplica si desea cambiar el tipo de retorno en la API, para que pueda hacerlo serializers.DecimalField(source=...)y también otros tipos de campo.

Sin embargo, esto funcionaría solo para campos de solo lectura.

erthalion
fuente
Ahora, esta excepción arroja AttributeError en / ViewName / el objeto 'module' no tiene atributo 'SerializerMethodField'
Shoaib Ijaz
1
¿Cómo sería este entrenamiento con solicitudes de creación y edición?
iankit
1
Línea no 13 de 'Zen of Python': "Debe haber una, y preferiblemente solo una, forma obvia de hacerlo".
iankit
14
Esta no debería ser la respuesta aceptada. Vea el de abajo, que tiene casi 5 veces más votos a favor en el momento en que escribo esto.
cderwin
6
Ésta es una mala solución. Utilice el sourcekwarg en su lugar como se describe a continuación.
Patrick
215

Hay una característica muy buena en los campos de serializador y en los serializadores en general llamada 'fuente' donde puede especificar la fuente de datos del campo de modelo.

class ParkSerializer(serializers.ModelSerializer):
    location = serializers.SomeSerializerField(source='alternate_name')

    class Meta:
        model = Park
        fields = ('other_fields', 'location')

Donde serializers.SomeSerializerField pueden ser serializers.CharField como sugiere su modelo pero también pueden ser por cualquiera de los otros campos. También puede poner campos relacionales y otros serializadores en su lugar y esto aún funcionaría a la perfección. es decir, incluso si alternate_name fuera un campo de clave externa para otro modelo.

class ParkSerializer(serializers.ModelSerializer):
    locations = AlternateNameSerializer(source='alternate_name', many=true)

    class Meta:
        model = Park
        fields = ('other_fields', 'locations')

class AlternateNameSerializer(serializers.ModelSerialzer):
    class Meta:
        model = SomeModel

Esto también funciona con la creación, eliminación y modificación de tipos de solicitudes. Crea efectivamente una asignación uno a uno del nombre de campo en el serializador y el nombre de campo en modelos.

iankit
fuente
Estuve de acuerdo, ese sourcees un enfoque más general. Pero puede ver algunos intentos de usarlo en la pregunta, por lo que si desea responder de esa manera, también debe aclarar por qué el código original no funciona, ¿no es así?
erthalion
Su código funcionará bien ... siempre que la solicitud sea para listar y recuperar
iankit
Ambas respuestas están incompletas. En el caso de la clave externa, este método implica que al crear un nuevo Parque, debe dar el objeto principal completo (nombre_alternativo) como un dictado en su solicitud POST, lo cual es una locura ya que el objeto principal ya existe. Uno debería poder mencionar la instancia externa a través de su id.
stelios
En mi caso (clave externa) resolví este problema con locations = serializers.PrimaryKeyRelatedField(source='alternate_name', queryset=AlternateName.objects.all()). Al parecer, también RelatedFieldse puede utilizar.
stelios
@chefarov source = 'new_name' es un argumento genérico que puede dar a los campos del serializador, relaciones y otros serializadores relacionados, etc. No estoy seguro de por qué dice que la respuesta está incompleta.
iankit
15

Esto también funcionaría para operaciones de escritura

class ParkSerializer(serializers.ModelSerializer):
    location = serializers.CharField(source='alternate_name')

    class Meta:
        model = Park
        fields = ('id', 'name', 'location')
vijay
fuente