django user creation form with auth.user clean method - python

I have created a form to add users in my front-end but the form does not validate duplicated username.I am using auth.user model.
This is my code:
views.py
from django.contrib.auth.models import User, Group
#login_required(login_url='/login/')
#permission_required('auth.add_user',raise_exception=True)
def user_new(request):
if request.method == "POST":
form = NewUserForm(request.POST)
if form.is_valid():
user = form.save(commit=False)
user.set_password(user.password)
user.save()
return redirect('userdetail', user.id)
else:
form = NewUserForm()
return render(request, 'ace/user_edit.html', {'form': form})
forms.py
class NewUserForm(forms.ModelForm):
class Meta:
model = User
fields = ['username','first_name','last_name','password','email','is_active','is_staff','groups']
widgets = {
'username':TextInput(attrs={'class': u'form-control'}),
'first_name':TextInput(attrs={'class': u'form-control'}),
'last_name':TextInput(attrs={'class': u'form-control'}),
'password':PasswordInput(attrs={'class': u'form-control'}),
'email':EmailInput(attrs={'class': u'form-control'}),
'is_active':NullBooleanSelect(attrs={'class': u'form-control'}),
'is_staff':NullBooleanSelect(attrs={'class': u'form-control'}),
'groups':SelectMultiple(attrs={'class': u'form-control'}),
}
def clean_username(self):
username = self.cleaned_data['username']
user_exists = User.objects.get(username=username)
if user_exists:
raise ValidationError("User exists")
template
...
{% if form.errors %}
{% for field in form %}
{% for error in field.errors %}
<div class="alert alert-danger">
<strong>{{ error|escape }}</strong>
</div>
{% endfor %}
{% endfor %}
{% for error in form.non_field_errors %}
<div class="alert alert-danger">
<strong>{{ error|escape }}</strong>
</div>
{% endfor %}
{% endif %}
<form method="POST" class="service-form">{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="save btn btn-info">Salvar</button>
<a href="{% url 'userlist' %}">
<button class="btn btn-danger" type="button">Cancelar</button>
</a>
</form>
...
When I create a new user OK, but when a try create a user that same username of other I get a error:
The view ace.views.user_new didn't return an HttpResponse object. It
returned None instead.
If I add a print line "print form.errors" in view i get in console:
usernameUser
exists

Your view does not have an else statement for if, form is not valid it should render the template with form errors.
You need to change your view like this,
def user_new(request):
if request.method == "POST":
form = NewUserForm(request.POST)
if form.is_valid():
user = form.save(commit=False)
user.set_password(user.password)
user.save()
return redirect('userdetail', user.id)
else:
return render(request, 'ace/user_edit.html', {'form': form})
else:
form = NewUserForm()
return render(request, 'ace/user_edit.html', {'form': form})
And also you need to add the tag {%for field in form%} {{field.error}}{%endfor%} along with the form fields and labels.

You need to make sure that your view returns a response for POST requests when the form is invalid. You can do this by moving the final return render() statement out of the else block.
def user_new(request):
if request.method == "POST":
form = NewUserForm(request.POST)
if form.is_valid():
...
return redirect('userdetail', user.id)
else:
form = NewUserForm()
return render(request, 'ace/user_edit.html', {'form': form})

For registration django.contrib.auth User needs the username field to be unique. If you want to use other model field as unique (as unique registration field) and not the username, for example the email field, you can use this approach or use other registration bakends like django registration or django registration redux.

Instead of fixing the bug in your code I suggest to not invent the wheel and use excellent django-allauth package. It handles user login, logout, change password, registration and social sign in. I always start new projects from adding django-allauth - it handles all authentication problems with no effort.
You can use the saved time and effort to write actual application code instead of solving trivial user management details.
Also, the proper way to check for existence of the model instance is this:
user_exists = User.objects.filter(username=username).exists()
if user_exists:
raise ValidationError("User exists")

Related

The form validation returning False in Django

views.py
def student_login(request):
form = StudentLoginForm()
if request.method == 'POST':
form = StudentLoginForm(data=request.POST)
print(form.is_valid())
if form.is_valid():
username = form.cleaned_data.get('username')
password = form.cleaned_data.get('password')
print(username)
user = authenticate(username=username,password=password)
if user is not None:
login(request,user)
return redirect('index')
else:
messages.error(request,'Invalid username or password!')
else:
messages.error(request,'Invalid username or password!')
context = {'form':form}
return render(request,'student_login.html',context)
models.py
class Student(models.Model):
name = models.CharField(max_length=100)
username = models.CharField(max_length=100,unique=True,default=None)
mobile = models.CharField(max_length=8)
email = models.CharField(max_length=200,unique=True)
password = models.CharField(max_length=200,default=None,unique=True)
total_books_due = models.IntegerField(default=0)
def __str__(self):
return self.name
forms.py
class StudentLoginForm(forms.ModelForm):
class Meta:
model = Student
fields = ['username','password']
widgets = {
'password':forms.PasswordInput(),
}
student_login.html
{% extends 'base.html' %}
<!-- {% load crispy_forms_tags %} -->
{% block content %}
<div class="container">
<br>
<h2>Student Login Form</h2>
<br>
<form method="POST" action="">
{% csrf_token %}
{% if messages %}
{% for message in messages %}
<div class="alert alert-danger" role="alert">
{{ message }}
</div>
{% endfor %}
{% endif %} <br>
{% for field in form %}
<p>{{ field.label }} </p>
<p>{{ field }} </p>
<br>
{% endfor %}
<br>
<input type="submit" class="btn btn-primary" value="Login">
</form>
</div>
{% endblock %}
I have been trying to change again and again but form.is_valid() is still returning False. I could not figure out the reason that the form is not valid because I have already specify the fields that I want to show and added the csrf_token. Could anyone help me to figure out where is the problem?
Try to use just a simple form and not a ModelForm, as ModelForms in the background work with creating and updating objects.
In this case, ModelForm is validating as if you are trying to create a new Student, causing the already exists failures.
For example, you can write a simple login form like this:
class StudentLoginForm(forms.Form):
username = forms.CharField(widget=forms.TextInput(attrs={'autofocus': True}))
password = forms.CharField(
label='Password',
strip=False,
widget=forms.PasswordInput,
)
And then in your views, use it as is:
def student_login(request):
form = StudentLoginForm()
if request.method == 'POST':
form = StudentLoginForm(data=request.POST)
if form.is_valid():
user = authenticate(
username=form.cleaned_data.get('username'),
password=form.cleaned_data.get('password'),
)
if user:
login(request, user)
return redirect('index')
messages.error(request, 'Invalid username or password!')
context = {'form':form}
return render(request,'student_login.html',context)
can you please change this line
form = StudentLoginForm(data=request.POST)
to
form = StudentLoginForm(request.POST)
and also why you are validating the fields as you already validating using .is_valid()
function?
Just create a simple form with username and password. Also, the password field should not be unique if you are using plain text.
Better to use inbuilt User model provided by django. If you want to extend fields in User model, use AbstractUser and override the User model. See in documentation https://docs.djangoproject.com/en/3.2/topics/auth/customizing/

Django form.errors not showing up in template

I have refrenced this stackoverflow page and tried to display my forms error on the html template.
I did:
{% if form.error %}
{% for field in form %}
{% for error in field.errors %}
<div class="alert alert-danger">
<strong>{{ error|escape }}</strong>
</div>
{% endfor %}
{% endfor %}
{% for error in form.non_field_errors %}
<div class="alert alert-danger">
<strong>{{ error|escape }}</strong>
</div>
{% endfor %}
{% endif %}
as said in the stackoverflow question I also tried simply doing:
{% if form.error %}
This should pop up if there is an error
{% endif %}
Nothing comes up regardless:
Heres my view.py code:
def register(request):
if request.method == 'POST':
form = UserCreationForm(request.POST)
if form.is_valid():
form.save()
username = form.cleaned_data.get('username')
password = form.cleaned_data.get('password2')
user = authenticate(username=username, password=password)
login(request, user)
return HttpResponseRedirect('/')
else:
print(form.errors)
form = UserCreationForm()
return render(request, 'registration/register.html', {'form': form})
I am able to get the form errors onto the django console but it refuses to show up on the template.
Printing form.errors prints to the console: <li>password2<ul class="errorlist"><li>The two password fields didn't match.</li></ul></li></ul>
forms.errors fired up, but at the end, you declare a new form form = UserCreationForm() just before you render your view.
After checking whether the form is valid or not, all your validation errors are inside form instance, remember processes run as sequence, at the end, you destroy the variable form with form = UserCreationForm() so no validation errors anymore.
What you can do is add this new form form = UserCreationForm() to else statement when your request method is GET to keep having an empty form. By adding the else statement you avoid the new assignment of the form; after the validation process, it will jump to render(request,....) with the form instance containing all validation errors
if request.method == 'POST':
form = UserCreationForm(request.POST)
if form.is_valid():
form.save()
username = form.cleaned_data.get('username')
password = form.cleaned_data.get('password2')
user = authenticate(username=username, password=password)
login(request, user)
return HttpResponseRedirect('/')
else:
print(form.errors)
else:
form = UserCreationForm()
return render(request, 'registration/register.html', {'form': form})
Note, the correct call for form errors in templates is form.errors with s not form.error
{% if form.error %} {% if form.errors %}

Require Email When Creating an Account with Django

I've got a Django project, which requires users to be able create accounts to access content.
I'm using the UserCreationForm to do this.
In views.py I have
def register_user(request):
if request.method == "POST":
user_form = UserCreationForm(request.POST)
if user_form.is_valid():
new_user = user_form.save(commit=False)
new_user.set_password(user_form.cleaned_data["password1"])
new_user.save()
template = "account/registration/registration_done.html"
context = {"new_user": new_user}
else:
# TODO: Handle exception
raise BaseException
elif request.method == "GET":
user_form = UserCreationForm()
template = "account/registration/register.html"
context = {"user_form": user_form}
else:
raise NotImplementedError
return render(request, template, context=context)
And then my template is:
{% extends "base.html" %}
{% block title %}Create an Account{% endblock %}
{% block content %}
<h1>Create an Account</h1>
<form action="." method="post">
{{ user_form.as_p }}
{% csrf_token %}
<p><input type="submit" value="Create my account"></p>
</form>
{% endblock %}
Which works okay. But when the create account form is displayed, it only has fields for the username, password, and password verification. There's no requirement that the user enter a valid email.
What I'd like to do is have a have the user be required to enter an email address, and then send them an email to ensure that the address is valid, and that they have access to is etc.
Surely this is a common enough pattern that there's already a way to implement is using Django's authentication? Or will I need to write all the forms and handling etc myself?
Override the Meta class of the UserCreationForm
In your forms.py
from django.contrib.auth.forms import UserCreationForm
class YourUserCreationForm(UserCreationForm)
class Meta:
fields = ("username", "email")
and use YourUserCreationForm instead

How to save a data after user logs in DJANGO

After user logs in, user is able to submit a form. On click of submit button, data is being stored in DB, but how should I connect this information to the submitting user.
I would need the code as well as the structure of the new db
Kind of starting out in django.
Any help would be appreciated!!!
I have included user as foreign key in the CustomizeRequest model, but now how do i fill in this information?
Exact Scenario: After user log in, once he comes to contactUs.html, he submits a form which tells the number of travellers. This number is being stored in the DB. But now how do I connect each of these numbers to the submitted user?
models.py
class CustomizeRequest(models.Model):
user = models.ForeignKey(User)
travellers = models.CharField(max_length=2)
def __str__(self):
return self.travellers
contactUs.html
<form method="POST" class="form-horizontal">
{% csrf_token %}
<div class="btn-group" data-toggle="buttons">
{% for radio in crform.travellers %}
<label class="btn btn-default {% if radio.choice_label = '1' %}active{% endif %}" for="{{ radio.id_for_label }}">
{{ radio.choice_label }}
{{ radio.tag }}
</label>
{% endfor %}
</div>
<button type="submit" class="btn btn-default btn-block btn-warning">SUBMIT</button>
</form>
views.py
def contactUs(request):
if request.method=="POST":
form = CustomizeRequestForm(request.POST)
form.save()
else:
form = CustomizeRequestForm()
context_dict = {'form': form}
return render(request, 'tour/contactUs.html', context_dict)
Based on catavaran answer (with a check to see if the form is valid):
from django.contrib.auth.decorators import login_required
from django.shortcuts import redirect, render
#login_required
def contactUs(request):
form = CustomizeRequestForm(data=request.POST or None)
if request.method == "POST":
if form.is_valid():
customize_request = form.save(commit=False)
customize_request.user = request.user
customize_request.save()
return redirect('.')
else:
pass # could add a notification here
context_dict = {'form': form}
return render(request, 'tour/contactUs.html', context_dict)
Logged user is available as request.user property. You can get the unsaved model instance using form.save(commit=False) trick, set the user field and then save the instance to database:
from django.contrib.auth.decorators import login_required
from django.shortcuts import redirect, render
#login_required
def contactUs(request):
if request.method == "POST":
form = CustomizeRequestForm(request.POST)
if form.is_valid():
customize_request = form.save(commit=False)
customize_request.user = request.user
customize_request.save()
return redirect('.')
else:
form = CustomizeRequestForm()
context_dict = {'form': form}
return render(request, 'tour/contactUs.html', context_dict)

Django method to change User email not working

I am attempting to create a page where a user can see what their current email is and change it if they would like. I am just testing with a very simple form and a very simple HttpResponseRedirect if the form is not valid. However neither my email is changing for the user nor is my failure response if the form is not valid working. I am not sure what is causing this
forms.py:
class ChangeEmail(forms.Form):
email1 = forms.EmailField(label=u'Type new Email')
email2 = forms.EmailField(label=u'Type Email again')
views.py:
def change_email(request, username):
if request.method == 'POST':
user1 = User.objects.get(username=username)
form1 = ChangeEmail(request.POST)
if form1.is_valid():
user1.email = form.cleaned_data['email1']
form1.save()
return HttpResponseRedirect('/register/success')
else:
return HttpResponseRedirect('/stupid')
else:
user = User.objects.get(username=username)
email = user.email
form = ChangeEmail()
variables = RequestContext(request, {
'form': form,
'email': email
})
return render_to_response('registration/email.html', variables
Thanks for your help in advance.
EDIT:
The URL that I have mapped to render the form is /user/testuser/email. I am attempting to put in invalid input in to the fields to get an error message but when I push submit it redirects me back to /user/testuser page which displays info about the user. My change email template is below:
{% extends "base.html" %}
{% block title %}Change Email{% endblock %}
{% block head %}Change Email{% endblock %}
{% block content %}
<p> Current Email: {{ email }} </p>
<form method="post" action=".">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Change Email" />
</form>
{% endblock %}
ChangeEmail is a normal form. These don't have save methods - only ModelForms do. You're correctly setting the user email from the form's cleaned_data - but you should be saving the user1 object, not the form.
Also, it's best not to redirect away on validation failure. Leave out that first else clause, and move the variables/render_to_response lines back one indentation level, and the form will be redisplayed with any errors.
views.py:
def change_email(request, username):
# a common django idiom for forms
form1 = ChangeEmail(request.POST or None)
user1 = User.objects.get(username=username)
if form1.is_valid():
#check that emails are the same
if form.cleaned_data['email1'] == form.cleaned_data['email2']:
user1.email = form.cleaned_data['email1']
#Save the user object here, since we're not dealing with a ModelForm
user1.save()
return HttpResponseRedirect('/register/success')
# We're presenting them with the empty form if something went wrong
# and redisplaying. The form's field errors should be printed out in
# the template
else:
user = User.objects.get(username=username)
email = user.email
variables = RequestContext(request, {
'form': form,
'email': email
})
return render_to_response('registration/email.html', variables)

Categories