J'essaie d'intégrer validateurs Django 1.9 avec Django reste des sérialiseurs de framework. Mais le "utilisateur" sérialisé (de Django reste framework) n'est pas compatible avec les validateurs Django.
Voici le serializers.py
import Django.contrib.auth.password_validation as validators
from rest_framework import serializers
class RegisterUserSerializer(serializers.ModelSerializer):
password = serializers.CharField(style={'input_type': 'password'}, write_only=True)
class Meta:
model = User
fields = ('id', 'username', 'email, 'password')
def validate_password(self, data):
validators.validate_password(password=data, user=User)
return data
def create(self, validated_data):
user = User.objects.create_user(**validated_data)
user.is_active = False
user.save()
return user
J'ai réussi à obtenir des valeurs MinimumLengthValidator et NumericPasswordValidator correctes, car les deux fonctions valident n'utilisent pas "utilisateur" dans la validation. Le code source est ici
Extrait de Django code source:
def validate(self, password, user=None):
if password.isdigit():
raise ValidationError(
_("This password is entirely numeric."),
code='password_entirely_numeric',
)
Pour d'autres validateurs comme UserAttributeSimilarityValidator, la fonction utilise un autre argument 'user' dans la validation ('user' est Django User model, si je ne me trompe pas)
Extrait de Django code source:
def validate(self, password, user=None):
if not user:
return
for attribute_name in self.user_attributes:
value = getattr(user, attribute_name, None)
Comment puis-je changer un utilisateur sérialisé en ce que Django (UserAttributeSimilarityValidator) peuvent voir
Extrait de Django code source:
def validate(self, password, user=None):
if not user:
return
for attribute_name in self.user_attributes:
value = getattr(user, attribute_name, None)
if not value or not isinstance(value, string_types):
continue
Modifier
Django Rest Framework peut obtenir toute la validation de mot de passe intégrée de Django (mais c'est comme un hack). Voici un problème:
La validationError est comme ça
[ValidationError (['Ce mot de passe est trop court. Il doit contenir au moins 8 caractères.']), ValidationError (['Ce mot de passe est entièrement numérique.'])]
La validation ne contient pas de champ. Django reste le framework le voit comme
{
"non_field_errors": [
"This password is too short. It must contain at least 8 characters.",
"This password is entirely numeric."
]
}
Comment puis-je injecter un champ à raise ValidationError
Comme vous l'avez mentionné, lorsque vous validez le password
dans validate_password
utilisant le validateur UserAttributeSimilarityValidator
, vous n'avez pas l'objet user
.
Ce que je suggère qu'au lieu de faire une validation au niveau du champ, vous devez effectuer validation au niveau de l'objet en implémentant la méthode validate
sur le sérialiseur:
import sys
from Django.core import exceptions
import Django.contrib.auth.password_validation as validators
class RegisterUserSerializer(serializers.ModelSerializer):
# rest of the code
def validate(self, data):
# here data has all the fields which have validated values
# so we can create a User instance out of it
user = User(**data)
# get the password from the data
password = data.get('password')
errors = dict()
try:
# validate the password and catch the exception
validators.validate_password(password=password, user=User)
# the exception raised here is different than serializers.ValidationError
except exceptions.ValidationError as e:
errors['password'] = list(e.messages)
if errors:
raise serializers.ValidationError(errors)
return super(RegisterUserSerializer, self).validate(data)
Vous pouvez accéder à l'objet utilisateur via self.instance
sur l'objet sérialiseur, même lors de la validation au niveau du champ. Quelque chose comme ça devrait fonctionner:
from Django.contrib.auth import password_validation
def validate_password(self, value):
password_validation.validate_password(value, self.instance)
return value
Utilisez des sérialiseurs! Avoir un validate_fieldname
méthode!
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = (
'id', 'username', 'password', 'first_name', 'last_name', 'email'
)
extra_kwargs = {
'password': {'write_only': True},
'username': {'read_only': True}
}
def validate_password(self, value):
try:
validate_password(value)
except ValidationError as exc:
raise serializers.ValidationError(str(exc))
return value
def create(self, validated_data):
user = super().create(validated_data)
user.set_password(validated_data['password'])
user.is_active = False
user.save()
return user
def update(self, instance, validated_data):
user = super().update(instance, validated_data)
if 'password' in validated_data:
user.set_password(validated_data['password'])
user.save()
return user
Au moment de la création d'un nouvel utilisateur (enregistrement), self.instance ne sera aucun, cela fonctionnera lorsque vous reposez le mot de passe, changez le mot de passe ou mettez à jour les données utilisateur avec le mot de passe. Mais si vous souhaitez vérifier que le mot de passe ne doit pas être similaire à votre adresse e-mail ou nom d'utilisateur, vous devez inclure "SequenceMatcher" dans votre validation
data = self.get_initial()
username = data.get("username")
email = data.get("email")
password = data.get("password")
max_similarity = 0.7
if SequenceMatcher(a=password.lower(), b=username.lower()).quick_ratio() > max_similarity:
raise serializers.ValidationError("The password is too similar to the username.")
if SequenceMatcher(a=password.lower(), b=email.lower()).quick_ratio() > max_similarity:
raise serializers.ValidationError("The password is too similar to the email.")