Django Rest Framework: come aggiungere un campo personalizzato in ModelSerializer


89

Ho creato ModelSerializere desidero aggiungere un campo personalizzato che non fa parte del mio modello.

Ho trovato una descrizione per aggiungere campi extra qui e ho provato quanto segue:

customField = CharField(source='my_field')

Quando aggiungo questo campo e chiamo la mia validate()funzione, questo campo non fa parte del attrdict. attrcontiene tutti i campi del modello specificati tranne i campi aggiuntivi. Quindi non posso accedere a questo campo nella mia convalida sovrascritta, posso?

Quando aggiungo questo campo all'elenco dei campi in questo modo:

class Meta:
    model = Account
    fields = ('myfield1', 'myfield2', 'customField')

allora ricevo un errore perché customField non fa parte del mio modello - ciò che è corretto perché voglio aggiungerlo solo per questo serializzatore.

C'è un modo per aggiungere un campo personalizzato?


Potresti espandere su "Ma quando il mio campo non è nell'elenco dei campi del modello specificato nel serializzatore, non fa parte del dizionario attr-validate ().", Non sono sicuro che sia molto chiaro.
Tom Christie,

Inoltre "si lamenta - correttamente - che non ho un campo customField nel mio modello.", Potresti essere esplicito riguardo all'eccezione che stai vedendo - grazie! :)
Tom Christie

Ho aggiornato il mio post e spero che ora sia più chiaro. Voglio solo sapere come posso aggiungere un campo che non fa parte del mio modello ...
Ron


Risposte:


63

Stai facendo la cosa giusta, tranne quella CharField (e gli altri campi digitati) sono per i campi scrivibili.

In questo caso vuoi solo un semplice campo di sola lettura, quindi usa semplicemente:

customField = Field(source='get_absolute_url')

4
grazie, ma voglio un campo scrivibile. Passo un determinato token utente che identifica il mio utente. ma nel mio modello ho l'utente e non il token. quindi voglio passare il token e "convertirlo" in un oggetto utente tramite una query.
Ron

la prossima cosa è che la sorgente deve indirizzare un attributo del modello, giusto? nel mio caso non ho un attributo a cui puntare.
Ron

Non capisco la parte utente / token del commento. Ma se si desidera includere un campo nel serializzatore che viene rimosso prima del ripristino in un'istanza del modello, è possibile utilizzare il .validate()metodo per rimuovere l'attributo. Vedi: django-rest-framework.org/api-guide/serializers.html#validation Questo farebbe quello che ti serve, anche se non capisco davvero il caso d'uso, o perché vuoi un campo scrivibile che è legato a un proprietà di sola letturaget_absolute_url ?
Tom Christie,

dimentica il l' get_absolute_urlho appena copiato e incollato dai documenti. Voglio solo un normale campo scrivibile a cui posso accedere nel file validate(). Ho solo immaginato che avrei avuto bisogno sourcedell'attributo ...
Ron,

Questo ha più senso. :) Il valore dovrebbe essere passato per la convalida, quindi ricontrollerei come stai istanziando il serializzatore e se quel valore viene davvero fornito.
Tom Christie,

85

In effetti c'è una soluzione senza toccare affatto il modello. Puoi usare SerializerMethodFieldche ti permettono di collegare qualsiasi metodo al tuo serializzatore.

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

    def get_foo(self, obj):
        return "Foo id: %i" % obj.pk

6
Come OP menzionato in questo commento , vogliono un campo scrivibile, che SerializerMethodFieldnon lo è
esmail

14

... per chiarezza, se hai un Model Method 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',
        )

3
E se volessi che sia scrivibile?
Csaba Toth

1
@Csaba: ti basta al costume di scrittura e cancellazione Salva ganci per il contenuto aggiuntivo: Vedere "Salvare e cancellazione ganci" in "Metodi" ( Qui ) È necessario personalizzati scrittura perform_create(self, serializer), perform_update(self, serializer), perform_destroy(self, instance).
Lindauson

13

qui rispondi alla tua domanda. dovresti aggiungere al tuo account modello:

@property
def my_field(self):
    return None

ora puoi usare:

customField = CharField(source='my_field')

fonte: https://stackoverflow.com/a/18396622/3220916


6
Ho usato questo approccio quando ha senso, ma non è eccezionale aggiungere codice extra ai modelli per cose che sono realmente utilizzate solo per chiamate API specifiche.
Andy Baker

1
Puoi scrivere un modello proxy per il
frassino

10

Per mostrare self.author.full_name, ho ricevuto un errore con Field. Ha funzionato con ReadOnlyField:

class CommentSerializer(serializers.HyperlinkedModelSerializer):
    author_name = ReadOnlyField(source="author.full_name")
    class Meta:
        model = Comment
        fields = ('url', 'content', 'author_name', 'author')

6

Con l'ultima versione di Django Rest Framework, è necessario creare un metodo nel modello con il nome del campo che si desidera aggiungere.

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

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

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

4

Stavo cercando una soluzione per aggiungere un campo personalizzato scrivibile a un serializzatore di modelli. Ho trovato questo, che non è stato trattato nelle risposte a questa domanda.

Sembra che tu abbia davvero bisogno di scrivere il tuo semplice serializzatore.

class PassThroughSerializer(serializers.Field):
    def to_representation(self, instance):
        # This function is for the direction: Instance -> Dict
        # If you only need this, use a ReadOnlyField, or SerializerField
        return None

    def to_internal_value(self, data):
        # This function is for the direction: Dict -> Instance
        # Here you can manipulate the data if you need to.
        return data

Ora puoi usare questo serializzatore per aggiungere campi personalizzati a un ModelSerializer

class MyModelSerializer(serializers.ModelSerializer)
    my_custom_field = PassThroughSerializer()

    def create(self, validated_data):
        # now the key 'my_custom_field' is available in validated_data
        ...
        return instance

Funziona anche se il Modello MyModelha effettivamente una proprietà chiamata my_custom_fieldma vuoi ignorare i suoi validatori.


quindi non funziona se my_custom_field non è una proprietà di MyModel giusto? Ho ricevuto l'errore Il campo serializzatore potrebbe essere denominato in modo errato e non corrispondere ad alcun attributo o chiave MyModelnell'istanza.
Sandeep Balagopal

3

Dopo aver letto tutte le risposte qui, la mia conclusione è che è impossibile farlo in modo pulito. Devi giocare sporco e fare qualcosa di strano come creare un campo write_only e poi sovrascrivere i metodi validatee to_representation. Questo è ciò che ha funzionato per me:

class FooSerializer(ModelSerializer):

    foo = CharField(write_only=True)

    class Meta:
        model = Foo
        fields = ["foo", ...]

    def validate(self, data):
        foo = data.pop("foo", None)
        # Do what you want with your value
        return super().validate(data)

    def to_representation(self, instance):
        data = super().to_representation(instance)
        data["foo"] = whatever_you_want
        return data
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.