Django表单中的KeyError异常

huangapple go评论76阅读模式
英文:

Django KeyError at forms.py

问题

Here is the translated content without the code:

我正在学习Django,正在尝试使用表单注册用户。当我填写用户名、电子邮件、密码和确认密码字段后,提交注册表单时出现错误,错误信息如下:

KeyError at director/register/ "confirm_password"

然而,当我查看错误消息中的POST数据时,我明确看到了我要访问的数据。

csrfmiddlewaretoken: blahblahblabh

username: dasdasd

email: dasd@dsada.com

password: test123!

confirm_password: test123!

以下是forms.py、views.py和register.html文件的部分内容。

forms.py

from django import forms
from django.contrib.auth.forms import UserCreationForm, UserChangeForm

from .models import CustomUser

class CustomUserCreationForm(UserCreationForm):

    class Meta:
        model = CustomUser
        fields = ("email", "password")

class CustomUserChangeForm(UserChangeForm):

    class Meta:
        model = CustomUser
        fields = ("email", "password")

class RegisterUser(forms.Form):
    username = forms.CharField(max_length=64, required=True)
    email = forms.EmailField()
    password = forms.CharField(max_length=128, widget=forms.PasswordInput(), required=True)
    confirm_password = forms.CharField(max_length=128, widget=forms.PasswordInput(), required=True)

    def clean_username(self):
        username = self.cleaned_data["username"]
        if CustomUser.objects.filter(username=username).exists():
            raise forms.ValidationError("Email already exists")
        return username
    
    def clean_email(self):
        email = self.cleaned_data['email']
        if CustomUser.objects.filter(email=email).exists():
            raise forms.ValidationError("Email already exists")
        return email

    def clean_password(self):
        password = self.cleaned_data['password']
        confirm_password = self.cleaned_data['confirm_password']
        if password != confirm_password:
            raise forms.ValidationError("Passwords do not match")
        return password

views.py(register_view)

def register_view(request):
    if request.method == "POST":
        form = RegisterUser(request.POST)
        if form.is_valid():
            new_user = form.save()
            login(request, new_user)
            return redirect('home')
    else:
        form = RegisterUser()
    return render(request, 'pokedex/register.html', {'form': form})

register.html

{% extends 'pokedex/layout.html' %}

{% block title %}Register{% endblock title %}


{% block body %}

<h1>Register</h1>

{% if form.errors %}
<div class="alert alert-danger">
    <ul>
        {% for error in form.errors.values %}
            <li>{{ error }}</li>
        {% endfor %}
    </ul>
</div>
{% endif %}

<form action="{% url 'register' %}" method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <input type="submit" value="Register">
</form>

{% endblock body %}

EDIT: 添加了Traceback

环境


请求方法POST
请求URLhttp://127.0.0.1:8000/pokedex/register/

Django版本4.1.7
Python版本3.9.2
已安装应用程序
['pokedex'
'django.contrib.admin'
'django.contrib.auth'
'django.contrib.contenttypes'
'django.contrib.sessions'
'django.contrib.messages'
'django.contrib.staticfiles']
已安装中间件
['django.middleware.security.SecurityMiddleware'
'django.contrib.sessions.middleware.SessionMiddleware'
'django.middleware.common.CommonMiddleware'
'django.middleware.csrf.CsrfViewMiddleware'
'django.contrib.auth.middleware.AuthenticationMiddleware'
'django.contrib.messages.middleware.MessageMiddleware'
'django.middleware.clickjacking.XFrameOptionsMiddleware']

Traceback最新调用最前):
  File C:\Users\Alice\Documents\Code\env\lib\site-packages\django\core\handlers\exception.py”,56在inner
    response = get_responserequest
  File C:\Users\Alice\Documents\Code\env\lib\site-packages\django\core\handlers\base.py”,197在_get_response
    response = wrapped_callbackrequest* callback_args** callback_kwargs
  File C:\Users\Alice\Documents\Code\stepchallenge\pokedex\views.py”,45在register_view
    if form.is_valid():
  File C:\Users\Alice\Documents\Code\env\lib\site-packages\django\forms\forms.py”,205在is_valid
    return self.is_bound and not self.errors
  File C:\Users\Alice\Documents\Code\env\lib\site-packages\django\forms\forms.py”,200在errors
    self.full_clean()
  File C:\Users\Alice\Documents\Code\env\lib\site-packages\django\forms\forms.py”,437在full_clean
    self._clean_fields()
  File C:\Users\Alice\Documents\Code\env\lib\site-packages\django\forms\forms.py”,452在_clean_fields
    value = getattrself,“clean_s”%name)()
  File C:\Users\Alice\Documents\Code\stepchallenge\pokedex\forms.py”,38在clean_password
    confirm_password = self.cleaned_data['confirm_password']

