Django - Form not displaying in HTML when receiving an error - python

In my Django project, I display a form when a user sends a GET request.
Here's the code for this:
form = SignUpForm()
if request.method == 'POST':
....
else:
return render(request, 'users/signup.html', {'form': form})
HTML FOR THIS:
<form method="POST" class="signupform">
{% csrf_token %}
{% for field in form %}
<div class="fields">{{ field }}</div>
{{ field.errors }}
<br>
{% endfor %}
<input class="submitButton" type="submit" value="Sign Up">
</form>
If the user sends a post request, I set form = SignUpForm(request.POST) and check if a user with the same username as someone else exists. When this happens, I want to render the whole page again, including the form fields, with an error message displayed.
Here's my current code for this:
try:
user = User.objects.get(username=form.cleaned_data['username'])
return render(request, 'users/signup.html', {'error': 'Username field has already been taken', 'form':form})
except User.DoesNotExist:
...
HTML:
{% if error %}
<form method="POST" class="signupform">
{% csrf_token %}
{% for field in form %}
<div class="fields">{{ field }}</div>
{{ field.errors }}
<br>
{% endfor %}
<input class="submitButton" type="submit" value="Sign Up">
</form>
{{ error }}
{% endif %}
However when this error occurs, The error message does show but none of the form fields are displayed on the screen. They disappear. Does anybody know the issue? Thank you.
Updated code:
context = []
if request.method == 'POST':
form = SignUpForm(request.POST)
if form.is_valid():
if not form.cleaned_data['password'] == form.cleaned_data['confirmPassword']:
context["error"] = 'Username field has already been taken'
else:
raise Http404
else:
form = SignUpForm()
context['form'] = form
return render(request, 'users/signup.html', context)

View.py:
context = {}
if request.method == 'POST':
form = SignUpForm(request.POST)
try:
user = User.objects.get(username=form.cleaned_data['username'])
messages.error(request, 'Username field has already been taken')
return redirect('users/signup.html')
except User.DoesNotExist:
...
else:
form = SignUpForm()
context['form'] = form
return render(request, 'users/signup.html', context)
template:
{% if messages %}
{% for message in messages %}
{{ message }}
{% endfor %}
{% endif %}

Related

django after redirect adding form.errors to messages doesn't appear in template?

after redirect adding form.errors to messages if form not valid , form.errors doesn't appear in template
#login_required(login_url="login")
#user_passes_test(user_is_patient_check, login_url='login')
def changePasswordPatient(request):
if request.method == "POST":
form = PasswordChangeForm(request.user,request.POST)
if form.is_valid():
user=form.save()
update_session_auth_hash(request, user)
messages.success(request,'Şifreniz başarıyla güncellendi!')
return redirect("changePasswordPatient")
else:
messages.error(request,form.errors,extra_tags="invalidchangepassword")
return redirect("changePasswordPatient") # this part
form=PasswordChangeForm(request.user)
context={
"form":form,
"which_active":"passwordchange"
}
return render(request,"change-password.html",context)
but when I changed if form not valid part like using this(render method).Form errors showing in template.But in this method when I refresh page errors messages still showing.Can anyone help me to fix that?
if form.is_valid():
user=form.save()
update_session_auth_hash(request, user) # Important!
messages.success(request,'Şifreniz başarıyla güncellendi!')
return redirect("changePasswordPatient")
else:
messages.error(request,form.errors,extra_tags="invalidchangepassword")
return render(request,"change-password.html",{"form":form,"which_active":"passwordchange"})
change-password.html
{% if form.errors %}
{% for field in form %}
{% for error in field.errors %}
<div class="alert alert-danger">
<center><strong>{{ error|escape }}</strong></center>
</div>
{% endfor %}
{% endfor %}
{% for error in form.non_field_errors %}
<div class="alert alert-danger">
<center><strong>{{ error|escape }}</strong></center>
</div>
{% endfor %}
{% endif %}
You may edit your redirect into the following:
messages.error(request,form.errors,extra_tags="invalidchangepassword")
form=PasswordChangeForm(request.user)
return redirect("changePasswordPatient", {"form":form}) # this part

Invalid submitted Django form data being temporarily used elsewhere in a template

