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)
Related
I am trying to allow users create an account from without using the from django.forms import UserCreationForm. I just want the users to use just the input field and i can grab unto what ever they are passing into the input field and create an account for them.
This is the django forms for creating users with UserCreationForm, how do i now do the same but without the UserCreationForm?
views.py
def RegisterView(request):
if request.method == "POST":
form = UserRegisterForm(request.POST or None)
if form.is_valid():
new_user = form.save()
username = form.cleaned_data.get('email')
messages.success(request, f'Account Created')
new_user = authenticate(username=form.cleaned_data['email'],
password=form.cleaned_data['password1'],)
login(request, new_user)
return redirect('index')
elif request.user.is_authenticated:
return redirect('index')
else:
form = UserRegisterForm()
context = {
'form': form,
}
return render(request, 'userauths/sign-up.html', context)
forms.py
from django.contrib.auth.forms import UserCreationForm
from userauths.models import User
class UserRegisterForm(UserCreationForm):
class Meta:
model = User
fields = ['username', 'email', 'password1', 'password2']
You can do this by creating your own user modelForm.
This will include multiple steps:
validate form
save form
set password using user.set_password() method.
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 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 using django's UserCreationForm to sign up users. It works perfectly except for the errors. I cannot get them to render. I don't think there is anything wrong with the template as I have tried this with the most basic of templates and using form.as_p and form.as_table and still the same the registration works but if you put 2 different passwords in it just refreshes the screen with an empty form and no errors. Also I have tried sending the form.errors through the django messages and it passes the correct error when there is one but this solution is not practical for me.
It wont let me post the template because of indenting. I am using
{{form.non_field_errors}} at the top of the form and then {{ form.email.error }} etc.
Please help if you can:)
Form class..
class MyRegistrationForm(UserCreationForm):
email = forms.EmailField(required=True)
class Meta:
model = User
fields=('username', 'email', 'password1', 'password2')
def save(self, commit=True):
User= super(MyRegistrationForm, self).save(commit=False)
User.email = self.cleaned_data["email"]
if commit:
User.save()
return User
View method...
def home(request):
args = {}
args.update(csrf(request))
if request.method =='POST':
form = MyRegistrationForm(request.POST)
if form.is_valid():
##save_it = form.save(commit=False)
form.save()
messages.success(request, 'Thank you for joining!')
#return HttpResponseRedirect('thank-you')
return render_to_response('thankyou.html', locals(), context_instance=RequestContext(request))
else:
args['form'] = MyRegistrationForm()
return render_to_response('signup.html', args, context_instance=RequestContext(request))
args={}
args.update(csrf(request))
args['form'] = MyRegistrationForm()
context = RequestContext(request,
{'user': request.user})
return render_to_response('signup.html', args,
context_instance=context)
You are explicitly recreating the form if it has errors, replacing it in the context with one that is unbound and therefore doesn't have errors. Don't do that. Your view should be simply:
def home(request):
if request.method =='POST':
form = MyRegistrationForm(request.POST)
if form.is_valid():
form.save()
messages.success(request, 'Thank you for joining!')
return HttpResponseRedirect('thank-you')
else:
form = MyRegistrationForm()
return render(request, 'signup.html', {'form': form})
Note the other changes: always redirect after a successful post; render rather than render_to_response, since it creates a RequestContext for you; and no need to add the user or csrf values yourself, since they are added by the context processors as long as you do use a RequestContext.
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()