将django密码验证器与django rest framework validate_password集成

mom*_*aaa 13 django validation django-rest-framework

我正在尝试将django验证器 1.9与django rest框架序列化器集成.但序列化的"用户"(django rest框架)与django验证器不兼容.

这是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
Run Code Online (Sandbox Code Playgroud)

我设法得到MinimumLengthValidator和NumericPasswordValidator正确,因为两个函数验证都不使用'user'进行验证.源代码在这里

摘自django源代码:

def validate(self, password, user=None):
        if password.isdigit():
            raise ValidationError(
                _("This password is entirely numeric."),
                code='password_entirely_numeric',
            )
Run Code Online (Sandbox Code Playgroud)

对于像UserAttributeSimilarityValidator这样的其他验证器,该函数在验证中使用另一个参数'user'('user'是django用户模型,如果我没错的话)

摘自django源代码:

 def validate(self, password, user=None):
        if not user:
            return

        for attribute_name in self.user_attributes:
            value = getattr(user, attribute_name, None)
Run Code Online (Sandbox Code Playgroud)

如何将序列化用户更改为django验证器(UserAttributeSimilarityValidator)可以看到的内容

摘自django源代码:

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
Run Code Online (Sandbox Code Playgroud)

编辑

Django Rest Framework可以获得所有Django的内置密码验证(但它就像一个黑客).这是一个问题:

validationError就是这样的

[ValidationError(['此密码太短.它必须包含至少8个字符.']),ValidationError(['此密码完全是数字.'])]

验证不包含字段.Django休息框架将其视为

{
    "non_field_errors": [
        "This password is too short. It must contain at least 8 characters.",
        "This password is entirely numeric."
    ]
}
Run Code Online (Sandbox Code Playgroud)

我怎样才能注入一个字段 raise ValidationError

AKS*_*AKS 18

如您所述,当您使用验证器验证passwordin validate_password方法时UserAttributeSimilarityValidator,您没有该user对象.

我建议您不要进行字段级验证,而应通过在序列化程序上实现方法来执行对象级验证validate:

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)
Run Code Online (Sandbox Code Playgroud)

  • 请记住,在执行部分(`PATCH`)更新时,`**data` 可能不包括创建用户对象的所有必需字段。 (2认同)

fap*_*aph 11

self.instance即使在进行字段级验证时,您也可以通过序列化程序对象访问用户对象.这样的事情应该有效:

 from django.contrib.auth import password_validation

 def validate_password(self, value):
    password_validation.validate_password(value, self.instance)
    return value
Run Code Online (Sandbox Code Playgroud)

  • 只有在创建用户时才能使用此选项,但在注册新用户和验证密码预创建时不会出现这种情况. (2认同)

shr*_*ing 9

使用序列化程序!有validate_fieldname办法!

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
Run Code Online (Sandbox Code Playgroud)