web-dev-qa-db-fra.com

Django REST Framework): ajout d'un champ supplémentaire à ModelSerializer

Je souhaite sérialiser un modèle, mais inclure un champ supplémentaire nécessitant des recherches dans la base de données sur l'instance de modèle à sérialiser:

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

Quel est le bon moyen de le faire? Je vois que vous pouvez passer en extra "contexte" au sérialiseur, est-ce la bonne réponse à transmettre dans le champ supplémentaire d'un dictionnaire de contexte? Avec cette approche, la logique d’obtention du champ dont j’ai besoin ne serait pas autonome avec la définition du sérialiseur, ce qui est idéal car chaque instance sérialisée aura besoin de my_field. Ailleurs dans la documentation sur les sérialiseurs DRF, il dit "Des champs supplémentaires peuvent correspondre à toute propriété ou appelable sur le modèle". Est-ce que les champs supplémentaires sont ce dont je parle? Devrais-je définir une fonction dans la définition de modèle de Foo qui retourne my_field valeur, et dans le sérialiseur je raccorde my_field à cet appelable? A quoi cela ressemble-t-il?

Merci d'avance, heureux de clarifier la question si nécessaire.

115
Neil

Je pense que SerializerMethodField est ce que vous recherchez:

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

191
J.P.

Vous pouvez modifier votre méthode de modèle en propriété et l'utiliser dans le sérialiseur avec cette approche.

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',)

Edit: Avec les versions récentes de rest framework (j'ai essayé la version 3.3.3), vous n'avez pas besoin de changer de propriété. La méthode modèle fonctionnera parfaitement.

34
Wasil Sergejczyk

Avec la dernière version de Django Rest Framework, vous devez créer une méthode dans votre modèle avec le nom du champ que vous souhaitez ajouter. Pas besoin de @property et source='field' déclenche une erreur.

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

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

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

Ma réponse à une question similaire ( ici ) pourrait être utile.

Si vous avez une méthode de modèle définie de la manière suivante:

class MyModel(models.Model):
    ...

    def model_method(self):
        return "some_calculated_result"

Vous pouvez ajouter le résultat de l'appel de cette méthode à votre sérialiseur de la manière suivante:

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

p.s. Comme le champ personnalisé n'est pas vraiment un champ dans votre modèle, vous souhaiterez généralement le rendre en lecture seule, comme suit:

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

si vous voulez lire et écrire sur votre champ supplémentaire, vous pouvez utiliser un nouveau sérialiseur personnalisé, qui étend les sérialiseurs.Sérialiseur, et l'utiliser comme ceci

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']

j'utilise ceci dans des champs imbriqués liés avec une logique personnalisée

6
Marco Silva

Cela a fonctionné pour moi .Si nous voulons simplement ajouter un champ supplémentaire dans ModelSerializer, nous pouvons le faire comme ci-dessous et aussi le champ peut être assigné un peu après quelques calculs de recherche. Ou Dans certains cas, si nous voulons envoyer les paramètres dans la réponse de l'API.

Dans model.py

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



   **

Dans 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 ')

**

J'espère que cela pourrait aider quelqu'un

**

1
Vinay Kumar

Comme Chemical Programer dit dans ce commentaire , dans le dernier DRF, vous pouvez simplement le faire comme ceci:

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', ...)

source de la documentation DRF

0
trici0pa