I had password change form like below
forms.py
class PasswordChangeForm(forms.Form):
old_password = forms.CharField(widget=forms.PasswordInput())
new_password = forms.CharField(widget=forms.PasswordInput())
confirm_password = forms.CharField(widget=forms.PasswordInput())
def clean(self):
if self.cleaned_data['new_password'] != self.cleaned_data['confirm_password']:
raise forms.ValidationError(_('The new passwords must be same'))
else:
return self.cleaned_data
template.html
<form action="/save/data/" method="post">
<div>
<p>{{form.old_password}}</p>
<span>{{form.old_password.errors}}</span>
</div>
<div>
<p>{{form.new_password}}</p>
<span>{{form.new_password.errors}}</span>
</div>
<div>
<p>{{form.confirm_password}}</p>
<span>{{form.confirm_password.errors}}</span>
</div>
</form>
views.py
#login_required
def change_password(request):
user_obj = User.objects.get(id=request.user.id)
form = PasswordChangeForm()
if request.method=="POST":
form = PasswordChangeForm(reques.POST)
#########
Here in this part i need to check if the user given old passoword
matched the already saved password in the database, create a password
with the user given new password
#########
new_password = form.cleaned_data['new_password']
......
user_obj.password = new_password
..........
return render_to_response('template.html',{'form':form})
So in the above code, how can we check the password saved in the database with the old password given by the user ?, Also how can we create the new password and sve in to the database ?
After that send an email to user, that ur password has been changed successfully
You have the user object. So you can just call it's set_password method.
request.user.set_password(password)
Also, you don't need to get the user again from the database. You're making an unnecessary DB request. request.user is the user.
I would rewrite the entire view like so,
from django.shortcuts import render
#login_required
def change_password(request):
form = PasswordChangeForm(request.POST or None)
if form.is_valid()
if request.user.check_password(form.cleaned_data['old_password']):
request.user.set_password(form.cleaned_data['new_password'])
request.user.save()
return render(request, 'success.html')
return render(request, 'template.html', {'form':form})
This means that if there is POST data you initialise the form with it. Otherwise it gets None. If the form is valid (which an empty form never will be) then you do the password change and send them to a success page. Otherwise you return the empty form (or the form with the validation errors) to the user.
you can check via check_password method.
if request.user.check_password(form.cleaned_data['old_password']):
request.user.set_password(form.cleaned_data['new_password'])
request.user.save()
Related
I'm new to django and am trying to discover why creating new accounts via my account form do not hash the password (I assume the passwords are not hashed because I cannot log in using the password when the account is created, and this message shows under the password field in the django admin for accounts created via the form: Invalid password format or unknown hashing algorithm). I can successfully create new accounts in the django admin that do not have this un-hashed password issue.
views.py:
#unauthenticated_user
def create_account(request):
form = AccountForm()
if request.method == 'POST':
form = AccountForm(request.POST)
# should hash the password, check username/email doesnt already exist, etc
if form.is_valid():
user = form.save()
return redirect('/login')
else:
messages.info(request, "Count not create account.")
context = {'form': form}
return render(request, 'accounts/create_account.html', context)
models.py:
class Account(AbstractUser):
def __str__(self) -> str:
return self.first_name
pass
Create account form:
<form action="{% url 'create_account' %}" method="POST">
{% csrf_token %}
{{form.as_p}}
<input type="submit" name="submit">
</form>
The form:
class AccountForm(ModelForm):
class Meta:
model = Account # which model we're building a form for
# password not hashed and requires username even if username omitted from fields
fields = ['email', 'password', 'first_name', 'last_name', 'username']
I'm following a tutorial series where the only difference with my code is that I extend from the AbstractUser model with the Account class (so that I can change the create user form to only require an email and password instead of a username and password). Unless I'm incorrect, I thought the AbstractUser model should automatically hash passwords for you.
Where am I going wrong here?
As #purple mentioned, use set_password(...)--Doc method as
#unauthenticated_user
def create_account(request):
form = AccountForm()
if request.method == 'POST':
form = AccountForm(request.POST)
if form.is_valid():
user = form.save(commit=False) # set `commit=False`
user.set_password(
form.cleaned_data["password"]
) # call `set_password(...)` with "raw password"
user.save() # save the actual User instance
return redirect('/login')
else:
messages.info(request, "Count not create account.")
context = {'form': form}
return render(request, 'accounts/create_account.html', context)
Use set_password method
def create_account(request):
form = AccountForm()
if request.method == 'POST':
form = AccountForm(request.POST)
# should hash the password, check username/email doesnt already exist, etc
if form.is_valid():
form.set_password(request.POST['password'])
form.save()
return redirect('/login')
else:
messages.info(request, "Count not create account.")
context = {'form': form}
return render(request, 'accounts/create_account.html', context)
ValueError at /register/
The given username must be set
Request Method: POST
Request URL: http://127.0.0.1:8000/register/
Django Version: 1.11.4
Exception Type: ValueError
Exception Value:
The given username must be set
here's the views.py
def register_page(request):
form = RegisterForm(request.POST or None)
context = {
"form": form
}
if form.is_valid():
print(form.cleaned_data)
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)
print(username)
return render(request, "auth/register.html", context)
This is sending None as a username and i dont know why?
user = User.objects.create_user(username,email,password)
How to resolve this issue? It is not getting the username with POST method or there is some other mistake?
First, you are doing lots of things even though you are not in the POST case.
I suggest you check whether you are in POST or not.
if request.method == 'POST':
If you are in POST, you can retrieve the form with :
form = RegisterForm(request.POST)
if you are not in POST, you must send an empty form to the page with :
form = RegisterForm()
Then, after having checked that the form is valid, you can already create your new user.
user = User()
Note that you must import the class User with : from django.contrib.auth.models import User
Now you can retrieve the data from your form as you did, and assign it to your new empty User:
user.username = form.cleaned_data.get('username')
user.email = form.cleaned_data.get('email')
For the password, you should encrypt it with :
user.password = make_password(form.cleaned_data.get('password'))
Note that you must import the make_password method : from django.contrib.auth.hashers import make_password
After all this, you can save your new user to the database :
user.save()
Note that you should probably do the save into a try: ... except block in order to catch potential exception caused by IntegrityError if several users try to register with the same username or such, depending on the Integrity rules that Django uses for his User model by default (which I don't remember)
Finally, your view should look like that (or similar) :
def register_page(request):
if request.method == 'POST':
form = RegisterForm(request.POST)
if form.is_valid():
user = User()
user.username = form.cleaned_data.get('username')
user.email = form.cleaned_data.get('email')
user.password = make_password(form.cleaned_data.get('password'))
user.save()
else:
form = RegisterForm()
return render(request, 'auth/register.html', {'form': form})
It should work considering you made your imports, a proper RegisterForm, and a proper auth/register.html
I'm trying to implement resetting password in Django 1.6 with my own templates instead of the custom Django administration auth_view and I've mainly followed this tutorial;https://ruddra.com/posts/implementation-of-forgot-reset-password-feature-in-django/. I've managed to customize the reset password page and the email sent for resetting the password but the confirmation page, which the resetting link is sent to is blank when I try to use the PasswordResetConfirm() class without Django administration forms (which work). In short, when clicking the email link for resetting password the webpage is blank but titled Confirmation at the top, so something in the code is blocking or missing. I've tried numerous of tutorials but nothing works. I've changed /rest/.. to account/reset in the URL's, which matches the link in the email and now works for getting to the PasswordResetConfirmView(), but it renders the error 'SetPasswordForm' object has no attribute 'set_cookie', how can I fix this in Django 1.6?. I've also noticed that I can't import update_session_auth_hash, which many tutorial use, in Django 1.6 and it seems to exist in Django 1.7 and later versions. Instead I'm trying to use the password hashers PBKDF2PasswordHasher, SHA1PasswordHasher in this tutorial; https://apimirror.com/django~1.9/topics/auth/passwords but I'm not sure if it's related to the Attribute error about set_cookies in the SetPasswordForm.
I've tried by placing 'django.contrib.admin' in INSTALLED_APPS in Settings after the applications, which 'unplugs' the custom Django administration form for changing password in the confirmation step to a blank page with the text Confirmation on top. I've also changed the template password_reset_confirm.html
In views.py, following from linked tutorial
class PasswordResetConfirmView(FormView):
template_name = "registration/password_reset_confirm.html"
success_url = 'registration/password_reset_complete.html'
form_class = SetPasswordForm
def post(self, request, uidb64=None, token=None, *arg, **kwargs):
"""
View that checks the hash in a password reset link and presents a
form for entering a new password.
"""
UserModel = get_user_model()
form = self.form_class(request.POST)
assert uidb64 is not None and token is not None # checked by URLconf
try:
uid = urlsafe_base64_decode(uidb64)
user = UserModel._default_manager.get(pk=uid)
except (TypeError, ValueError, OverflowError,UserModel.DoesNotExist):
user = None
if user is not None and default_token_generator.check_token(user,
token):
if form.is_valid():
new_password= form.cleaned_data['new_password2']
user.set_password(new_password)
user.save()
messages.success(request, 'Password has been reset.')
return self.form_valid(form)
else:
messages.error(request, 'Password reset has not been
unsuccessful.')
return self.form_invalid(form)
else:
messages.error(request,'The reset password link is no longevalid.')
return self.form_invalid(form)```
In urls.py
url(r'^account/password_reset/', ResetPasswordRequestView.as_view(),
name="reset_password"),
url(r'^account/password/reset/done/', ResetPasswordRequestView.as_view(),
name="reset_password_done"),
url(r'^reset/(?P<uidb64>[0-9A-Za-z]+)/(?P<token>.+)/$',
PasswordResetConfirmView.as_view(),name='password_reset_confirm'),
# changed url to
url(r'^account/reset/(?P<uidb64>[0-9A-Za-z]+)/(?P<token>.+)/$',
PasswordResetConfirmView.as_view(),name='password_reset_confirm'),
# which matches email link
{{ protocol }}://{{ domain }}/account/reset/{{ uid }}/{{ token }}/
In password_reset_confirm.html in the Registration folder
{% extends 'base.html' %}
{% block title %}Enter new password{% endblock %}
{% block content %}
<h1>Set a new password!</h1>
<form method="POST">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Change my password">
</form>
{% endblock %}
# in forms.py from tutorial, works in the tutorial example but yields
# an Attribute error that the form doesn't have set_cookies
# after disconnecting from Djando Admin confirmation forms used in the
#tutorial
class SetPasswordForm(forms.Form):
# """
#
#A form that lets a user change set their password without entering the old
# password
# """
error_messages = {
'password_mismatch': ("The two password fields didn't match."),
}
new_password1 = forms.CharField(label=("New password"),
widget=forms.PasswordInput)
new_password2 = forms.CharField(label=("New password confirmation"),
widget=forms.PasswordInput)
def clean_new_password2(self):
password1 = self.cleaned_data.get('new_password1')
password2 = self.cleaned_data.get('new_password2')
if password1 and password2:
if password1 != password2:
raise forms.ValidationError(
self.error_messages['password_mismatch'],
code='password_mismatch',
)
return password2
I've managed to solve the problem in my question. If anyone runs into the same problem, I did the following;
The tutorial example worked but the admin view showed up even after removing all of the Django administration (admin) related tags in the template password_reset_confirm.py (even after running manage.py syncdb and tossing and starting a new sql-db), so I used a new template called test_reset_confirm.html in the Registration folder with the form related code part (middle section) from the original password_reset_confirm.py, and I filled in the form information by adding enctype = multipart/form-data;
and info in the password field
In views.py I changed the PasswordResetConfirmView from a class to a function, following Example 9 in this tutorial; https://www.programcreek.com/python/example/54414/django.contrib.auth.forms.SetPasswordForm;
def PasswordResetConfirmView(request, uidb64=None, token=None,
token_generator=default_token_generator,
post_reset_redirect=None,current_app=None):
"""
View that checks the hash in a password reset link and presents a
form for entering a new password.
"""
UserModel = get_user_model()
form = SetPasswordForm(request.POST)
template_name='registration/test_reset_confirm.html'
assert uidb64 is not None and token is not None # checked by URLconf
try:
uid = urlsafe_base64_decode(uidb64)
user = UserModel._default_manager.get(pk=uid)
except (TypeError, ValueError, OverflowError, UserModel.DoesNotExist):
user = None
if user is not None and token_generator.check_token(user, token):
validlink = True
if request.method == 'POST':
form = SetPasswordForm(request.POST)
if form.is_valid():
print request.POST,'request.POST'
print form.cleaned_data,'form.cleaned_data'
new_password= form.cleaned_data['new_password2']
user.set_password(new_password)
user.save()
#form.save()
messages.add_message(request, messages.SUCCESS, 'Your password has now been changed and you can login on any system site!',fail_silently=True)
return HttpResponseRedirect(post_reset_redirect)
else:
form = SetPasswordForm()
c = {"form":form}
c.update(csrf(request))
return TemplateResponse(request, template_name, c,
current_app=current_app)
# Setpasswordform in forms.py
class SetPasswordForm(forms.Form):
new_password1 = forms.CharField(widget=forms.PasswordInput)
new_password2 = forms.CharField(widget=forms.PasswordInput)
error_messages = {
'password_mismatch': ("The two password fields didn't match."),
}
class Meta:
model = User
fields = ('password',)
def __init__(self,*args, **kwargs):
super(SetPasswordForm, self).__init__(*args, **kwargs)
def clean(self):
cleaned_data = super(SetPasswordForm, self).clean()
return cleaned_data
The answer provided by the original poster does not apply any password validators, which means that the user can set the password to something very unsafe.
Below is updated code which will apply the password validation properly:
views.py:
def set_password_confirm_request(request, uidb64=None, token=None,
token_generator=default_token_generator,
post_reset_redirect=None,current_app=None):
if request.method == "POST":
try:
uid = urlsafe_base64_decode(uidb64)
user = User.objects.filter(pk=uid).first()
except (TypeError, ValueError, OverflowError, User.DoesNotExist):
user = None
if user is not None and token_generator.check_token(user, token):
set_password_form = SetPasswordForm(request.POST)
if set_password_form.is_valid():
new_password = set_password_form.cleaned_data['new_password2']
user.set_password(new_password)
user.save()
return render(request=request, template_name="accounts/login.html",
context={"message": "Your password has been updated.", "message_color": "green"})
if set_password_form.data["new_password1"] != set_password_form.data["new_password2"]:
errors = ["Passwords do not match."]
else:
errors = set_password_form.errors["new_password2"]
return render(request=request, template_name="accounts/password_reset_confirm.html",
context={"message": errors, "message_color": "red"})
set_password_form = PasswordResetForm()
return render(request=request, template_name="accounts/password_reset_confirm.html", context={"password_reset_confirm_form": set_password_form})
forms.py:
from django.contrib.auth.password_validation import validate_password
class SetPasswordForm(forms.Form):
new_password1 = forms.CharField(widget=forms.PasswordInput, validators=[validate_password])
new_password2 = forms.CharField(widget=forms.PasswordInput, validators=[validate_password])
error_messages = {
'password_mismatch': ("The two password fields didn't match."),
}
class Meta:
model = User
fields = ('password',)
def __init__(self,*args, **kwargs):
super(SetPasswordForm, self).__init__(*args, **kwargs)
I have a simple user registration form (in forms.py):
from django.contrib.auth.models import User
class UserForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput
validators=[MinLengthValidator(6)])
password_repeat = forms.CharField(widget=forms.PasswordInput)
class Meta:
model = User
fields = ['username', 'password','password_repeat']
If someone tries to enter something and the validation fails I want the same form to be rendered again but all fields should be cleared. At the moment my view looks like this (in views.py):
def signup(request):
form = UserForm(request.POST or None)
if form.is_valid():
user = form.save(commit=False)
username = form.cleaned_data['username']
password = form.cleaned_data['password']
password_repeat = form.cleaned_data['password-repeat']
user.set_password(password)
user.save()
user = auth.authenticate(username=username, password=password)
if user is not None and user.is_active:
auth.login(request, user)
return redirect('/')
return render(request, 'signup.html', {'form': form})
The problem is that the form.fields['username'] field still contains the username that was entered and is thus passed to render.
I've been searching for a solution a while now but can't find it. My guess is that the solution has something to do with the clean() method that I don't seem to get.
This is an odd thing to want to do - it is the opposite of the question people normally ask, as most people want to preserve the fields and show the errors.
However, if you really want to clear the form, you should just instantiate a new one.
if form.is_valid():
...
else:
form = UserForm()
return render(request, 'signup.html', {'form': form})
To always clear a particular form field while preserving all form validation errors, you can create a custom input widget that always "forgets" its old value. For example:
from django import forms
class NonstickyTextInput(forms.TextInput):
'''Custom text input widget that's "non-sticky"
(i.e. does not remember submitted values).
'''
def get_context(self, name, value, attrs):
value = None # Clear the submitted value.
return super().get_context(name, value, attrs)
class MyForm(forms.Form):
username = forms.CharField(widget=NonstickyTextInput())
# ...
Reference: django.forms.Widget.get_context
Behavior
Suppose we are using MyForm in such a view:
from django.shortcuts import render, redirect
from myapp.forms import MyForm
def myview(request):
if request.method == 'POST':
form = MyForm(request.POST)
if form.is_valid():
# Do something with the submitted values...
return redirect('home_page')
else:
form = MyForm()
return render(request, 'myapp/myview.html', {'form': form})
When the form encounters any validation error and the form is re-displayed, all the usual validation error messages will be shown on the form, but the displayed username form field will be blank.
I am trying to create a portable auth system that can be plugged in apps, and each different app I reimplement it in has the same issues.
1-Sometimes the user that recently logged in gets their sn in the email address field when a new user tries to register, as below
2- Sometimes a new user registers and logs out but the form will put the old user's email address and password in the appropriate fields, when of course I want the form to be blank if the user has logged out
3- always the last password used is filled in upon reload
I just want the form to completely clear itself when reloaded
How to clear form fields after a submit in Django
I have tried all 3 solutions from a similar question, I reinstantiated the from after saving the valid one, made a copy of request.POST and used that instead, and I was already redirecting to begin with. Here is my form
from django.contrib.auth.models import User
class UserForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput())
class Meta:
model = User
fields = ('username', 'email', 'password')
in views.py
def register(request):
context = RequestContext(request)
registered = False
user_form = UserForm()
if request.method == 'POST':
pDict = request.POST.copy()
form = UserForm(pDict)
if form.is_valid():
user = form.save()
user.set_password(user.password)
user.save()
user_form = UserForm()
registered = True
username = pDict['username']
password = pDict['password']
user = authenticate(username=username, password=password)
login(request, user)
#locals isn't working? won't print user
return HttpResponseRedirect('/url/')
else:
print user_form.errors
template_name = 'accounts/register.html'
user_form = UserForm()
response = TemplateResponse(request, 'accounts/register.html', locals())
return response
thank you