显示表单错误。 Django

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

display form errors. Django

问题

我有注册表单。我不知道如何显示错误。我试过在models.py中尝试,我有唯一的错误,但它们不会显示,我还在forms.py中进行了自定义验证,检查密码是否匹配以及密码的一些规则,但也不起作用。所以问题是,如何显示验证错误?

models.py

from django.db import models
from django.contrib.auth.models import AbstractBaseUser, AbstractUser

class User(AbstractUser):
    username = models.CharField(max_length=100, unique=True, error_messages={
        'unique': "This username has already been registered."})
    password = models.CharField(max_length=200, unique=False)
    email = models.EmailField(unique=True, error_messages={
        'unique': "This email has already been registered."})
    phoneNumber = models.CharField(max_length=12, unique=True, error_messages={
        'unique': "This phone number has already been registered."})
    first_name = None
    last_name = None

forms.py

from django.forms import ModelForm, ValidationError
from django import forms
from .models import User

class LogInForm(forms.Form):
    # ...

class SignUpForm(ModelForm):
    # ...

    def clean(self):
        # ...

views.py

# ...

def signUp(request):
    # ...

    if request.method == 'POST':
        form = SignUpForm(request.POST)
        if form.is_valid():
            # ...
        else:
            messages.error(request, "afasgasg")
        return redirect('signUp')
    else:
        form = SignUpForm()
    return render(request, 'signUp.html', {"form": form})

# ...

signUp.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="{% static 'css/signUp.css' %}" />
    <title>Sign up</title>
</head>
<body>
    <div class="container">
        <form class="form" method="post">
            <h1 class="title">Sign up</h1>
            {% csrf_token %}
            {% for field in form %}
                {{ field }}
                {% if field.errors %}
                    {% for error in field.errors %}
                        <span class="message-error"> {{ error }} </span>
                    {% endfor %}
                {% endif %}
            {% endfor %}
            {% if form.non_field_errors %}
                {% for non_field_error in form.non_field_errors %}
                    <span class="message-error"> {{ non_field_error }} </span>
                {% endfor %}
            {% endif %}
            <label for="confirm-valid" class="label-check">
                <input type="checkbox" id="confirm-valid" required> I've checked the correctness of the entered data
            </label>
            <label for="confirm-agreement" class="label-check">
                <input type="checkbox" id="confirm-agreement" required> I agree with the user agreement
            </label>
            <button class="form-button" type="submit">Sign up</button>
        </form>
    </div>
</body>
</html>

我尝试了raise ValidationError()add_error(),但它们不起作用。我有一个登录表单,它运行得很好,所有错误都会显示,一切都正常。我知道可以在注册表单中使用messages.error(),但我需要为特定字段显示错误,例如,如果电子邮件已经注册过。

我可以重写我的注册视图,错误会显示,但在这种情况下,用户将在重新加载页面后每次都发送POST请求。现在,只有在用户点击表单中的提交按钮后才会发送POST请求,但因此表单不会保存其错误。

编辑
我在重新加载页面后发送POST请求,这样做可以吗?
(未提供可用于查看的链接)

英文:

I have sign up form. I don't know how I can display errors. I'm trying it from models.py, I have unique errors, but they don't display, I also have custom validation in forms.py, where I'm checking if passwords match and some rules for passwords and it doesn't work either. So, question is, how can I display validation errors?
models.py

from django.db import models
from django.contrib.auth.models import AbstractBaseUser, AbstractUser
# Create your models here.


