214 votes

Cadre REST de Django : ajout d'un champ supplémentaire à ModelSerializer

Je veux sérialiser un modèle, mais je veux inclure un champ supplémentaire qui nécessite d'effectuer des recherches dans la base de données sur l'instance du 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')

Quelle est la bonne façon de procéder ? Je vois que vous pouvez passer dans un "contexte" supplémentaire au sérialiseur, la bonne réponse consiste-t-elle à transmettre le champ supplémentaire dans un dictionnaire contextuel ?

Avec cette approche, la logique d'obtention du champ dont j'ai besoin ne serait pas contenue dans la définition du sérialiseur, ce qui est idéal puisque chaque instance sérialisée aura besoin de my_field . Ailleurs dans la documentation sur les sérialiseurs de la DRF, il est question de dit "Les champs supplémentaires peuvent correspondre à n'importe quelle propriété ou appelable du modèle". Est-ce que les "champs supplémentaires" sont ce dont je parle ?

Dois-je définir une fonction dans Foo La définition du modèle de l'entreprise qui renvoie my_field et, dans le sérialiseur, je relie mon_champ à cet appelant ? A quoi cela ressemble-t-il ?

Je serai heureux de clarifier la question si nécessaire.

317voto

J.P. Points 3056

Je pense 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

56voto

Vous pouvez changer la méthode de votre 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 du rest framework (j'ai essayé 3.3.3), vous n'avez pas besoin de changer la propriété. La méthode du modèle fonctionnera très bien.

18voto

Marco Silva Points 351

Si vous voulez lire et écrire sur votre champ supplémentaire, vous pouvez utiliser un nouveau serializer personnalisé, qui étend serializers.Serializer, 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']

Je l'utilise dans des champs imbriqués connexes avec une logique personnalisée.

16voto

guillaumevincent Points 633

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 voulez ajouter. Pas besoin de @property y source='field' soulève une erreur.

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

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

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

12voto

Lindauson Points 1041

Ma réponse à une question similaire ( aquí ) 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 ladite méthode à votre sérialiseur comme suit :

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

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

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X