如果嵌套序列化程序上的验证失败,如何阻止用户的创建?

时间:2015-10-18 15:31:19

标签: django django-rest-framework

我正在为用户注册创建一个REST api,我有一个嵌套的序列化程序,我存储有关用户的其他信息。

用户序列化程序要求输入first_name,last_name,email和密码。

嵌套的序列化程序请求agree_terms_of_service

需要电子邮件,密码和agree_terms_of_service。

但是,如果用户键入了他们的电子邮件和密码并且未检查agree_terms_of_service框,则会返回并返回错误,但仍会创建一个包含电子邮件和密码的用户。

然后当用户“纠正这种情况”时,电子邮件地址已被使用。

如果我更新而不是创建,我觉得我会遇到人们覆盖其他用户的情况...... 我想知道人们如何用django rest serializers处理这个问题,最佳做法是什么?

VIEWS.PY

Case 1 (with hyphen)
Enter the phone number string
1-800-flowers<---User input
Phone #: 1-800-3569377

SERIALIZERS.PY

def serialize(self, request):
        if request.method =='POST':
            data = json.loads(request.body)
            #first validation
            if data['password'] != data['password2']:
                raise serializers.ValidationError({'msgType':'error','message':'Passwords do not match.'})
            #move to serializer
            else:                   
            serializer = userSerializer(data = data)
            data['username'] = data['email'] 
            if serializer.is_valid(raise_exception=True): 
                serializer.save() 
                response = {'msgType':'success', 'message':'Your account has been created successfully.'}
            elif serializer.errors:
                raise serializers.ValidationError({'msgType':'error', 'message': serializer.errors})
            return Response(response)

Models.py

class nestedSerializer(serializers.ModelSerializer):

class Meta:
    model = Nested
    fields = ('agreed_terms_of_service')

def validate(self, data):
    return data


class userSerializer(serializers.ModelSerializer):

nested = nestedSerializer()

class Meta:
    model = User
    fields = ('pk','email', 'password', 'username','first_name','last_name','nested')

def validate(self, data):
    email = data['email']
    try:
        User.objects.get(email = email)
    except User.DoesNotExist:
        return data
    else:
        raise serializers.ValidationError({'msgType':'error', 'message':'A user with this email address already exists.'})
    return data

def create(self, validated_data):
    nested_data = validated_data.pop('extend')
    email = validated_data['email']
    user = User.objects.create(**validated_data)
    user.username = user.id
    user.set_password(validated_data['password'])
    user.save()
    nested = Nested.objects.create(user=user, **nested_data)
    return user

提前感谢您的帮助。非常感谢。

1 个答案:

答案 0 :(得分:1)

首先,我将您当前的validate()函数更改为validate_email()(因为您所做的只是验证电子邮件尚未使用)。如果要访问函数中的多个字段,则应使用validate()。请参阅此处的文档以了解有关何时应使用字段级验证和对象级验证的更多信息:http://www.django-rest-framework.org/api-guide/serializers/#validation

其次,在您看来,您可以:

if data['password'] != data['password2']:
                raise serializers.ValidationError({'msgType':'error','message':'Passwords do not match.'})

如果您要验证“密码”和“确认密码”字段是否匹配,我会检查序列化程序的validate()函数(因为您将同时访问'密码'和' password2'字段。

第三,在你的create方法中,我使用User.objects.create_user来创建一个用户(create_user将处理密码的散列等。这样,你就不需要显式地执行user.set_password( validated_data [ '密码']))。有关详细信息,请参阅此处的答案:How to create a user in Django?

最后,解决主要问题。您的“agree_terms_of_service”是一个布尔字段,这意味着它同时接受True和False。我试试的是这个:

class nestedSerializer(serializers.ModelSerializer):

    class Meta:
        model = Nested
        fields = ('agreed_terms_of_service')

    def validate_agreed_terms_of_service(self, data):
        print(data) # to verify if data is even a boolean
        if data == True or data == 'True':
            return data
        raise serializers.ValidationError({'msgType':'error', 'message':'Please accept the terms and conditions.'})

并在您的userSerializer的create函数中,在开头添加一个print语句,以查看在“agree_terms_of_service”验证之前是否正在执行create。

def create(self, validated_data):
    print("Creating the object before validating the nested field.")
    # I'd be surprised if DRF executes a create function before
    # even validating it's nested fields.

    # rest of the create code goes here

当您添加上述语句时,它为“数据”打印了什么,并在“创建对象”之前打印“数据”?