class User(AbstractUser):
    username = models.CharField(max_length=100, unique=True, error_messages={
                                &#39;unique&#39;: &quot;This username has already been registered.&quot;})
    password = models.CharField(max_length=200, unique=False)
    email = models.EmailField(unique=True, error_messages={
                              &#39;unique&#39;: &quot;This email has already been registered.&quot;})
    phoneNumber = models.CharField(max_length=12, unique=True, error_messages={
                                   &#39;unique&#39;: &quot;This phone number has already been registered.&quot;})
    first_name = None
    last_name = None

forms.py

from django.forms import ModelForm, ValidationError
from django import forms
from .models import User

class LogInForm(forms.Form):
    username = forms.CharField(widget=forms.TextInput(
        attrs={&#39;placeholder&#39;: &#39;Username&#39;, &#39;class&#39;: &#39;form-input&#39;}), required=True)
    password = forms.CharField(widget=forms.PasswordInput(
        attrs={&#39;placeholder&#39;: &#39;Password&#39;, &#39;class&#39;: &#39;form-input&#39;}), required=True, min_length=8)


class SignUpForm(ModelForm):
    password = forms.CharField(label=&#39;Password&#39;, widget=forms.PasswordInput(
        attrs={&quot;placeholder&quot;: &quot;Password&quot;}), min_length=8, required=True)
    password2 = forms.CharField(
        label=&#39;Confirm password&#39;, widget=forms.PasswordInput(attrs={&quot;placeholder&quot;: &quot;Confirm password&quot;}), min_length=8, required=True)
    error_css_class = &#39;message-error&#39;
    error_messages = {
        &#39;password_mismatch&#39;: &#39;Passwords must match.&#39;,
    }
    class Meta:
        model = User
        fields = (&#39;username&#39;, &#39;email&#39;, &#39;phoneNumber&#39;, &#39;password&#39;)
        widgets = {
            &quot;username&quot;: forms.TextInput(
                attrs={
                    &quot;placeholder&quot;: &quot;Username&quot;
                }
            ),
            &quot;email&quot;: forms.TextInput(
                attrs={
                    &quot;placeholder&quot;: &quot;Email&quot;
                }
            ),
            &quot;phoneNumber&quot;: forms.TextInput(
                attrs={
                    &quot;placeholder&quot;: &quot;Phone number (with a country code) &quot;,
                    &quot;type&quot;: &quot;tel&quot;,
                    &quot;minlength&quot;: 12,
                    &quot;maxlength&quot;: 12,
                },
            ),
        }

    def clean(self):
        cleaned_data = super(SignUpForm, self).clean()
        password = self.cleaned_data.get(&quot;password&quot;)
        confirm_password = self.cleaned_data.get(&quot;password2&quot;)
        if password and confirm_password:
            if password != confirm_password:
                self.add_error(None, &quot;Passwords don&#39;t match&quot;)
                # raise ValidationError(&quot;safasf&quot;)
        ifNums = False
        ifLets = False
        for letter in password:
            if letter.isalpha():
                ifLets = True
            if letter.isdigit():
                ifNums = True
            if ifNums == True and ifLets == True:
                break
        if ifNums == False or ifLets == False:
            self.add_error(&quot;password&quot;,&quot;Your password must contain at least one digit and one letter!&quot;)  
        return cleaned_data

views.py

from django.shortcuts import render
from django.shortcuts import render, redirect
from .forms import LogInForm, SignUpForm
from django.contrib.auth.decorators import login_required, permission_required
from django.contrib.auth.hashers import make_password
from django.contrib.auth import authenticate, logout, login as auth_login
from django.contrib import messages
import os
import shutil
# Create your views here.


@login_required
def index(request):
    return render(request, &#39;index.html&#39;)


def login(request):
    if not request.user.is_authenticated:
        logInForm = LogInForm()
        if request.method == &#39;POST&#39;:
            form = LogInForm(request.POST)
            if form.is_valid():
                cd = form.cleaned_data
                user = authenticate(
                    username=cd[&#39;username&#39;], password=cd[&#39;password&#39;])
                if user is not None:
                    auth_login(request, user)
                    return redirect(&quot;main&quot;)
                else:
                    messages.error(request, &quot;Invalid login or password&quot;)
                    return redirect(&#39;login&#39;)
            else:
                form = LogInForm()
        return render(request, &#39;logIn.html&#39;, {&quot;form&quot;: logInForm})
    else:
        return redirect(&#39;main&#39;)


def signUp(request):
    if not request.user.is_authenticated:
        def createFolder(username):
            toFolder = &quot;users/&quot; + username
            fromFolder = &quot;scripts/&quot;
            os.mkdir(toFolder)
            os.mkdir(&quot;users/&quot; + username + &quot;/jsons&quot;)
            if (os.path.exists(fromFolder) and os.path.exists(toFolder)):
                for file in os.listdir(fromFolder):
                    if os.path.isfile(os.path.join(fromFolder, file)):
                        shutil.copy(os.path.join(fromFolder, file),
                                    os.path.join(toFolder, file))
                    if os.path.isdir(os.path.join(fromFolder, file)):
                        os.system(f&#39;rd /S /Q {toFolder}/{file}&#39;)
                        shutil.copytree(os.path.join(fromFolder, file),
                                        os.path.join(toFolder, file))
            with open(toFolder + &#39;/config.ini&#39;, &#39;w&#39;) as file:
                file.write(&#39;[Telegram]\n&#39;)
                file.write(&#39;api_id = \n&#39;)
                file.write(&#39;api_hash = \n&#39;)
                file.write(&#39;[VK]\n&#39;)
                file.write(&#39;access_token = \n&#39;)
                file.write(&#39;[INST]\n&#39;)
                file.write(&#39;number = \n&#39;)
                file.write(&#39;password = \n&#39;)
                file.write(&#39;[WA]\n&#39;)
                file.write(&#39;id = \n&#39;)
                file.write(&#39;token = &#39;)

        if (request.method == &#39;POST&#39;):
            form = SignUpForm(request.POST)
            if (form.is_valid()):
                user = form.save(commit=False)
                user.password = make_password(&#39;password&#39;)
                user.save()
                auth_login(request, user)
                createFolder(user.username)
                return redirect(&quot;main&quot;)
            else:
                messages.error(request, &quot;afasgasg&quot;)
            return redirect(&#39;signUp&#39;)
        else:
            form = SignUpForm()
        return render(request, &#39;signUp.html&#39;, {&quot;form&quot;: form})
    else:
        return redirect(&#39;main&#39;)


def logout_view(request):
    logout(request)
    return redirect(&#39;login&#39;)


@login_required
def profile(request):
    current_user = request.user
    username = current_user.username
    email = current_user.email
    phoneNumber = current_user.phoneNumber
    content = {
        &#39;username&#39;: username,
        &#39;email&#39;: email,
        &#39;phoneNumber&#39;: phoneNumber,
    }
    return render(request, &#39;profile.html&#39;, context=content)

signUp.html

&lt;!DOCTYPE html&gt;
{% load static %}
&lt;html lang=&quot;en&quot;&gt;
    &lt;head&gt;
        &lt;meta charset=&quot;UTF-8&quot;&gt;
        &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot;&gt;
        &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
        &lt;link rel=&quot;stylesheet&quot; href=&quot;{% static &quot;css/signUp.css&quot; %}&quot; /&gt;
        &lt;title&gt;Sign up&lt;/title&gt;
    &lt;/head&gt;
    &lt;body&gt;
        &lt;div class=&quot;container&quot;&gt;
            &lt;form class=&quot;form&quot; method=&quot;post&quot;&gt;
                &lt;h1 class=&quot;title&quot;&gt;Sign up&lt;/h1&gt;
                {% csrf_token %}
                {% for field in form %}
                    {{ field }}
                    {% if field.errors %}
                        {% for error in field.errors %}
                            &lt;span class=&quot;message-error&quot;&gt; {{ error }} &lt;/span&gt;
                        {% endfor %}
                    {% endif %}
                {% endfor %}
                {% if form.non_field_errors %}
                    {% for non_field_error in form.non_field_errors %}
                        &lt;span class=&quot;message-error&quot;&gt; {{ non_field_error }} &lt;/span&gt;
                    {% endfor %}
                {% endif %}
                &lt;label for=&quot;confirm-valid&quot; class=&quot;label-check&quot;&gt;
                    &lt;input type=&quot;checkbox&quot; id=&quot;confirm-valid&quot; required&gt; I&#39;ve checked the correctness of the entered data
                &lt;/label&gt;
                &lt;label for=&quot;confirm-agreement&quot; class=&quot;label-check&quot;&gt;
                    &lt;input type=&quot;checkbox&quot; id=&quot;confirm-agreement&quot; required&gt; I agree with the user agreement
                &lt;/label&gt;
                &lt;button class=&quot;form-button&quot; type=&quot;submit&quot;&gt;Sign up&lt;/button&gt;
            &lt;/form&gt;
        &lt;/div&gt;
    &lt;/body&gt;
&lt;/html&gt;

I tried this: raise ValidationError(), add_error(). They don't work. I have log in form, and it works fine, all errors display, everything is ok. I know, that I can use messages.error() in sign up form, but I need to display errors for specific fields, for example, for email, if it's been already registered.
I can rewrite my sign up view and errors will display, but in that case user will send post requests every time after reloading page. Now user sends post request only if he pushes submit button in form, but because of it form doesn't save its errors.

EDIT
I send post requests after reloading page, is this ok?
https://drive.google.com/file/d/1DAtk2I6h6XQK0gjMUW8uk7Y8qCjkkG6U/view?usp=sharing

答案1

得分: 2

问题出在您尝试验证后进行重定向:

if (request.method == 'POST'):
    form = SignUpForm(request.POST)
    if (form.is_valid()):
        ...
    else:
        messages.error(request, "afasgasg")
    return redirect('signUp') # 这里有问题

这样做会阻止表单显示错误,因为重定向会创建一个新的表单实例。

将您的表单改回引发 ValidationError

forms.py

class SignUpForm(ModelForm):
    ...
    
    class Meta:
        ...

    def clean(self):
        cleaned_data = super().clean()
        password = self.cleaned_data.get("password")
        confirm_password = self.cleaned_data.get("password2")

        if password and confirm_password:
            if password != confirm_password:
                raise ValidationError("Passwords don't match", code='invalid password')

        ifNums = False
        ifLets = False
        for letter in password:
            if letter.isalpha():
                ifLets = True
            if letter.isdigit():
                ifNums = True
            if ifNums == True and ifLets == True:
                break
        if ifNums == False or ifLets == False:
            raise ValidationError("Your password must contain at least one digit and one letter!", code='invalid password')
        return cleaned_data

此外,您不需要检查错误是否存在,只需在有错误时渲染,或者您可以使用 表单渲染选项

注册模板

<form class="form" method="post" action="">
    <h1 class="title">Sign up</h1>
    {% csrf_token %}
    {{ form.non_field_errors }}
    {% for field in form %}
        {% for error in field.errors %}
            <span class="message-error"> {{ error }} </span>
        {% endfor %}
        {{ field }}
    {% endfor %}
    <label for="confirm-valid" class="label-check">
        <input type="checkbox" id="confirm-valid" required> I've checked the correctness of the entered data
    </label>
    <label for="confirm-agreement" class="label-check">
        <input type="checkbox" id="confirm-agreement" required> I agree with the user agreement
    </label>
    <button class="form-button" type="submit">Sign up</button>
</form>

在提交时从 signUp 视图中移除重定向:

def signUp(request):
    if not request.user.is_authenticated:
        ...

        if request.method == 'POST':   
            form = SignUpForm(request.POST)
            if form.is_valid():
                user = form.save(commit=False)
                user.password = make_password('password')
                user.save()
                auth_login(request, user)
                createFolder(user.username)
                return redirect('main')
            
        else:
            form = SignUpForm()
        return render(request, 'signUp.html', {"form": form})
    else:
        return redirect('main')
英文:

Problem is because you are redirecting after trying to validate:

if (request.method == &#39;POST&#39;):
    form = SignUpForm(request.POST)
    if (form.is_valid()):
        ...
    else:
        messages.error(request, &quot;afasgasg&quot;)
    return redirect(&#39;signUp&#39;) # problem is here

When you do that, you are not letting the form render the errors, because the redirect will create a new form instance.

Change your forms back to raise ValidationError:

forms.py

class SignUpForm(ModelForm):
    ...
    
    class Meta:
        ...

    def clean(self):
        cleaned_data = super().clean()
        password = self.cleaned_data.get(&quot;password&quot;)
        confirm_password = self.cleaned_data.get(&quot;password2&quot;)

        if password and confirm_password:
            if password != confirm_password:
                raise ValidationError((&quot;Passwords don&#39;t match&quot;), code=&#39;invalid password&#39;)

        ifNums = False
        ifLets = False
        for letter in password:
            if letter.isalpha():
                ifLets = True
            if letter.isdigit():
                ifNums = True
            if ifNums == True and ifLets == True:
                break
        if ifNums == False or ifLets == False:
            raise ValidationError((&quot;Your password must contain at least one digit and one letter!&quot;), code=&#39;invalid password&#39;)
        return cleaned_data

Also, you do not need to check if errors exist, just render when there is one, alternatively you can use form rendering options:

sign up template

&lt;form class=&quot;form&quot; method=&quot;post&quot; action=&quot;&quot;&gt;
    &lt;h1 class=&quot;title&quot;&gt;Sign up&lt;/h1&gt;
    {% csrf_token %}
    {{ form.non_field_errors }}
    {% for field in form %}
        {% for error in field.errors %}
            &lt;span class=&quot;message-error&quot;&gt; {{ error }} &lt;/span&gt;
        {% endfor %}
        {{ field }}
    {% endfor %}
    &lt;label for=&quot;confirm-valid&quot; class=&quot;label-check&quot;&gt;
        &lt;input type=&quot;checkbox&quot; id=&quot;confirm-valid&quot; required&gt; I&#39;ve checked the correctness of the entered data
    &lt;/label&gt;
    &lt;label for=&quot;confirm-agreement&quot; class=&quot;label-check&quot;&gt;
        &lt;input type=&quot;checkbox&quot; id=&quot;confirm-agreement&quot; required&gt; I agree with the user agreement
    &lt;/label&gt;
    &lt;button class=&quot;form-button&quot; type=&quot;submit&quot;&gt;Sign up&lt;/button&gt;
&lt;/form&gt;

remove redirection from signUp view when posting:

def signUp(request):
    if not request.user.is_authenticated:
        ...

        if request.method == &#39;POST&#39;:   
            form = SignUpForm(request.POST)
            if form.is_valid():
                user = form.save(commit=False)
                user.password = make_password(&#39;password&#39;)
                user.save()
                auth_login(request, user)
                createFolder(user.username)
                return redirect(&#39;main&#39;)
            
        else:
            form = SignUpForm()
        return render(request, &#39;signUp.html&#39;, {&quot;form&quot;: form})
    else:
        return redirect(&#39;main&#39;)

huangapple
  • 本文由 发表于 2023年2月27日 04:26:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/75574828.html
匿名

发表评论

匿名网友

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

确定