Agregue un campo que no sea modelo en un ModelSerializer en DRF 3

5 minutos de lectura

avatar de usuario
Prometeo

¿Cómo se agrega un campo que no es modelo en un ModelSerializer en DRF 3? es decir, agregar un campo que no existe en mi modelo real?

class TestSerializer(serializers.ModelSerializer):
    url = serializers.HyperlinkedIdentityField(view_name="vote_detail")
    non_field = serializers.CharField()  # no corresponding model property.


    class Meta:
        model = vote_model
        fields = ("url", "non_field")

    def create(self, validated_data):
      print(direction=validated_data['non_field'])

Pero DRF 3 me da el error:

Got AttributeError when attempting to get a value for field `non_field` on serializer `TestSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `Test` instance.
Original exception text was: 'Test' object has no attribute 'non_field'.

Busqué en la pila DRF – ModelSerializer con un campo write_only que no es modelo y encontré algunas soluciones, pero estas se refieren a DRF 2 donde estoy usando DRF 3. ¿Hay una solución para esto en esta versión?

avatar de usuario
AJ Parr

class MySerializer(serializers.ModelSerializer):
    write_only_char_field = serializers.CharField(write_only=True)
    write_only_list_char_field = serializers.ListField(child=serializers.CharField(max_length=100, default=""), write_only=True)
    empty_method_field = serializers.SerializerMethodField()
    read_only_custom_model_field = serializers.CharField(source="custom_property", read_only=True)

    def create(self, validated_data):
        validated_data.pop('write_only_char_field', None)
        validated_data.pop('write_only_list_char_field', None)
        return super().create(validated_data)

los serializers.CharField(write_only=True) y serializers.ListField(...) es una buena solución para proporcionar datos adicionales a su .create() y .update() métodos, ya sea como una sola cadena o como una lista de cadenas (puede mezclar ListField con otros tipos de campo de serializador).
Con este método, también puede definir def validate_write_only_char_field implementar alguna validación rápida y sencilla.

serializers.SerializerMethodField() le permite agregar algún campo personalizado de solo lectura a la salida de su serializador desde un método definido en el serializador.

los read_only_custom_model_field usaría un método en su modelo para leer algunos datos, no estrictamente un campo de modelo, sino un método personalizado. Es decir

class MyModel(models.Model):
    my_field = models.CharField(max_length=100)

    @property
    def custom_property(self):
        return "Perform calculations, combine with related models, etc. etc."

  • Esta es la respuesta más completa, perfecto~

    – Hola

    13 de septiembre de 2018 a las 3:15

  • Y el único que encontré (demasiado tarde) sobre escribir solo campos que no son modelo. Eventualmente podrías hacer estallar las llaves en el validate método, tal vez más apropiado

    –Guillaume Lebreton

    27 de septiembre de 2018 a las 7:51

  • Muchas gracias @ARJMP

    – Prakash Kumar

    24 de octubre de 2018 a las 10:19

  • write_only es exactamente lo que necesitaba. Gracias

    – inostia

    15 abr 2020 a las 21:47

  • Al usar el write_only solución que tuve que agregar el campo no modelo a fields. ¿Porqué es eso?

    – binny

    29 de noviembre de 2021 a las 5:52


avatar de usuario
chandu

class Foo(models.Model):
    . . .
    @property
    def my_field(self):
        return stuff
    . . .

Fuente:

Django REST Framework: agregar un campo adicional a ModelSerializer

  • Gracias, funciona, dado que lo agregamos en el fields lista.

    – Evidencia

    24 de enero de 2017 a las 18:42

avatar de usuario
chandu

Solo un ejemplo podría ayudarte.

  class ExtensibleModelSerializerOptions(serializers.SerializerOptions):
    """
    Meta class options for ModelSerializer
    """
    def __init__(self, meta):
        super(ExtensibleModelSerializerOptions, self).__init__(meta)
        self.model = getattr(meta, 'model', None)
        self.read_only_fields = getattr(meta, 'read_only_fields', ())
        self.non_native_fields = getattr(meta, 'non_native_fields', ())


class ExtensibleModelSerializer(serializers.ModelSerializer):

    _options_class = ExtensibleModelSerializerOptions

    def restore_object(self, attrs, instance=None):
        """
        Deserialize a dictionary of attributes into an object instance.
        You should override this method to control how deserialized objects
        are instantiated.
        """
        for field in self.opts.non_native_fields:
            attrs.pop(field)

        return super(ExtensibleModelSerializer, self).restore_object(attrs, instance)

Fuente:
https://github.com/tomchristie/django-rest-framework/issues/951

Como se mencionó, hay dos formas. (1) agregar una propiedad de modelo. (2) agregar un campo modelo. Siento que agregar una @propiedad al modelo se explicó bien en esta publicación. Si desea mantener sus modelos “delgados y eficientes”, use un campo Método. Sin embargo, la respuesta de Chandu omite algunos puntos cruciales:

class DeliveryItemSerializer(serializers.ModelSerializer):

    product_name = serializers.SerializerMethodField(read_only=True)

    def get_product_name(self, obj):
        return obj.product.name

    class Meta:
        model = DeliveryItem
        fields = (
            (...your field names),
            'product_name',)
  1. Establecer solo lectura
  2. El campo y el método correspondiente no tienen el mismo nombre. El nombre del método por defecto es get_nombre del campo. Si usa otro nombre use el method_name=method argumento de nombre en SerializerMethodField()

avatar de usuario
chandu

class TestSerializer(serializers.ModelSerializer):
    url = serializers.HyperlinkedIdentityField(view_name="vote_detail")
    non_field = serializers.SerializerMethodField()  # no corresponding model property.

    class Meta:
        model = vote_model
        fields = ("url", "non_field")

    def create(self, validated_data):
        print(direction=validated_data['non_field'])

http://www.django-rest-framework.org/api-guide/fields/#serializermethodfield

o pasa por este enlace

  • non_field = serializers.serializers.SerializerMethodField() # sin propiedad de modelo correspondiente: elimine los serializadores adicionales. ?

    – Liz

    23 de febrero de 2016 a las 23:11

  • Estoy tratando de hacer exactamente lo mismo pero tengo un error que me dice que mi modelo no tiene este campo. ¿Alguien encontró una solución?

    – ben

    22 de noviembre de 2016 a las 16:31

  • Esta respuesta es incorrecta. Lo primero que dice en el enlace de arriba, django-rest-framework.org/api-guide/fields/…es que SerializerMethodField es un campo de solo lectura.

    – kylebebak

    4 de junio de 2019 a las 8:01

  • non_field = serializers.serializers.SerializerMethodField() # sin propiedad de modelo correspondiente: elimine los serializadores adicionales. ?

    – Liz

    23 de febrero de 2016 a las 23:11

  • Estoy tratando de hacer exactamente lo mismo pero tengo un error que me dice que mi modelo no tiene este campo. ¿Alguien encontró una solución?

    – ben

    22 de noviembre de 2016 a las 16:31

  • Esta respuesta es incorrecta. Lo primero que dice en el enlace de arriba, django-rest-framework.org/api-guide/fields/…es que SerializerMethodField es un campo de solo lectura.

    – kylebebak

    4 de junio de 2019 a las 8:01

¿Ha sido útil esta solución?