Template not displaying Validation error (Django) - django

I'm trying to display the Validation Error in my template (register.html) but it's not displaying. What is wrong with this code?
and one more question "how I can display email already exist in this form"
I don't know how to display the "email already exist".
The Codes Goes here.
forms.py
from django.contrib.auth.models import User
from django import forms
from .models import *
from django.utils.translation import gettext as _
class RegisterForm(forms.ModelForm):
username = forms.CharField(widget=forms.TextInput())
password = forms.CharField(widget=forms.PasswordInput())
email = forms.CharField(widget=forms.EmailInput())
class Meta:
model = Customer
fields =["full_name", "username", "email", "password"]
def clean_username(self):
uname = self.cleaned_data.get('username')
if User.objects.filter(username = uname).exists():
raise forms.ValidationError(_('Customer with this username already exists'), code='invalid')
return uname
def __init__(self, *args, **kwargs):
super(RegisterForm, self).__init__(*args, **kwargs) # Call to ModelForm constructor
self.fields['username'].widget.attrs['style'] = 'width:500px; height:40px;'
self.fields['password'].widget.attrs['style'] = 'width:500px; height:40px;'
self.fields['email'].widget.attrs['style'] = 'width:500px; height:40px;'
self.fields['full_name'].widget.attrs['style'] = 'width:500px; height:40px;'
I can't display the Error Message on above code Customer with this username already exists
views.py
from django.shortcuts import render, redirect
from django.views.generic import CreateView, View, FormView
from django.contrib.auth import authenticate, login, logout
from django.urls import reverse_lazy
from .forms import *
# Create your views here.
class customerRegister(CreateView):
template_name = "register.html"
form_class = RegisterForm
success_url = reverse_lazy("main_app:base")
def form_valid(self, form):
username = form.cleaned_data.get("username")
email = form.cleaned_data.get("email")
password = form.cleaned_data.get("password")
user = User.objects.create_user(username, email, password)
form.instance.user = user
login(self.request, user)
return super().form_valid(form)
def get_success_url(self):
if "next" in self.request.GET:
next_url = self.request.GET.get("next")
return next_url
else:
return self.success_url
register.html
<body style="background-color: #95a5a6">
{% if error %}
<div class="alert alert-dismissible alert-danger">
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
<strong>Oh snap!</strong>{{form.non_field_errors}}
</div>
{% endif %}
<div class="container-register mx-auto">
<form action="{% url 'account:register' %}" method="post">
{% csrf_token %}
<fieldset>
<h2 class="text-center" style="margin-top: 50px">Register</h2>
<div class="form-group">
<label class="col-form-label mt-4" for="inputDefault">Full Name*</label><br>
{{form.full_name}}<br>
</div>
<div class="form-group">
<label class="col-form-label mt-4" for="inputDefault">Username*</label><br>
{{form.username}}<br>
</div>
<div class="form-group">
<label for="exampleInputEmail1" class="form-label mt-4 ">Email address*</label><br>
{{form.email}}<br>
<small id="emailHelp" class="form-text text-muted">We'll never share your email with anyone else.</small>
</div>
<div class="form-group">
<label for="exampleInputPassword1" class="form-label mt-4">Password*</label><br>
{{form.password}}<br>
</div>
<br/>
<div class="text-center">
<button type="submit" class="btn btn-lg btn-primary">Register</button>
</div>
</fieldset>
</form>
<hr>
<h5 class="text-center" style="margin-top: 50px">Already Registered?<a class="nav-link" href="{% url 'account:login'%}">Log in</a></h5>
</div>
</body>

In your template you have {% if error %} which does not exist as you are not passing the template context error. Inside the template if you are then accessing {{form.non_field_errors}} which will exist if there are errors.
So it should be {% if form.non_field_errors %} like so:
{% if form.non_field_errors %}
<div class="alert alert-dismissible alert-danger">
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
<strong>Oh snap!</strong>{{form.non_field_errors}}
</div>
{% endif %}
See related StackOverflow post here for customising the email error message.

Related

