python - queryset - partial update django rest framework
Django Rest Framework-Leer datos anidados, escribir entero (3)
Puede crear un campo de serializador personalizado ( http://www.django-rest-framework.org/api-guide/fields )
El ejemplo tomó del enlace:
class ColourField(serializers.WritableField):
"""
Color objects are serialized into "rgb(#, #, #)" notation.
"""
def to_native(self, obj):
return "rgb(%d, %d, %d)" % (obj.red, obj.green, obj.blue)
def from_native(self, data):
data = data.strip(''rgb('').rstrip('')'')
red, green, blue = [int(col) for col in data.split('','')]
return Color(red, green, blue)
Luego use este campo en su clase de serializador.
Hasta el momento estoy muy contento con Django Rest Framework, por lo que casi no puedo creer que haya una omisión tan grande en la base de código. Con suerte, alguien sabe de qué manera apoyar esto:
class PinSerializer(serializers.ModelSerializer):
item = ItemSerializer(read_only=True, source=''item'')
item = serializers.IntegerSerializer(write_only=True)
class Meta:
model = Pin
con el objetivo
The goal here is to read:
{pin: item: {name: ''a'', url: ''b''}}
but to write using an id
{pin: item: 10}
Una alternativa sería usar dos serializadores, pero eso parece una solución realmente fea: serializadores de modelos django rest framework - lectura anidada, escritura plana
Si está utilizando DRF 3.0 puede implementar el nuevo método to_internal_value
para anular el campo del elemento para cambiarlo a PrimaryKeyRelatedField para permitir las escrituras planas. El to_internal_value
toma datos entrantes no validados como entrada y debe devolver los datos validados que estarán disponibles como serializer.validated_data
. Ver los documentos: http://www.django-rest-framework.org/api-guide/serializers/#to_internal_valueself-data
Entonces en tu caso sería:
class ItemSerializer(ModelSerializer):
class Meta:
model = Item
class PinSerializer(ModelSerializer):
item = ItemSerializer()
# override the nested item field to PrimareKeyRelatedField on writes
def to_internal_value(self, data):
self.fields[''item''] = serializers.PrimaryKeyRelatedField(queryset=Item.objects.all())
return super(PinSerializer, self).to_internal_value(data)
class Meta:
model = Pin
Dos cosas a tener en cuenta: la API web navegable seguirá pensando que las anotaciones se anidarán. No estoy seguro de cómo solucionarlo, pero solo uso la interfaz web para la depuración, así que no es un gran problema. Además, después de escribir, el artículo devuelto tendrá un artículo plano en lugar del anidado. Para solucionarlo, puede agregar este código para forzar que las lecturas usen siempre el serializador de elementos.
def to_representation(self, obj):
self.fields[''item''] = ItemSerializer()
return super(PinSerializer, self).to_representation(obj)
Obtuve la idea de esto a partir de la respuesta de Anton Dmitrievsky aquí: DRF: asignación de clave externa simple con serializadores anidados?
Asumiendo que está utilizando OneToOneField o ForeignKey para relacionar su Pin con su Elemento, Django almacena la relación como item_id
, pero a menudo abstrae el Ítem como item
. Puede aprovechar esto para evitar el hecho de que un objeto Python no puede tener dos atributos con el mismo nombre (un problema que encontraría en su código).
Simplemente agregue _id
al nombre de su atributo de escritura y cualquier escritura establecerá la relación subyacente, mientras que cualquier lectura usará el objeto abstraído. Tu código final será:
class PinSerializer(serializers.ModelSerializer):
item = ItemSerializer(read_only=True)
item_id = serializers.IntegerField(write_only=True)
class Meta:
model = Pin
Nota 1: También eliminé source=''item''
ya que era redundante y cambié los serializers.IntegerSerializer
a serializers.IntegerField
, ya que creo que debe haber sido un error tipográfico.
Nota 2: en realidad me parece bastante poco intuitivo que Django Rest esté configurado de manera que un serializador Pin sin un serializador de Item especificado devuelva el item_id como "item": <id>
y no "item_id": <id>
, pero eso está al lado el punto.