I have a model 'Admin' related to a (custom) user within a one to one field. To update all the fields I'm using django-extra-views app that basically use Django formset. It works well but for updating the password.
Models
class Users(AbstractUser):
# common fields for all the users
class Admins(models.Model):
# Custom fields for the admins
Forms:
class AdminProfileForm(forms.ModelForm):
class Meta:
model = Users
fields = ['first_name', 'last_name']
password1 = forms.CharField(widget=forms.PasswordInput, label='New Password')
password2 = forms.CharField(widget=forms.PasswordInput, label='Confirm Password')
Views:
class AdminInline(InlineFormSet):
model = Admins
fields = ['admins_fiels_here']
class AdminProfile(BaseContext, UpdateWithInlinesView):
....
inlines = [AdminInline]
form_class = AdminProfileForm
def get_object(self):
return Users.objects.get(pk=self.request.user.id)
def forms_valid(self, formset, inlines):
password1 = formset.cleaned_data['password1']
password2 = formset.cleaned_data['password2']
if password2 and password1:
if password1 != password2:
# raise a ValidationError message
user = self.request.user # at this point password is 'old_passord'
user.set_password(password1) # now is 'password1'
user.save() # 'password1' is correctly saved in db
return super(AdminProfile, self).forms_valid(formset, inlines)
As I describe above, the new password is saved but when super(AdminProfile, self).forms_valid(formset, inlines) ends its works the password results to be the old one.
Related
I have a small question about Django so I have a custom Registration Form which inherits from "UserCreationForm" and therefore I use "fields" to define the fields I want to display. For this test just ("username", "email",). But when I go to see my form which is displayed "Password" and "Password confirmation" are also present. Wanting to understand, I therefore went to see the UserCreationForm class of django but this one has a Meta class which has fields = ("username"), so I don't really see why I have the passwords which are also present. I'm sorry the explanation is a bit laborious but I would like to understand so if anyone has a little explanation thank you very much.
My custom class:
class CustomSignupForm(UserCreationForm):
class Meta:
model = CustomUser
fields = ("username", "email",)
The UserCreationForm of django:
class UserCreationForm(forms.ModelForm):
"""
A form that creates a user, with no privileges, from the given username and
password.
"""
error_messages = {
"password_mismatch": _("The two password fields didn’t match."),
}
password1 = forms.CharField(
label=_("Password"),
strip=False,
widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}),
help_text=password_validation.password_validators_help_text_html(),
)
password2 = forms.CharField(
label=_("Password confirmation"),
widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}),
strip=False,
help_text=_("Enter the same password as before, for verification."),
)
class Meta:
model = User
fields = ("username",)
field_classes = {"username": UsernameField}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self._meta.model.USERNAME_FIELD in self.fields:
self.fields[self._meta.model.USERNAME_FIELD].widget.attrs[
"autofocus"
] = True
def clean_password2(self):
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise ValidationError(
self.error_messages["password_mismatch"],
code="password_mismatch",
)
return password2
def _post_clean(self):
super()._post_clean()
# Validate the password after self.instance is updated with form data
# by super().
password = self.cleaned_data.get("password2")
if password:
try:
password_validation.validate_password(password, self.instance)
except ValidationError as error:
self.add_error("password2", error)
def save(self, commit=True):
user = super().save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user
Here is what it gives:
Display of my form
The reason this happens is that the password1 and password2 field are defined in the UserCreationForm class. The form fields do not need to be listed in the fields attribute: all field objects in the class are form fields.
A ModelForm will in essence add form field objects to the class, and thus automates that process, but any field object defined in the class is a form field, that is how a simple Form [Django-doc] behaves, and a ModelForm is just a subclass of Form.
Based on your comment you want to reorder the fields. You can do so with the .field_order attribute [Django-doc]:
class CustomSignupForm(UserCreationForm):
field_order = ('username', 'email', 'password1', 'password2', 'newsletter')
class Meta:
model = CustomUser
fields = ('username', 'email', 'newsletter')
I want to have a user sign up form in Django, I know that for the Backend I should have something like this:
>>> from django.contrib.auth.models import User
>>> user = User.objects.create_user('john', 'lennon#thebeatles.com', 'johnpassword')
>>> user.last_name = 'Lennon'
>>> user.save()
However, I don't know how to make the Frontend. I've already looked up in the Django documentation and found the UserCreationForm class and it says it's deprecated.
What should I do? Thank you
Try something like this:
#forms.py
class UserCreationForm(forms.ModelForm):
"""A form for creating new users. Includes all the required
fields, plus a repeated password."""
password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
class Meta:
model = MyUser
fields = ('email', 'date_of_birth')
def clean_password2(self):
# Check that the two password entries match
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise ValidationError("Passwords don't match")
return password2
def save(self, commit=True):
# Save the provided password in hashed format
user = super().save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user
You should read this section of the Django Docs on authentication.
I am making a registration form for 3 types of users. When a user enters email and password he/she must select one of the roles.
First I used BooleanFields and it works, but more than one checkbox can be selected. I need that user can select only one role.
I have tried ChoiceField, which I could display on the template but it does not POST any data to db.
forms.py
class RegisterForm(forms.ModelForm):
"""A form for creating new users. Includes all the required
fields, plus a repeated password."""
password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
parent = forms.BooleanField(label="I am a Parent")
school = forms.BooleanField(label="I am a School Admin")
vendor = forms.BooleanField(label="I am a Vendor")
role_select=forms.ChoiceField(
widget=forms.RadioSelect,
label="Select your role.",
choices=(('is_parent','parent '),('is_school','school'),('is_vendor','vendor')),
)
if not parent and not school and not vendor:
raise forms.ValidationError("Users must have a role")
class Meta:
model = User
fields = ['role_select', 'parent', 'school', 'vendor', 'email'] #'full_name',)
def clean_password2(self):
# Check that the two password entries match
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise forms.ValidationError("Passwords don't match")
return password2
def save(self, commit=True):
# Save the provided password in hashed format
user = super(RegisterForm, self).save(commit=False)
user.set_password(self.cleaned_data["password1"])
user.role_select( self.cleaned_data['role_select'])
# user.active = False # send confirmation email
if commit:
user.save()
return user
As you see in the forms.py I have a combination of two approaches. So it has some useless lines. Which approach to use and how?
you can use Django groups where you can create a group and assign it as a choicefield when user registers. You can check my repository. I have done the same thing here. Hope it can get you some idea
https://github.com/tsephel/User-authentication-django-/tree/master/env
I created a customized User Admin change form but when I create a new user it goes to the Change form.
The Base Admin add form meets what I want hence why I want to keep it?
Also how do I encrypt the password once I reverted back to the Base admin create form?
How do I change this?
Admin.py:
from django.contrib import admin
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from .forms import UserAdminChangeForm
from .models import User
# Register your models here.
User=get_user_model()
class UserAdmin(admin.ModelAdmin):
form = UserAdminChangeForm
search_fields=['username','user_type']
list_display=('username','full_name','password','email','user_type','ad','pqa','ts','tl','tm','stm','active')
list_filter = ('ad',)
fieldsets = (
(None, {'fields': ('username', 'password')}),
('Personal info', {'fields': ('full_name','birth_date','hire_date',)}),
('Permissions', {'fields': ('ad','tm','pqa','stm','ts','tl')}),
)
class Meta:
model = User
admin.site.register(User,UserAdmin)
forms.py:
from django import forms
from django.contrib.auth.forms import ReadOnlyPasswordHashField
from .models import User
class RegisterForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput)
password2 = forms.CharField(label='Confirm password', widget=forms.PasswordInput)
usertype= forms.Select(choices=User.USER_TYPE_CHOICES)
class Meta:
model = User
fields = ('username',)
def clean_username(self):
username = self.cleaned_data.get('username')
qs = User.objects.filter(username=username)
if qs.exists():
raise forms.ValidationError("Username is taken")
return username
def clean_password2(self):
# Check that the two password entries match
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise forms.ValidationError("Passwords don't match")
return password2
def save(self, commit = True):
user = super(RegisterForm, self).save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user
class UserAdminCreationForm(forms.ModelForm):
"""
A form for creating new users. Includes all the required
fields, plus a repeated password.
"""
password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
usertype= forms.Select(choices=User.USER_TYPE_CHOICES)
class Meta:
model = User
fields = ['username','password','user_type']
def clean_password2(self):
# Check that the two password entries match
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise forms.ValidationError("Passwords don't match")
return password2
def save(self, commit=True):
# Save the provided password in hashed format
user = super(UserAdminCreationForm, self).save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user
class UserAdminChangeForm(forms.ModelForm):
"""A form for updating users. Includes all the fields on
the user, but replaces the password field with admin's
password hash display field.
"""
password = ReadOnlyPasswordHashField()
class Meta:
model = User
fields = ('username', 'password', 'active', 'ad')
def clean_password(self):
# Regardless of what the user provides, return the initial value.
# This is done here, rather than on the field, because the
# field does not have access to the initial value
return self.initial["password"]
I really appreciate the help. TIA
I currently have this ModelForm for validating a new user registration:
class RegistrationForm(forms.ModelForm):
email = forms.CharField(max_length=75, required=True)
password = forms.PasswordInput()
password_confirm = forms.PasswordInput()
class Meta:
model = User
fields = ['username', 'email', 'password']
def clean(self):
if self.password != self.password_confirm:
self.add_error('password_confirm', 'Passwords do not match.')
The user is required to confirm his password. When submitting this form I get the following error:
ValueError at /register
'RegistrationForm' has no field named 'password_confirm'.
I tried using the self.cleaned_data as well, but still get the same error.
The fields attribute cannot be removed nor can password_confirm be added to it.
How would one go about fixing this?
password and password_confirm are defined as widgets, not form fields.
Define two CharFields and pass the widget argument:
class RegistrationForm(forms.ModelForm):
email = forms.CharField(max_length=75, required=True)
password = forms.CharField(widget=forms.PasswordInput)
password_confirm = forms.CharField(widget=forms.PasswordInput)
You need to call the super clean first, and then you should use the cleaned data rather than the field.
def clean(self):
cleaned_data = super(RegistrationForm, self).clean()
if cleaned_data.get('password') != cleaned_data.get('password_confirm'):
self.add_error('password_confirm', 'Passwords do not match.')