How to retain two forms data in the same view/template/page?

I have two forms and two views using the same profile.html template. When I GET/POST to the ProfileUpdateView, all of the users profile data is present. However, when I post to UserDeleteView and not select the "accountActivation" box the page renders but the user profile data which is above is empty.
# urls.py
from django.contrib import admin
from django.urls import include, path
from apps.iam.views import ProfileUpdateView, UserDeleteView
urlpatterns = [
path("accounts/profile", ProfileUpdateView, name="profile_detailupdate"),
path("accounts/delete", UserDeleteView, name="user_delete"),
]
# views.py
import zoneinfo
from django.contrib.auth import get_user_model
from django.contrib.auth import logout as auth_logout
from django.contrib.auth.decorators import login_required
from django.shortcuts import HttpResponseRedirect, get_object_or_404, render
from django.utils import timezone
from django.views.decorators.http import require_http_methods
from django.views.generic import TemplateView, UpdateView
from .forms import ProfileForm
from .models import Profile
User = get_user_model()
# Homepage
class HomeDetailView(TemplateView):
template_name = "home.html"
# Profile
## Update
#login_required
#require_http_methods(["GET","POST"])
def ProfileUpdateView(request):
# dictionary for initial data with field names as keys
context = {}
# fetch the object related to passed id
profile_object = get_object_or_404(Profile, id=request.user.id)
# pass the object as instance in form
profile_form = ProfileForm(request.POST or None, instance=profile_object)
# save the data from the form
if profile_form.is_valid():
profile_form.save()
# add form dictionary to context
context["profile_form"] = profile_form
# activate the new timezone in case changed
request.session["django_timezone"] = request.user.profile.timezone
timezone.activate(zoneinfo.ZoneInfo(request.user.profile.timezone))
return render(request, "profile.html", context)
## Delete
#login_required
#require_http_methods(["POST"])
def UserDeleteView(request):
# dictionary for initial data with field names as keys
context = {}
# fetch the object related to passed id
user_object = get_object_or_404(Profile, id=request.user.id)
# pass the object as instance in form
userdelete_form = ProfileForm(request.POST or None, instance=user_object)
# add form dictionary to context
context["userdelete_form"] = userdelete_form
# user verified account deletion
if request.POST.get("accountActivation") == "on":
User = get_user_model()
user_pk = request.user.pk
auth_logout(request)
User.objects.filter(pk=user_pk).delete()
return render(request, "home.html", context)
else:
return render(request, "profile.html", context)
{% extends 'base.html' %}
{% load static %}
{% load widget_tweaks %}
{% block head_title %} {{ title }} {% endblock %}
{% block body %}
<div class="container-xxl flex-grow-1 container-p-y">
<div class="row">
<div class="col-md-12">
<div class="card mb-4">
<h5 class="card-header">Profile Details</h5>
<!-- Account -->
<div class="card-body">
<div class="d-flex align-items-start align-items-sm-center gap-4">
<img src="{% static 'img/avatars/1.png' %}" alt="user-avatar" class="d-block rounded"
height="100" width="100" id="uploadedAvatar" />
<div class="button-wrapper">
<label for="upload" class="btn btn-primary me-2 mb-4" tabindex="0">
<span class="d-none d-sm-block">Upload new photo</span>
<i class="bx bx-upload d-block d-sm-none"></i>
<input type="file" id="upload" class="account-file-input" hidden
accept="image/png, image/jpeg" />
</label>
<button type="button" class="btn btn-outline-secondary account-image-reset mb-4">
<i class="bx bx-reset d-block d-sm-none"></i>
<span class="d-none d-sm-block">Reset</span>
</button>
<p class="text-muted mb-0">Allowed JPG, GIF or PNG. Max size of 800K</p>
</div>
</div>
</div>
<hr class="my-0" />
<div class="card-body">
<form id="formAccountSettings" method="POST" action="{% url 'profile_detailupdate' %}">
<!-- Security token by Django -->
{% csrf_token %}
<!-- form -->
{% for hidden in profile_form.hidden_fields %}
{{ hidden }}
{% endfor %}
<div class="row">
{% for field in profile_form.visible_fields %}
<div class="mb-3 col-md-6">
<label for="{{ field.id_for_label }}">{{ field.label }}</label>
{% if field.field.widget.input_type == "select" %}
{{ field|add_class:'form-control form-select' }}
{% else %}
{{ field|add_class:'form-control' }}
{% endif %}
{% for error in field.errors %}
<span class="help-block">{{ error }}</span>
{% endfor %}
</div>
{% endfor %}
<div class="mt-2">
<button name="profile" type="submit" class="btn btn-primary me-2">Save changes</button>
<button type="reset" class="btn btn-outline-secondary">Cancel</button>
</div>
</form>
</div>
<!-- /Account -->
</div>
<div class="card">
<h5 class="card-header">Delete Account</h5>
<div class="card-body">
<div class="mb-3 col-12 mb-0">
<div class="alert alert-warning">
<h6 class="alert-heading fw-bold mb-1">Are you sure you want to delete your account?</h6>
<p class="mb-0">Once you delete your account, there is no going back. Please be certain.</p>
</div>
</div>
<form id="formAccountDeactivation" method="POST" action="{% url 'user_delete' %}">
<!-- Security token by Django -->
{% csrf_token %}
<!-- form -->
{% for hidden in deleteuser_form.hidden_fields %}
{{ hidden }}
{% endfor %}
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox" name="accountActivation"
id="accountActivation" />
<label class="form-check-label" for="accountActivation">I confirm my account
deactivation</label>
</div>
<button name="delete" type="submit" class="btn btn-danger deactivate-account">Deactivate Account</button>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
The issue was in the view which is shared below. I added logic to save the form if valid or read from the database if not. If anyone has suggestions to simply the below that would be great.
"""
file : views.py
reference : https://docs.djangoproject.com/en/4.1/topics/http/views/
https://docs.djangoproject.com/en/4.1/topics/class-based-views/
"""
import re
import zoneinfo
from django.contrib import messages
from django.contrib.auth import get_user_model
from django.contrib.auth import logout as auth_logout
from django.contrib.auth.decorators import login_required
from django.http.response import HttpResponseForbidden, HttpResponseRedirect
from django.shortcuts import HttpResponseRedirect, get_object_or_404, render
from django.utils import timezone
from django.views.decorators.http import require_http_methods
from django.views.generic import TemplateView, UpdateView
from django.views.generic.base import ContextMixin, TemplateResponseMixin
from django.views.generic.edit import ProcessFormView
from .forms import DeleteUserForm, ProfileForm
from .models import Profile
User = get_user_model()
# Homepage
class HomeDetailView(TemplateView):
template_name = "home.html"
# # Profile
#login_required
#require_http_methods(["GET","POST"])
def ProfileUpdateView(request):
context = {}
profile_object = Profile.objects.get(user=request.user.id)
# display profile
if request.method == 'GET':
profile_form = ProfileForm(None, instance=profile_object)
user_form = DeleteUserForm(request.POST)
# update profile or delete account
if request.method == 'POST':
# associate request.POST to form
profile_form = ProfileForm(request.POST, instance=profile_object)
user_form = DeleteUserForm(request.POST)
# profile_form
if 'btn-profile-submit' in request.POST:
# validate form
if profile_form.is_valid():
# valid: update database
profile_form.save()
# activate the new timezone in case changed
request.session["django_timezone"] = request.user.profile.timezone
timezone.activate(zoneinfo.ZoneInfo(request.user.profile.timezone))
messages.error(request, 'Successful profile update.')
else:
# invalid: disgard and read from the database
profile_form = ProfileForm(instance=profile_object)
messages.error(request, 'Failed profile update.')
# user_form
if 'btn-accountdeactivation-submit' in request.POST:
profile_form = ProfileForm(instance=profile_object)
# validate form
if user_form.is_valid():
# valid: delete user from database
cd = user_form.cleaned_data
print(cd.get('accountdeactivation'))
User = get_user_model()
user_pk = request.user.pk
auth_logout(request)
User.objects.filter(pk=user_pk).delete()
messages.error(request, 'Successful account deactivation.')
return render(request, "home.html", context)
else:
# invalid: disgard and warn the user
messages.error(request, 'Failed account deactivation. You must confirm your account deactivation.')
context["profile_form"] = profile_form
context["user_form"] = user_form
return render(request, "profile.html", context)

