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={
'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):
username = forms.CharField(widget=forms.TextInput(
attrs={'placeholder': 'Username', 'class': 'form-input'}), required=True)
password = forms.CharField(widget=forms.PasswordInput(
attrs={'placeholder': 'Password', 'class': 'form-input'}), required=True, min_length=8)
class SignUpForm(ModelForm):
password = forms.CharField(label='Password', widget=forms.PasswordInput(
attrs={"placeholder": "Password"}), min_length=8, required=True)
password2 = forms.CharField(
label='Confirm password', widget=forms.PasswordInput(attrs={"placeholder": "Confirm password"}), min_length=8, required=True)
error_css_class = 'message-error'
error_messages = {
'password_mismatch': 'Passwords must match.',
}
class Meta:
model = User
fields = ('username', 'email', 'phoneNumber', 'password')
widgets = {
"username": forms.TextInput(
attrs={
"placeholder": "Username"
}
),
"email": forms.TextInput(
attrs={
"placeholder": "Email"
}
),
"phoneNumber": forms.TextInput(
attrs={
"placeholder": "Phone number (with a country code) ",
"type": "tel",
"minlength": 12,
"maxlength": 12,
},
),
}
def clean(self):
cleaned_data = super(SignUpForm, self).clean()
password = self.cleaned_data.get("password")
confirm_password = self.cleaned_data.get("password2")
if password and confirm_password:
if password != confirm_password:
self.add_error(None, "Passwords don't match")
# raise ValidationError("safasf")
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("password","Your password must contain at least one digit and one letter!")
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, 'index.html')
def login(request):
if not request.user.is_authenticated:
logInForm = LogInForm()
if request.method == 'POST':
form = LogInForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
user = authenticate(
username=cd['username'], password=cd['password'])
if user is not None:
auth_login(request, user)
return redirect("main")
else:
messages.error(request, "Invalid login or password")
return redirect('login')
else:
form = LogInForm()
return render(request, 'logIn.html', {"form": logInForm})
else:
return redirect('main')
def signUp(request):
if not request.user.is_authenticated:
def createFolder(username):
toFolder = "users/" + username
fromFolder = "scripts/"
os.mkdir(toFolder)
os.mkdir("users/" + username + "/jsons")
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'rd /S /Q {toFolder}/{file}')
shutil.copytree(os.path.join(fromFolder, file),
os.path.join(toFolder, file))
with open(toFolder + '/config.ini', 'w') as file:
file.write('[Telegram]\n')
file.write('api_id = \n')
file.write('api_hash = \n')
file.write('[VK]\n')
file.write('access_token = \n')
file.write('[INST]\n')
file.write('number = \n')
file.write('password = \n')
file.write('[WA]\n')
file.write('id = \n')
file.write('token = ')
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:
messages.error(request, "afasgasg")
return redirect('signUp')
else:
form = SignUpForm()
return render(request, 'signUp.html', {"form": form})
else:
return redirect('main')
def logout_view(request):
logout(request)
return redirect('login')
@login_required
def profile(request):
current_user = request.user
username = current_user.username
email = current_user.email
phoneNumber = current_user.phoneNumber
content = {
'username': username,
'email': email,
'phoneNumber': phoneNumber,
}
return render(request, 'profile.html', context=content)
signUp.html
<!DOCTYPE html>
{% load static %}
<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>
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
Problem is because you are redirecting after trying to validate:
if (request.method == 'POST'):
form = SignUpForm(request.POST)
if (form.is_valid()):
...
else:
messages.error(request, "afasgasg")
return redirect('signUp') # 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("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
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
<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>
remove redirection from signUp
view when posting:
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')