I am trying to make a view for users to change their usernames and emails. The page works as expected for the most part. However, there is an issue that occurs when a user inputs another user's username. You see, the profile editing page has a heading that displays the current user's username and email. When the user inputs a duplicate username, the heading becomes the invalid username until the page is refreshed. Here is the relevant code:
View
#login_required()
def profile(request):
if request.method == 'POST':
u_form = UserUpdateForm(request.POST, instance=request.user)
p_form = ProfileUpdateForm(request.user, request.POST, instance=request.user.profile)
if u_form.is_valid() and p_form.is_valid():
u_form.save()
p_form.save()
messages.success(request, 'Your account has been updated.')
return redirect('profile')
else:
u_form = UserUpdateForm(instance=request.user)
p_form = ProfileUpdateForm(request.user, instance=request.user.profile)
return render(request, 'users/profile.html', context(title='Procfile', u_form=u_form, p_form=p_form))
Form
class UserUpdateForm(forms.ModelForm):
email = forms.EmailField()
class Meta:
model = User
fields = ['username', 'email']
Template
{% extends "courses/base.html" %}
{% load crispy_forms_filters %}
{% load crispy_forms_tags %}
{% block content %}
<div class="content-section">
<div class="media">
<div class="media-body">
<h2 class="account-heading">{{ user.username }}</h2>
<p class="text-secondary">{{ user.email }}</p>
</div>
</div>
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Profile Info</legend>
{{ u_form|crispy }}
{{ p_form|crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Update</button>
</div>
</form>
</div>
{% endblock content %}
I have tried making the view return a redirect if the forms aren't valid, but that results in the automatically generated error messages (e.g. A user with that username already exists.) not appearing. Is there a way to prevent the issue while keeping the error messages? My apologies if I'm missing something obvious here. I'm still learning the ins and outs of Django.
You can simply check if the values provided are duplicate in the body of the if request.method == 'POST': . You would simply need methods like so in your form class.
def clean_email(self):
if User.objects.filter(username=username).exists():
raise forms.ValidationError("Username is not unique")
def clean_username(self):
if User.objects.filter(email=email).exists():
raise forms.ValidationError("Email is not unique")
You can then make a call to validate the values provided. see here for an example.

How do I properly pass form.errors after an invalid form submission

I have created a registration view where I want ValidationError(s) to be displayed after a form is submitted but it happens to be invalid form.is_valid() == False. ChefRegisterationForm is a subclass of UserCreationForm. If the form is valid, a flash message will do. With what I have, it gets the job done, but there has to be a cleaner way than having two render statements in the view. How could this be refactored so that there is only one render statement?
def register_chef(request):
if request.method == 'POST':
new_user_form = ChefRegisterationForm(request.POST)
if new_user_form.is_valid():
user = new_user_form.save()
login(request, user)
messages.info(
request,
f"Logged in: {user}!"
)
return HttpResponseRedirect(reverse("menu:menu_list"))
return render(request, 'chef/register_chef.html', {'form': new_user_form})
new_user_form = ChefRegisterationForm()
return render(request, 'chef/register_chef.html', {'form': new_user_form})
{% extends 'index.html '%}
{% block content %}
{% if form.errors %}
{% for field in form %}
{% for error in field.errors %}
<p>{{ error }}</p>
{% endfor %}
{% endfor %}
{% endif %}
<form action="{% url 'chef:register' %}" method="post">
{% csrf_token %}
{% for field in form %}
{{ field }}
{% endfor %}
<button>Register</button>
</form>
{% for key in form.errors %}
{{ key }}
{% endfor %}
{% endblock %}
Let me know if I'm missing something, but how about this?
def register_chef(request):
if request.method == 'POST':
new_user_form = ChefRegisterationForm(request.POST)
if new_user_form.is_valid():
user = new_user_form.save()
login(request, user)
messages.info(request, f"Logged in: {user}!")
return HttpResponseRedirect(reverse("menu:menu_list"))
else:
new_user_form = ChefRegisterationForm()
return render(request, 'chef/register_chef.html', {'form': new_user_form})
You might want to take a look at the class-based FormView which -I think- would reduce the boilerplate you have to write:
class ChefRegisterView(FormView):
form_class = ChefRegisterationForm
success_url = reverse("menu:menu_list")
template_name = 'chef/register_chef.html'
def form_valid(self, form):
user = new_user_form.save()
login(request, user)
messages.info(request, f"Logged in: {user}!")
return super().form_valid(form)

Django puts my password in browser url field

I have the following view class:
class LoginView(View):
form_class = LoginForm
template_name = 'workoutcal/login.html'
def post(self, request):
form = self.form_class(request.POST)
if form.is_valid():
email = form.cleaned_data['email']
password = form.cleaned_data['password']
user = authenticate(email = email, password = password)
if user is not None:
if user.is_active:
login(request, user)
return calendar(request)
else:
return render(request, self.template_name, {'form':form})
else:
form['custom_error_message'] = 'Invalid user'
return render(request, self.template_name, {'form':form})
def get(self, request):
form = self.form_class(None)
return render(request, self.template_name, {'form':form})
And this template:
login.html
{% extends "workout/base.html" %}
{% block logoutwidget %}{% endblock %}
{% block content %}
<form action="/workoutcal/login/">
{% include "workoutcal/form_disp_errors.html" %}
<input type="submit" value="Log in">
</form>
{% endblock %}
form_disp_errors.html
{% csrf_token %}
{{ form.custom_error_message }}
{{ form.non_field_errors }}
{% for field in form.visible_fields %}
<div class="row">
<div class="col-xs-2">
{{ field.label_tag }}
</div>
<div class="col-xs-2">
{{ field }}
</div>
<div class="col-xs-3">
{{ field.errors }}
</div>
</div>
{% endfor %}
When I go to workoutcal/login, type in an incorrect username and password (user doesn't exist), the page goes to workoutcal/login again, but with this url:
http://localhost:8000/workoutcal/login/?csrfmiddlewaretoken=ZywQUh7gnNfaHi8FcA3be4ynLB7SpGgwdJ0UxGzUuRYp0G0Y9LQ9e24Jx8Q1OD3Y&email=myemail%40hotmail.com&password=MYPASSWORD
As you can see in the end of the link, the password is displayed. This is obviously not good. However, I can't understand why it happens. Any ideas?
You have to use HTTP method POST, for that you must set attribute method="post" to your form tag. Like that:
<form method="post" action="/workoutcal/login/" >
With method POST request will send query string (key/value pairs) in HTTP message body instead of URL.
Note: consider using PUT/PATCH to update objects and DELETE to remove for RESTful APIs (by default Django will use method POST for all these cases).

Python/Django: How to display error messages on invalid login?

I'm trying to do the Login for my Django (2.0) website, so far I've got the login working for existing accounts. I'm using the built-in login function.
Now I want to display an error message when you enter an invalid account, for example "Invalid username or password!". But I have no idea how to go about this.
Right now it just refreshes the login page when your enter an invalid account. Any help is appreciated!
Login.html
{% block title %}Login{% endblock %}
{% block content %}
<h2>Login</h2>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Login</button>
</form>
{% endblock %}
Login view
def login(request):
if request.method == 'POST':
form = AuthenticationForm(request.POST)
username = request.POST['username']
password = request.POST['password']
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
auth_login(request, user)
return redirect('index')
else:
form = AuthenticationForm()
return render(request, 'todo/login.html', {'form': form})
in your template
{% for message in messages %}
<div class="alert alert-success">
<a class="close" href="#" data-dismiss="alert">×</a>
{{ message }}
</div>
{% endfor %}
in view
from django.contrib import messages
def login(request):
if request.method == 'POST':
form = AuthenticationForm(request.POST)
username = request.POST['username']
password = request.POST['password']
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
auth_login(request, user)
return redirect('index')
else:
messages.error(request,'username or password not correct')
return redirect('login')
else:
form = AuthenticationForm()
return render(request, 'todo/login.html', {'form': form})
You should just add inside your template:
{% block title %}Login{% endblock %}
{% block content %}
<h2>Login</h2>
{% if form.errors %}
<p>username or password not correct</p>
{% endif %}
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Login</button>
</form>
{% endblock %}
Updating for Django 2.0:
For individual field errors (e.g) :
<input type="text" class="form-control my-4" id="id_username" placeholder="Username" name="username">
{% for error in form.username.errors %}
<p class="text-danger">{{ error }}</p>
{% endfor %}
For non field errors (e.g if password is incorrect):
{% if form.non_field_errors %}
{% for error in form.non_field_errors %}
<p class="text-danger">{{ error }}</p>
{% endfor %}
{% endif %}
You should place the above at the end of all fields.
You already have {{ form.as_p }} in your template, so Django will show any form errors.
The problem is that the AuthenticationForm takes request as its first argument. You can then use form.is_valid() to check whether the username and password are valid and that the user is active, instead of fetching the data from request.POST manually.
def login(request):
if request.method == 'POST':
form = AuthenticationForm(request, request.POST)
if form.is_valid():
auth_login(self.request, form.get_user())
return redirect('index')
else:
form = AuthenticationForm(request)
return render(request, 'todo/login.html', {'form': form})
Another option would be to use the built-in LoginView instead of writing your own:
from django.contrib.auth import views as auth_views
urlpatterns = [
...
path('accounts/login/', auth_views.LoginView.as_view(template_name='todo/login.html')),
...
]
Then set LOGIN_REDIRECT_URL in your settings.
LOGIN_REDIRECT_URL = 'index'

Categories