django form errors not showing on template

I'm using the basic django registration form and I'm not getting any errors displayed. I've seen a bunch of answers and nothing is working for me. I'm not sure if it's because I have custom css for the page or bootstrap or something else. Basically how do I display the errors in this particular case.
Here's my form:
<div class="form-content">
<h1>Sign Up</h1>
{% if user.is_authenticated == False %}
<form method="POST">
{% csrf_token %} {{form.as_p}}
<button class="btn form-btn">Sign Up</button>
<h4><span>or</span></h4>
<a
class="btn google-btn"
href="{% provider_login_url 'google' %}"
role="button"
style="text-transform: none; width: 100%"
>
<img
width="20px"
style="margin-bottom: 3px; margin-right: 5px"
alt="Google sign-in"
src="https://upload.wikimedia.org/wikipedia/commons/thumb/5/53/Google_%22G%22_Logo.svg/512px-Google_%22G%22_Logo.svg.png"
/>
Sign up with Google
</a>
</form>
{% else %}
<p>You're already registered...</p>
{% endif %}
</div>
Here's my view:
class UserRegistration(generic.CreateView):
form_class = RegisterForm
template_name = 'registration/registration.html'
def form_valid(self, form):
user = form.save()
form.registration_notification()
login(self.request, user, backend='django.contrib.auth.backends.ModelBackend')
return redirect(self.request.GET.get('next'))
and form:
class RegisterForm(UserCreationForm):
email = forms.EmailField()
first_name = forms.CharField(max_length=100)
last_name = forms.CharField(max_length=100)
class Meta:
model = User
fields = ('username', 'first_name', 'last_name', 'email', 'password1', 'password2')
def registration_notification(self):
email = self.cleaned_data['email']
username = self.cleaned_data['username']
if self.is_valid():
registration_notification_task.delay(email, username)
I'm not sure where to return the errors or where to validate the form and no answers for other questions have helped my situation. Now when I submit an invalid form there are no errors the page just doesn't submit. There's not even an error in the network tab so it's probably happening on the html side.
Updating my post following comments below:
**forms.py** (dont forget the import bit)
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
class RegisterForm(UserCreationForm):
class Meta:
model = User
fields = ["username", "email", "password1", "password2",]
views.py
def register_user(request):
if request.method == "POST":
form = RegisterForm(request.POST)
if form.is_valid():
new_user = form.save()
new_user = authenticate(username=form.cleaned_data['username'],
password=form.cleaned_data['password1'],)
login(request, new_user)
messages.success(request,("Registration succesful!"))
return HttpResponseRedirect("/home")
else:
form = RegisterForm()
return render(request,'main/registration/register_user.html',{'form':form})
registration template
{% load crispy_forms_tags %}
{% block content %}
<div class="container">
<div class="row">
<div class="col-md-5 mx-auto">
<div id="second">
<div class="myform form ">
<div class="logo mb-3">
<div class="col-md-12 text-center">
<h1 >Signup</h1>
</div>
</div>
<form method="POST" action = "{% url 'register_user' %}" class="form-group">
{% csrf_token %}
{{ form| crispy }}
<div class="col-md-12 text-center mb-3">
<button type="submit" class=" btn btn-block mybtn btn-primary tx-tfm">Let's do it!</button>
</div>
<div class="col-md-12 ">
<div class="form-group">
<p class="text-center">Already have an account?</p>
</div>
</div>
</div>
</form>
</div>
</div>
{% endblock %}

