Django REST Framework: aggiunta di un campo aggiuntivo a ModelSerializer


149

Voglio serializzare un modello, ma voglio includere un campo aggiuntivo che richiede la serializzazione di alcune ricerche di database sull'istanza del modello:

class FooSerializer(serializers.ModelSerializer):
  my_field = ... # result of some database queries on the input Foo object
  class Meta:
        model = Foo
        fields = ('id', 'name', 'myfield')

Qual è il modo giusto per farlo? Vedo che puoi passare un ulteriore "contesto" al serializzatore, è la risposta giusta per passare nel campo aggiuntivo in un dizionario di contesto? Con questo approccio, la logica di ottenere il campo di cui ho bisogno non sarebbe autonoma con la definizione di serializzatore, che è l'ideale poiché ogni istanza serializzata avrà bisogno my_field. Altrove nella documentazione dei serializzatori DRF afferma che "i campi extra possono corrispondere a qualsiasi proprietà o richiamo sul modello". Sono campi extra di cosa sto parlando? Devo definire una funzione nella Foodefinizione del modello che restituisce my_fieldvalore e nel serializzatore collego my_field a quel callable? Che aspetto ha?

Grazie in anticipo, felice di chiarire la domanda, se necessario.

Risposte:


226

Penso che SerializerMethodField sia quello che stai cercando:

class FooSerializer(serializers.ModelSerializer):
  my_field = serializers.SerializerMethodField('is_named_bar')

  def is_named_bar(self, foo):
      return foo.name == "bar" 

  class Meta:
    model = Foo
    fields = ('id', 'name', 'my_field')

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


19
è possibile aggiungere la convalida a tali campi? la mia domanda è: come accettare valori POST personalizzati che possono essere validati ed elaborati nel gestore post_save ()?
Alp

20
Si noti che SerializerMethodField è di sola lettura, quindi non funzionerà con POST / PUT / PATCH in arrivo.
Scott A

24
In DRF 3, viene modificato in field_name = serializers.SerializerMethodField()edef get_field_name(self, obj):
Programmatore chimico il

1
qual è il foomomento in cui un SerializerMethodField? quando si utilizza CreateAPIView, il foo è stato archiviato, quindi è possibile utilizzare il metodo is_named_bar ()?
244boy,

Quindi, in base a questa risposta, se vuoi passare diciamo 12 campi extra al tuo serializzatore, devi definire 12 metodi specifici per ogni campo che restituisce solo foo.field_custom?
AlxVallejo,

41

È possibile modificare il metodo del modello in proprietà e utilizzarlo nel serializzatore con questo approccio.

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

class FooSerializer(ModelSerializer):
    my_field = serializers.ReadOnlyField(source='my_field')

    class Meta:
        model = Foo
        fields = ('my_field',)

Modifica: con le versioni recenti di Rest Framework (ho provato 3.3.3), non è necessario modificare la proprietà. Il metodo del modello funzionerà perfettamente.


Grazie @Wasil! Non ho familiarità con l'uso delle proprietà nei modelli Django e non riesco a trovare una buona spiegazione di cosa significhi. Puoi spiegare? Qual è il punto del decoratore @property qui?
Neil,

2
significa che puoi chiamare questo metodo come una proprietà: cioè variable = model_instance.my_fielddà lo stesso risultato che variable = model_instance.my_field()senza il decoratore. anche: stackoverflow.com/a/6618176/2198571
Wasil Sergejczyk

1
Questo non funziona, almeno in Django 1.5.1 / djangorestframework == 2.3.10. ModelSerializer non sta ottenendo il valore corretto anche se esplicitamente indicato nell'attributo Meta "field".
ygneo,

9
devi aggiungere il campo al serializzatore perché non è un vero modelfield: my_field = serializers.Field (source = 'my_field')
Marius

2
source='my_field' non è più necessario e sollevare un'eccezione
Guillaume Vincent,

13

Con l'ultima versione di Django Rest Framework, è necessario creare un metodo nel modello con il nome del campo che si desidera aggiungere. Non è necessario @propertye source='field'genera un errore.

class Foo(models.Model):
    . . .
    def foo(self):
        return 'stuff'
    . . .

class FooSerializer(ModelSerializer):
    foo = serializers.ReadOnlyField()

    class Meta:
        model = Foo
        fields = ('foo',)

e se volessi avere un requestoggetto all'interno di def foo (self) che potesse modificare il valore di foo? (esempio una ricerca basata su request.user)
saran3h

1
Cosa succede se il valore di foo proviene dalla richiesta?
saran3h,

11

La mia risposta a una domanda simile ( qui ) potrebbe essere utile.

Se si dispone di un metodo modello definito nel modo seguente:

class MyModel(models.Model):
    ...

    def model_method(self):
        return "some_calculated_result"

Puoi aggiungere il risultato della chiamata di detto metodo al tuo serializzatore in questo modo:

class MyModelSerializer(serializers.ModelSerializer):
    model_method_field = serializers.CharField(source='model_method')

ps Poiché il campo personalizzato non è realmente un campo nel tuo modello, di solito vorrai renderlo di sola lettura, in questo modo:

class Meta:
    model = MyModel
    read_only_fields = (
        'model_method_field',
        )

10

se vuoi leggere e scrivere sul tuo campo extra, puoi usare un nuovo serializzatore personalizzato, che estende i serializzatori. Serializzatore, e usalo in questo modo

class ExtraFieldSerializer(serializers.Serializer):
    def to_representation(self, instance): 
        # this would have the same as body as in a SerializerMethodField
        return 'my logic here'

    def to_internal_value(self, data):
        # This must return a dictionary that will be used to
        # update the caller's validation data, i.e. if the result
        # produced should just be set back into the field that this
        # serializer is set to, return the following:
        return {
          self.field_name: 'Any python object made with data: %s' % data
        }

class MyModelSerializer(serializers.ModelSerializer):
    my_extra_field = ExtraFieldSerializer(source='*')

    class Meta:
        model = MyModel
        fields = ['id', 'my_extra_field']

lo uso in campi nidificati correlati con una logica personalizzata


2

Questo ha funzionato per me . Se vogliamo solo aggiungere un campo aggiuntivo inModelSerializer, possiamo farlo come sotto, e anche al campo può essere assegnato un po 'di val dopo alcuni calcoli di ricerca. O in alcuni casi, se vogliamo inviare i parametri nella risposta API.

In model.py

class Foo(models.Model):
    """Model Foo"""
    name = models.CharField(max_length=30, help_text="Customer Name")

In serializer.py

class FooSerializer(serializers.ModelSerializer):
    retrieved_time = serializers.SerializerMethodField()
    
    @classmethod
    def get_retrieved_time(self, object):
        """getter method to add field retrieved_time"""
        return None

  class Meta:
        model = Foo
        fields = ('id', 'name', 'retrieved_time ')

Spero che questo possa aiutare qualcuno.


0

Come ha detto il programmatore chimico in questo commento , nell'ultimo DRF puoi semplicemente farlo in questo modo:

class FooSerializer(serializers.ModelSerializer):
    extra_field = serializers.SerializerMethodField()

    def get_extra_field(self, foo_instance):
        return foo_instance.a + foo_instance.b

    class Meta:
        model = Foo
        fields = ('extra_field', ...)

Fonte dei documenti DRF

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.