Django REST Framework-Sérializing optional fields

j'ai un objet qui a des champs facultatifs. J'ai défini ma sérialiseur de cette façon:

class ProductSerializer(serializers.Serializer):
    code = serializers.Field(source="Code")
    classification = serializers.CharField(source="Classification", required=False)

I penséerequired=False ferait le travail de contourner le champ s'il n'existe pas. Cependant, il est mentionné dans la documentation que cela affecte la desérialisation plutôt que la sérialisation.

j'obtiens l'erreur suivante:

'Product' object has no attribute 'Classification'

ce Qui se passe lorsque j'essaie d'accéder à .data de l'instance sérialisée. (N'est-ce pas dire c'est la desérialisation qui soulève ça?)

cela se produit pour les instances qui n'ont pas Classification. Si je omettre Classification de la classe serializer il fonctionne très bien.

Comment faire correctement? Sérialiser un objet avec des champs optionnels, ce qui est.

23
demandé sur ferrangb 2013-07-09 19:36:14

5 réponses

les sérialiseurs sont délibérément conçus pour utiliser un ensemble fixe de champs de sorte que vous ne seriez pas facilement en mesure de laisser tomber une des clés en option.

vous pourriez utiliser un SerializerMethodField pour retourner à la valeur du champ ou None si le champ n'existe pas, ou si vous ne pouvez pas utiliser de sérialiseurs du tout et simplement écrire une vue qui renvoie la réponse directement.

mise à Jour de REPOS framework 3.0serializer.fields peut être modifié sur un instancié sérialiseur. Lorsque des classes serializer dynamiques sont nécessaires, je suggérerais probablement de modifier les champs dans un méthode.

10
répondu Tom Christie 2015-10-08 20:22:09

Django REST Framework 3.0+

Les champs dynamiques sont maintenant pris en charge, voir http://www.django-rest-framework.org/api-guide/serializers/#dynamically-modifying-fields -- cette approche définit tous les champs dans le serializer, et vous permet ensuite de supprimer sélectivement ceux que vous ne voulez pas.

ou vous pouvez aussi faire quelque chose comme ça pour un Serialiseur modèle, où vous vous amusez avec Meta.les champs dans le sérialiseur initialisation:

class ProductSerializer(serializers.ModelSerializer):
    class Meta:
        model = Product
        fields = ('code',)

    def __init__(self, *args, **kwargs):
        if SHOW_CLASSIFICATION: # add logic here for optional viewing
            self.Meta.fields = list(self.Meta.fields)
            self.Meta.fields.append('classification')
        super(ProductSerializer, self).__init__(*args, **kwargs)

vous devriez demander à Tom cependant si c'est la "bonne façon" puisque cela peut ne pas correspondre avec le plan à long terme.

Django REST Framework < 3.0

Essayez quelque chose comme ceci:

class ProductSerializer(serializers.Serializer):
    ...
    classification = serializers.SerializerMethodField('get_classification')

    def get_classification(self, obj):
        return getattr(obj, 'classification', None)

Sérialiseurs Multiples

une Autre approche serait de créer plusieurs sérialiseurs avec différents ensembles de champs. Un sérialiseur hérite d'une autre, et ajoute des champs supplémentaires. Ensuite, vous pouvez choisissez le sérialiseur dans la vue avec le get_serializer_class méthode. Voici un exemple concret de la façon dont j'utilise cette approche pour appeler des sérialiseurs différents pour présenter des données utilisateur différentes si l'objet utilisateur est le même que l'utilisateur de la requête.

def get_serializer_class(self):
    """ An authenticated user looking at their own user object gets more data """
    if self.get_object() == self.request.user:
        return SelfUserSerializer
    return UserSerializer

Supprimer des champs de la représentation

une Autre approche que j'ai utilisée dans les contextes de sécurité est de supprimer des champs dans le to_representation méthode. Définissez une méthode comme

def remove_fields_from_representation(self, representation, remove_fields):
    """ Removes fields from representation of instance.  Call from
    .to_representation() to apply field-level security.
    * remove_fields: a list of fields to remove
    """
    for remove_field in remove_fields:
        try:
            representation.pop(remove_field)
        except KeyError:
            # Ignore missing key -- a child serializer could inherit a "to_representation" method
            # from its parent serializer that applies security to a field not present on
            # the child serializer.
            pass

puis, dans votre sérialiseur, appelez la méthode

def to_representation(self, instance):
    """ Apply field level security by removing fields for unauthorized users"""
    representation = super(ProductSerializer, self).to_representation(instance)
    if not permission_granted: # REPLACE WITH PERMISSION LOGIC
        remove_fields = ('classification', ) 
        self.remove_fields_from_representation(representation, remove_fields)
    return representation

cette approche est simple et flexible, mais elle est au prix de sérialiser des champs qui ne sont parfois pas affichés. Mais c'est probablement correct.

18
répondu Mark Chackerian 2016-11-08 22:28:31

La méthode décrit ci-dessous a fait le travail pour moi. Assez simple, facile et ça a marché pour moi.

DRF version used = djangorestfram Framework (3.1.0)

class test(serializers.Serializer):
  id= serializers.IntegerField()
  name=serializers.CharField(required=False,default='some_default_value')
2
répondu RAJ GUPTA 2018-02-23 07:35:41

"c'est un terrible hack en s'appuyant sur des détails de mise en œuvre de deux DRF et Django, mais il fonctionne (au moins pour l'instant) de fichiers", voici la démarche que j'ai utilisée ajoutent quelques données de débogage dans la réponse de "créer" la méthode de la mise en œuvre sur un sérialiseur:

def create(self, validated_data)
    # Actual model instance creation happens here...
    self.fields["debug_info"] = serializers.DictField(read_only=True)
    my_model.debug_info = extra_data
    return my_model

il s'agit d'une approche temporaire qui me permet d'utiliser L'API browsable pour afficher certaines des données brutes de réponse reçues d'un service distant particulier au cours du processus de création. Dans le futur, je suis enclin à garder cette capacité, mais la cacher derrière un drapeau "report debugging info" dans la demande de création plutôt que de retourner les informations de niveau inférieur par défaut.

0
répondu ncoghlan 2015-09-21 02:26:35

pour cela les sérialiseurs ont le partial argument. Si lorsque le processus est initialisé, vous pouvez passer partial=True. Si vous utilisez des génériques ou des mixins, vous pouvez outrepasser le get_serializer fonction:

def get_serializer(self, *args, **kwargs):
    kwargs['partial'] = True
    return super(YOUR_CLASS, self).get_serializer(*args, **kwargs)

et ça fera l'affaire.

Remarque: cela permet à tous les champs d'être optionnels et pas seulement un spécifique. Si vous voulez seulement des détails, vous pouvez outrepasser la méthode (i.e. update) et ajouter des validations d'existence pour divers Fields.

0
répondu Uri Shalit 2017-02-14 10:25:24