How can I fetch attributes from User model using UserProfile's OneToOneField relationship?

I'd love to write a form where a user can change their data. So I have a User model and a UserProfile extension model, the last one looks like this:
from django.db import models
from django.contrib.auth.models import User
from django.urls import reverse
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
info = models.TextField('информация')
def get_absolute_url(self):
return reverse('user_profile', args=[str(self.user.username)])
How to generate a form where my user attribute would have all its parameters like username, email, first_name, last_name to be changed? So I could do something like this in the template:
{% extends "layout.html" %}
{% block content %}
<h2>{{ title }}</h2>
<div class="row">
<form method="POST" action="{% url 'profile_edit' user.username %}" class="col s12">
{% csrf_token %}
{{ form.non_field_errors }}
<div class="row">
<div class="input-field col s6">
{{ form.user.username }}
{{ form.user.username.label_tag }}
{% if form.user.username.errors %}
<span class="helper-text">{{ form.user.username.errors }}</span>
{% else %}
<span class="helper-text">{{ form.user.username.help_text }}</span>
{% endif %}
</div>
<div class="input-field col s6">
{{ form.user.email }}
{{ form.user.email.label_tag }}
{% if form.user.email.errors %}
<span class="helper-text">{{ form.user.email.errors }}</span>
{% else %}
<span class="helper-text">{{ form.user.email.help_text }}</span>
{% endif %}
</div>
</div>
<div class="row">
<div class="input-field col s6">
{{ form.user.first_name }}
{{ form.user.first_name.label_tag }}
</div>
<div class="input-field col s6">
{{ form.user.last_name }}
{{ form.user.last_name.label_tag }}
</div>
</div>
<div class="row">
<div class="input-field col s12">
{{ form.info }}
{{ form.info.label_tag }}
</div>
</div>
<button class="btn waves-effect waves-light blue" type="submit" name="action">Изменить</button>
<a class="waves-effect waves-light btn-flat" href="{% url 'password_change' %}">Сменить пароль</a>
</form>
</div>
{% endblock %}
And finally, this is my view, obviously incorrect:
from django.shortcuts import get_object_or_404
from django.views import generic
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from datetime import datetime
from .models import *
class UserEditView(LoginRequiredMixin, UserPassesTestMixin, generic.UpdateView): # still does not work
fields = '__all__'
template_name = 'accounts/user_form.html'
def get_object(self, queryset=None):
return get_object_or_404(UserProfile, user=self.request.user)
def test_func(self):
return self.request.user == self.get_object().user
def form_valid(self, form):
response = super().form_valid(form)
user = User.objects.get_by_natural_key(form.cleaned_data['user'])
user.email = form.cleaned_data.get('email')
user.first_name = form.cleaned_data.get('first_name')
user.last_name = form.cleaned_data.get('last_name')
user.userprofile.info = form.cleaned_data.get('info')
user.save()
return response
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = 'Редактировать профиль'
context['year'] = datetime.now().year
return context
All of this does nothing but produces status 200 response.
Some of solutions:
Create 2 forms: UserForm and UserProfileForm.
profile_form = UserProfileForm(request.POST, instance=request.user.profile)
user_form = UserForm(request.POST, instance=request.user)
if profile_form.is_valid() and user_form.is_valid():
profile_form.save()
user_form.save()
....
Without 2 forms:
Write your own method inside form_valid and pass variables from request.POST to your profile
Also, you may check this user profile in drf

