Django表单中的奇怪行为

4

我正在使用Django Forms,对一个字段进行自定义验证时,遇到了奇怪的问题。

forms.py

class RegistrationForm(forms.Form):
    username = forms.CharField(max_length=50)
    email = forms.EmailField(required=False)
    password = forms.CharField(max_length=50)
    password1 = forms.CharField(max_length=50)        

    def clean_password(self):
        password = self.cleaned_data['password']
        print(self.cleaned_data) # all fields are present except password1 in cleaned Data
        re_password = self.cleaned_data['password1'] #Gives Key Error here
        # Do something

clean_password函数中尝试对密码字段进行验证时,出现了password1字段的键错误,我不知道为什么会发生这种情况。我尝试过很多搜索,但找不到任何相关的信息,关于是什么导致了这个错误。但后来我尝试改变了一些代码,它起作用了,但我不知道为什么它起作用。

modified_forms.py

class RegistrationForm(forms.Form):
    username = forms.CharField(max_length=50)
    email = forms.EmailField(required=False)
    password1 = forms.CharField(max_length=50)    #swapped position    
    password = forms.CharField(max_length=50)

    def clean_password(self):
        password = self.cleaned_data['password']
        print(self.cleaned_data) # all fields are present in cleaned Data
        re_password = self.cleaned_data['password1'] #Doesn't Give Key Error here
        # Do something

我所做的更改是只是交换了password1password这两行的位置,也就是改变了passwordpassword1的顺序。我把password1移动到了password的那一行,把原来password1所在的位置放上了password。这样就解决了一个我不理解的错误。根据我的理解,字段的顺序不应该影响任何东西。可以有人解释一下这里发生了什么吗?谢谢 :)
3个回答

2
这并不是奇怪的行为。Django表单就是这样工作的。请查看这里的源代码,了解Django表单中字段清理的工作原理。下面是_clean_fields方法的简化版本。
def _clean_fields(self):
    for name, field in self.fields.items():
        # skipped
        try:
            # skipped
            value = field.clean(value)
            self.cleaned_data[name] = value
            if hasattr(self, 'clean_%s' % name):
                # skipped
                value = getattr(self, 'clean_%s' % name)()
                self.cleaned_data[name] = value
        except ValidationError as e:
            self.add_error(name, e)

它的作用是循环遍历表单字段,并将每个字段的清理值放在“cleaned_data”字典中。如果定义了用于清理字段(clean_<FIELD_NAME>)的方法,则会在该字段的清理数据上调用该方法,并将其放入“cleaned_data”字典中。
在您的第一种情况中,password1password之后。因为按顺序清理字段,所以在尝试在clean_password方法中访问它时,password1尚未被清理。这意味着它的清理值尚未存在于“cleaned_data”字典中。
在第二种情况下,您交换了passwordpassword1的位置。现在已经对password1进行了清理,您可以访问它的值。
经验法则是对于clean_<FIELD_NAME>方法,只能访问在该特定字段之前声明的那些字段的清理值
解决方案
您应该像Django在其UserCreationForm中所做的那样进行操作。他们在第二个字段上检查密码匹配,即password1应该与password匹配,而不是反过来(本质上是相同的)。修改后的代码段在这里
def clean_password1(self):
    password = self.cleaned_data.get("password")
    password1 = self.cleaned_data.get("password1")
    if password and password1 and password != password1:
        raise forms.ValidationError('Passwords do not match')
    return password1

1
你使用Python。Python是一种脚本类型的语言,使用解释器,因此代码将逐行评估。
在你的情况下,你创建了“def clean_password(self)”方法,因此该方法在惰性评估中从表单中获取密码后调用。如果您重命名方法而不交换“password”和“password1”,则可以使用“def clean_password1(self)”方法正常工作。

forms.py

class RegistrationForm(forms.Form):
    username = forms.CharField(max_length=50)
    email = forms.EmailField(required=False)
    password = forms.CharField(max_length=50)
    password1 = forms.CharField(max_length=50)        

    def clean_password1(self):
        password = self.cleaned_data['password']
        print(self.cleaned_data) # all fields are present
        re_password = self.cleaned_data['password1']

如果您对我的回答感到满意,请让我知道。

0

我认为最好的做法是为passwordpassword1字段分别编写独立的清理方法。

def clean_password(self):
    password = self.cleaned_data['password']
    # Do something

def clean_password1(self):
    password1 = self.cleaned_data['password1']
    # Do something

或者在clean()方法中同时验证

def clean(self):
    cleaned_data = super().clean()
    password = cleaned_data['password']
    re_password = cleaned_data['password1']
    # Do something

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接