异常类型KeyError at /pokedex/register/
异常值'confirm_password'
英文:

I'm learning django and I'm playing around with forms and registering users.
When I submit the registration form with the username, email, password, and confirm password fields filled, I get an error that states:

KeyError at director/register/
&quot;confirm_password&quot;

However, when I look at the POST data in the error message I clearly see the data I'm trying to access.

Django表单中的KeyError异常

csrfmiddlewaretoken: blahblahblabh

username: dasdasd

email: dasd@dsada.com

password: test123!

confirm_password: test123!

Below are the forms.py, portion of the views.py, register.html files.

forms.py

from django import forms
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from .models import CustomUser
class CustomUserCreationForm(UserCreationForm):
class Meta:
model = CustomUser
fields = (&quot;email&quot;, &quot;password&quot;)
class CustomUserChangeForm(UserChangeForm):
class Meta:
model = CustomUser
fields = (&quot;email&quot;, &quot;password&quot;)
class RegisterUser(forms.Form):
username = forms.CharField(max_length=64, required=True)
email = forms.EmailField()
password = forms.CharField(max_length=128, widget=forms.PasswordInput(), required=True)
confirm_password = forms.CharField(max_length=128, widget=forms.PasswordInput(), required=True)
def clean_username(self):
username = self.cleaned_data[&quot;username&quot;]
if CustomUser.objects.filter(username=username).exists():
raise forms.ValidationError(&quot;Email already exists&quot;)
return username
def clean_email(self):
email = self.cleaned_data[&#39;email&#39;]
if CustomUser.objects.filter(email=email).exists():
raise forms.ValidationError(&quot;Email already exists&quot;)
return email
def clean_password(self):
password = self.cleaned_data[&#39;password&#39;]
confirm_password = self.cleaned_data[&#39;confirm_password&#39;]
if password != confirm_password:
raise forms.ValidationError(&quot;Passwords do not match&quot;)
return password```

views.py (register_view)

def register_view (request):
if request.method == &quot;POST&quot;:
form = RegisterUser(request.POST)
if form.is_valid():
new_user = form.save()
login(request, new_user)
return redirect(&#39;home&#39;)
else:
form = RegisterUser()
return render(request, &#39;pokedex/register.html&#39;, {&#39;form&#39;: form})

register.html

{% extends &#39;pokedex/layout.html&#39; %}
{% block title %}Register{% endblock title %}
{% block body %}
&lt;h1&gt;Register&lt;/h1&gt;
{% if form.errors %}
&lt;div class=&quot;alert alert-danger&quot;&gt;
&lt;ul&gt;
{% for error in form.errors.values %}
&lt;li&gt;{{ error }}&lt;/li&gt;
{% endfor %}
&lt;/ul&gt;
&lt;/div&gt;
{% endif %}
&lt;form action=&quot;{% url &#39;register&#39; %}&quot; method=&quot;post&quot;&gt;
{% csrf_token %}
{{ form.as_p }}
&lt;input type=&quot;submit&quot; value=&quot;Register&quot;&gt;
&lt;/form&gt;
{% endblock body %}

EDIT: Added Traceback

Environment:
Request Method: POST
Request URL: http://127.0.0.1:8000/pokedex/register/
Django Version: 4.1.7
Python Version: 3.9.2
Installed Applications:
[&#39;pokedex&#39;,
&#39;django.contrib.admin&#39;,
&#39;django.contrib.auth&#39;,
&#39;django.contrib.contenttypes&#39;,
&#39;django.contrib.sessions&#39;,
&#39;django.contrib.messages&#39;,
&#39;django.contrib.staticfiles&#39;]
Installed Middleware:
[&#39;django.middleware.security.SecurityMiddleware&#39;,
&#39;django.contrib.sessions.middleware.SessionMiddleware&#39;,
&#39;django.middleware.common.CommonMiddleware&#39;,
&#39;django.middleware.csrf.CsrfViewMiddleware&#39;,
&#39;django.contrib.auth.middleware.AuthenticationMiddleware&#39;,
&#39;django.contrib.messages.middleware.MessageMiddleware&#39;,
&#39;django.middleware.clickjacking.XFrameOptionsMiddleware&#39;]
Traceback (most recent call last):
File &quot;C:\Users\Alice\Documents\Code\env\lib\site-packages\django\core\handlers\exception.py&quot;, line 56, in inner
response = get_response(request)
File &quot;C:\Users\Alice\Documents\Code\env\lib\site-packages\django\core\handlers\base.py&quot;, line 197, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File &quot;C:\Users\Alice\Documents\Code\stepchallenge\pokedex\views.py&quot;, line 45, in register_view
if form.is_valid():
File &quot;C:\Users\Alice\Documents\Code\env\lib\site-packages\django\forms\forms.py&quot;, line 205, in is_valid
return self.is_bound and not self.errors
File &quot;C:\Users\Alice\Documents\Code\env\lib\site-packages\django\forms\forms.py&quot;, line 200, in errors
self.full_clean()
File &quot;C:\Users\Alice\Documents\Code\env\lib\site-packages\django\forms\forms.py&quot;, line 437, in full_clean
self._clean_fields()
File &quot;C:\Users\Alice\Documents\Code\env\lib\site-packages\django\forms\forms.py&quot;, line 452, in _clean_fields
value = getattr(self, &quot;clean_%s&quot; % name)()
File &quot;C:\Users\Alice\Documents\Code\stepchallenge\pokedex\forms.py&quot;, line 38, in clean_password
confirm_password = self.cleaned_data[&#39;confirm_password&#39;]
Exception Type: KeyError at /pokedex/register/
Exception Value: &#39;confirm_password&#39;

答案1

得分: 0

按照Django官方文档中的描述,在Forms部分的Cleaning and validating fields that depend on each other章节中,你不能在clean_password函数内部读取self.cleaned_data['confirm_password'],因为该字段可能尚未填充。

我建议你通过以下方式来进行验证,覆盖clean函数,像这样:

class RegisterForm(forms.Form):
    username = forms.CharField(max_length=64, required=True)
    email = forms.EmailField()
    password = forms.CharField(max_length=128, widget=forms.PasswordInput(), required=True)
    confirm_password = forms.CharField(max_length=128, widget=forms.PasswordInput(), required=True)

    def clean_username(self):
        username = self.cleaned_data["username"]
        if CustomUser.objects.filter(username=username).exists():
            raise forms.ValidationError("Email already exists")
        return username

    def clean_email(self):
        email = self.cleaned_data['email']
        if CustomUser.objects.filter(email=email).exists():
            raise forms.ValidationError("Email already exists")
        return email

    # 移除clean_password函数以使用默认验证

    # 在所有字段都被清理后检查密码
    def clean(self):
        cleaned_data = super().clean()
        password = cleaned_data['password']
        confirm_password = cleaned_data['confirm_password']
        if password != confirm_password:
            raise forms.ValidationError("Passwords do not match")

我还建议你查看Django官方文档 - Forms,因为它描述了如何避免大多数错误。

英文:

As described in the Django official documentation in the section Forms at Cleaning and validating fields that depend on each other, you cannot read self.cleaned_data[&#39;confirm_password&#39;] inside the function clean_password, as the field might not be populated yet.

I suggest you to do the validation overriding the clean function like this:

class RegisterForm(forms.Form):
    username = forms.CharField(max_length=64, required=True)
    email = forms.EmailField()
    password = forms.CharField(max_length=128, widget=forms.PasswordInput(), required=True)
    confirm_password = forms.CharField(max_length=128, widget=forms.PasswordInput(), required=True)

    def clean_username(self):
        username = self.cleaned_data[&quot;username&quot;]
        if CustomUser.objects.filter(username=username).exists():
            raise forms.ValidationError(&quot;Email already exists&quot;)
        return username
    
    def clean_email(self):
        email = self.cleaned_data[&#39;email&#39;]
        if CustomUser.objects.filter(email=email).exists():
            raise forms.ValidationError(&quot;Email already exists&quot;)
        return email
    
    # Removed clean_password function to use the default validation
    
    # Moved password check after all field are cleaned
    def clean(self):
        cleaned_data = super().clean()
        password = cleaned_data[&#39;password&#39;]
        confirm_password = cleaned_data[&#39;confirm_password&#39;]
        if password != confirm_password:
            raise forms.ValidationError(&quot;Passwords do not match&quot;)

I suggest also to take a look to Django official documentation - Forms as it describes how to avoid most errors.

huangapple
  • 本文由 发表于 2023年5月17日 13:51:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/76268909.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定