Django: can't login after editing user details

I'm trying to find a solution for 2 days now... can't figure out why login does not work for me once I have changed any user details.
Normal flow works fine for me: user registers, logs in and out without a problem. But I thought it would be good to have profile change option where a user can change username, password or email. I did implement this, but after a user changes anything (even email), the login form does not admit him.
Here is my views.py:
from django.contrib.auth.forms import UserCreationForm, UserChangeForm, PasswordChangeForm
from .forms import ContactForm, CreateUserForm, EditUserForm
class ProfileView(UpdateView):
template_name = 'boat_app/edit_profile.html'
form_class = UserChangeForm
success_url = reverse_lazy('anchored')
def get_object(self):
return self.request.user
class LoginView(TemplateView):
template_name = 'boat_app/login.html'
success_url = reverse_lazy('category_home')
def post(self, request):
print(request)
user = authenticate(request, username=request.POST.get('username'), password=request.POST.get('password'))
if user is not None:
print('user not none')
login(request, user)
return HttpResponseRedirect(reverse('anchored'))
else:
print('user none')
messages.info(request, 'Username OR password is incorrect')
return HttpResponseRedirect(reverse('login'))
And forms.py
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
class EditUserForm(forms.ModelForm):
class Meta:
model = User
fields = ('username', 'email')
After changing a user I see this change in admin interface, so it does work. But I can't see a reason why the system does not see this relation. Any way to debug what exactly is going on there?
UPDATE
I've tried changing the user via admin backend - all works fine. I've also tried using {{ form.as_p }} instead of my custom template for ProfileView - and this also worked! So apparently there is something fishy going on with my html template which ruins the update view and does nont allow to collect data for future login.
Here is my 'boat_app/edit_profile.html'
{% block body_block %}
<div class="row align-items-center vh-100">
<div class="col-lg-4 d-none d-md-block">
</div>
<div class="col-lg-4 d-flex align-items-center">
<div class="container p-3 text-center bg-light">
<div class="my-3">
<h1>Change your details</h1>
<p>Did something change?</p>
</div>
<form method="post">
{% csrf_token %}
<div class="form-group row px-3">
<label for="{{ form.username.id_for_label }}">{{form.username.label}}</label>
{% render_field form.username class="form-control" %}
</div>
<div class="form-group row px-3">
{{ form.email.errors }}
<label for="{{ form.email.id_for_label }}">{{form.email.label}}</label>
{% render_field form.email class="form-control" %}
</div>
<div class="form-group row px-3">
{{ form.password.errors }}
<label for="{{ form.date_joined.id_for_label }}">{{form.date_joined.label}}</label>
{% render_field form.date_joined class="form-control" %}
</div>
<div class="form-group row text-left px-3">
<p>If you need to change password, click here.</p>
</div>
<button class="btn btn-primary" type="submit">Submit</button>
</form>
</div>
</div>
<div class="col-lg-4 d-none d-md-block">
</div>
</div>
{% endblock %}

