Ordre de Validation du Serializer dans le cadre de Django REST
Situation
tout en travaillant avec la validation dans le cadre du REST Framework de Django ModelSerializer
, j'ai remarqué que les Meta.model
les champs sont toujours validés, même si cela n'a pas nécessairement de sens de le faire. Prenons l'exemple suivant pour un User
serialization du model:
- j'ai un paramètre qui crée un utilisateur. En tant que tel, il est un
password
etconfirm_password
champ. Si les deux champs ne correspondent pas, l'utilisateur ne peut pas être créé. De même, si la demandeusername
existe déjà, l'utilisateur ne peut pas être créé. - L'utilisateur affiche des valeurs inappropriées pour chacun des champs mentionnés ci-dessus
- une implémentation de
validate
a été fait dans le serializer( voir ci-dessous), en attrapant le non-matchingpassword
etconfirm_password
champs
mise en oeuvre de validate
:
def validate(self, data):
if data['password'] != data.pop('confirm_password'):
raise serializers.ValidationError("Passwords do not match")
return data
Problème
même quand le ValidationError
est augmenté de validate
, le ModelSerializer
recherche toujours la base de données pour vérifier si le username
est déjà utilisé. Cela est évident dans la liste d'erreurs qui est retournée à partir du point final; les erreurs du modèle et les erreurs hors champ sont présentes.
par conséquent, je voudrais savoir comment empêcher la validation du modèle jusqu'à ce que la validation hors champ soit terminée, en m'enregistrant un appel à ma base de données.
Tentative de solution
j'ai essayé de passer par les DRF source pour comprendre où cela se produit, mais j'ai été incapable de trouver ce que je dois outrepasser afin que cela fonctionne.
2 réponses
puisque très probablement votre username
champ unique=True
définir, Django RESTE Cadre ajoute automatiquement un validateur qui vérifie que le nouveau nom d'utilisateur est unique. Vous pouvez confirmer cela en faisant repr(serializer())
, qui vous montrera tous les domaines, ce qui inclut les validateurs.
exécution de la Validation de dans un, des sans-papiers
- désérialisation du champ appelée (
serializer.to_internal_value
etfield.run_validators
) serializer.validate_[field]
est appelé pour chaque champ- les validateurs de niveau de sérialiseur sont appelés (
serializer.run_validation
suivi deserializer.run_validators
) serializer.validate
s'appelle
donc le problème que vous voyez est que la validation au niveau du champ est appelée avant votre validation au niveau du sérialiseur. Bien que je ne le recommande pas, vous pouvez supprimer le validateur de champ par le paramètre extra_kwargs
dans le méta de votre sérilaliseur.
class Meta:
extra_kwargs = {
"username": {
"validators": [],
},
}
vous aurez besoin de re-implémenter le unique
vérifiez votre propre validation, ainsi que tout autre validateur généré automatiquement.
Je ne crois pas que les solutions ci-dessus fonctionnent plus. Dans mon cas, mon model a les champs 'first_name' et' last_name', mais l'API ne recevra que'name'.
définir 'extra_kwargs' et' validators ' dans la classe Meta semble n'avoir aucun effet, first_name et last_name sont toujours considérés comme nécessaires, et les validateurs sont toujours appelés. Je ne peux pas surcharger les champs de caractères first_name/last_name avec
anotherrepfor_first_name = serializers.CharField(source=first_name, required=False)
comme les noms de sens. Après de nombreuses heures de frustration, J'ai trouvé que la seule façon que j'ai pu outrepasser les validateurs avec une instance ModelSerializer était d'outrepasser l'initialiseur de classe comme suit (pardonnez l'indentation incorrecte):
class ContactSerializer(serializers.ModelSerializer):
name = serializers.CharField(required=True)
class Meta:
model = Contact
fields = [ 'name', 'first_name', 'last_name', 'email', 'phone', 'question' ]
def __init__(self, *args, **kwargs):
self.fields['first_name'] = serializers.CharField(required=False, allow_null=True, allow_blank=True)
self.fields['last_name'] = serializers.CharField(required=False, allow_null=True, allow_blank=True)
return super(ContactSerializer, self).__init__(*args, **kwargs)
def create(self, validated_data):
return Contact.objects.create()
def validate(self, data):
"""
Remove name after getting first_name, last_name
"""
missing = []
for k in ['name', 'email', 'question']:
if k not in self.fields:
missing.append(k)
if len(missing):
raise serializers.ValidationError("Ooops! The following fields are required: %s" % ','.join(missing))
from nameparser import HumanName
names = HumanName(data['name'])
names.capitalize()
data['last_name'] = names.last
if re.search(r'\w+', names.middle):
data['first_name'] = ' '.join([names.first, names.middle])
else:
data['first_name'] = names.first
del(data['name'])
return data
maintenant le doc dit qu'autoriser les blancs et les nuls avec des champs de caractères est un non non, mais c'est un serializer, pas un model, et comme L'API est appelée par toutes sortes de cowboys, je dois couvrir mes bases.