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