Issues with two forms submission in django one after another

I'm working on forget password page in which the user first have to answer the question for enabling the textfields for creating new password.
Here, I have two forms, One for security question and second for password and confirm password.
Following is my forms.py
from django import forms
from .models import SecurityQuestions
class PasswordForm(forms.Form):
password = forms.CharField(disabled=True, widget=forms.PasswordInput(attrs={'placeholder':'New Password'}))
password_confirm = forms.CharField(disabled=True, widget=forms.PasswordInput(attrs={'placeholder':'Re-enter Password'}))
def clean(self, *args,**kwargs):
password = self.cleaned_data.get('password')
password_confirm = self.cleaned_data.get('password_confirm')
if password and password_confirm:
if password != password_confirm:
raise forms.ValidationError('Password Mismatch')
return super(PasswordForm, self).clean(*args, **kwargs)
class PasswordVerificationForm(forms.Form):
question = forms.ModelChoiceField(queryset=SecurityQuestions.objects.all(), empty_label=None, widget=forms.Select(attrs={'class':'form-control','id': 'sectxt'}))
answer = forms.CharField(label='answer', widget=forms.TextInput(attrs={'placeholder':'Answer','id': 'anstxt'}))
Following is my views.py
from django.shortcuts import render, redirect
from .forms import PasswordForm, PasswordVerificationForm
from django.contrib.auth.decorators import login_required
from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth.hashers import make_password
from .models import SecurityQuestions
from django.contrib import messages
#login_required
#csrf_exempt
def password_reset(request):
form = PasswordForm(request.POST or None)
form1 = PasswordVerificationForm(request.POST or None)
if request.method == 'POST':
if request.POST.get("verify", False):
question = request.POST.get('question')
answer = request.POST.get('answer')
print("question",question)
print("answer",answer)
check = SecurityQuestions.objects.get(id=question) #id=1
print(check.answer)
if check.answer == answer:
messages.success(request, 'Enter Your New Password', 'alert-success')
form.fields['password'].disabled = False
form.fields['password_confirm'].disabled = False
else:
redirect('/')
messages.error(request, 'Incorrect Answer', 'alert-danger')
if request.POST.get("create", False):
if form.is_valid():
print("For Changing Password...")
password = form.cleaned_data.get('password')
request.user.password = make_password(password)
request.user.save()
return redirect('/')
else:
form = PasswordForm()
form1 = PasswordVerificationForm()
return render(request,"forget_password.html", {"form": form, "form1":form1})
Following is my forget_password.html
<div class="container">
<div class="main">
<div class="row justify-content-center">
<div class="col-md-4">
<div class="login-form">
<div class="row">
<div class="col-md-12">
<div class="login-title-holder">
<h4>Forgot Password</h4>
</div>
</div>
<form method="post">
<div class="form-group col-md-12">
<div class="input-group">
{{ form1.question | add_class:'form-control' }}
<span class="input-group-append">
<div class="input-group-text input-group-icon"><i class="fa fa-question" aria-hidden="true"></i></div>
</span>
</div>
</div>
<div class="form-group col-md-12">
<div class="input-group">
{{ form1.answer | add_class:'form-control' }}
<span class="input-group-append">
<div class="input-group-text input-group-icon "><i class="fa fa-comment" aria-hidden="true"></i></div>
</span>
</div>
</div>
<div class="col-md-12">
{% if messages %}
{% for message in messages %}
<div {% if message.tags %} class="alert {{ message.tags }} text-center"{% endif %}>
×
{{ message }}
</div>
{% endfor %}
{% endif %}
<input type="submit" name = "verify" formmethod="post" style="visibility: hidden;">
</div>
</form>
<form method="post">
<div class="form-group col-md-12">
<div class="input-group">
{{ form.password | add_class:'form-control' }}
<span class="input-group-append">
<div class="input-group-text input-group-icon"><i class="fa fa-key" aria-hidden="true"></i></div>
</span>
</div>
</div>
<div class="form-group col-md-12">
<div class="input-group">
{{ form.password_confirm | add_class:'form-control' }}
<span class="input-group-append">
<div class="input-group-text input-group-icon"><i class="fa fa-key" aria-hidden="true"></i></div>
</span>
</div>
</div>
<div class="col-md-12">
<div class="button-holder">
Cancel
<button class="login-btn" type="submit" formmethod="post" name="create">Create</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
If I enter the security answer first, based on the condition, if true, it enables the textfields for password and password_confirm.
But it's not creating the new password.
However, if I change the disabled = False in PasswordForm then it creating the new password successfully.
I want to know why it's not executing the code after the first form executes successfully.
Thanks!
You really should chain this into 2 urls, rather then trying 2 forms in one page. You can only submit one form and this is the problem you're facing. Once you have submitted the security question, you instantiate the form again with fields disabled:
form = PasswordForm(request.POST or None)
And now they do not get enabled, because the submit button called 'verify' from form1 is no longer present, so the code in that branch is not executed.
Let's say url is /password_reset/ - a rough outline (untested):
#login_required
#csrf_exempt
def security_question(request):
form = PasswordVerificationForm(request.POST)
if request.method == 'POST':
if form.is_valid():
token = generate_strong_token() # Implement: generate a strong token, url safe
request.session["password_reset_token"] = token
return redirect(f'/password_reset/{token}/')
else:
return render(...)
#login_required
#csrf_exempt
def change_password(request, **kwargs):
form = PasswordForm(request.POST)
token = request.session.get('password_reset_token')
if token == kwargs['token']:
if request.method == 'POST' and form.is_valid():
del request.session['password_reset_token']
# handle password change and redirect to wherever
else:
return render(...)
else:
raise SecurityError('Invalid token')
Your urls would be something like:
urlpatterns = [
re_path('password_reset/(?P<token>[0-9A-F]{32})/', change_password)
path('password_reset/', security_question)
]