Extending user admin form in django - django

I'm trying to change user admin in Django. In my project the email address, first name and last name is required. I changed my user admin as following :
class UserForm(forms.ModelForm):
class Meta:
model = User
def __init__(self, *args, **kwargs):
super(UserForm, self).__init__(*args, **kwargs)
self.fields['email'].required = True
self.fields['first_name'].required = True
self.fields['last_name'].required = True
class UserAdmin(admin.ModelAdmin):
form = UserForm
list_display = ('first_name','last_name','email','is_active')
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
The problem is whenever I save a user with a password, it's displayed as without hashing. I guess the problem is, I need to hash the password field with my new form. But the old form does it, so is there a way that I can extend the oldform ?

You can subclass the existing UserChangeForm in django.contrib.auth.forms, and customise its behaviour, rather than subclassing forms.ModelForm.
from django.contrib.auth.forms import UserChangeForm
class MyUserChangeForm(UserChangeForm):
def __init__(self, *args, **kwargs):
super(MyUserChangeForm, self).__init__(*args, **kwargs)
self.fields['email'].required = True
self.fields['first_name'].required = True
self.fields['last_name'].required = True
class UserAdmin(admin.ModelAdmin):
form = MyUserChangeForm
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
The above will use the default behaviour for the user password, which is to display the password hash, and link to the password change form. If you want to modify that, I would look at SetPasswordForm, to see how the password is set in the Django admin.

Related

Django: How change the password of another user

I'm constructing site by Django.
I'm setting some authentications in this site, like Site Manager, Group Manager and Normal User.
When System Manager or Group Manager logged in, they could change the password of Normal Users as a part of system management.
In django, PasswordChangeForm or SetPasswordForm provide some user password change form. I think these forms are for changing password of users themselves.
Yes, I need that too. But I also need the form of changing the password of another users like django admin site.
How can I construct the form ?
I read the code of django's admin site.
I found AdminPasswordChangeForm, can I use this class ?
forms.py
class MyAdminPasswordChangeForm(AdminPasswordChangeForm):
def __init__(self, user, *args, **kwargs):
self.user = user
super().__init__(user, *args, **kwargs)
for field in self.fields.values():
field.widget.attrs['class'] = 'form-control'
views.py
class PasswordChange(PasswordChangeView):
form_class = MyAdminPasswordChangeForm
success_url = reverse_lazy('password_change_done')
template_name = 'accounts/password_change/password_admin_change.html'
def get(self, request, *args, **kwargs):
if 'user_id' in self.request.session:
user = User.objects.get(id=self.request.session['user_id'])
form_class = self.get_form_class()
form = form_class(user=user)
return self.render_to_response(self.get_context_data())
def get_form_class(self):
return self.form_class
you can use set_password() for change user passwords
first you should select user with such code
user=User.objects.get(username=username)
then you can set password
user.set_password(password)
user.save()

Edit password of user in admin panel

I've overwrite "save_model" method to manage user's password in my admin's panel application. What I want is:
Create a new random password when I create new user (if password field is empty)
Encrypt the password (if I set it)
Use user's password (if I change user but not set the password)
How can I define the last condition?
def save_model(self, request, obj, form, change):
if not change and (not form.cleaned_data['password']) :
password = User.objects.make_random_password()
obj.set_password(password)
elif form.cleaned_data['password'] :
obj.set_password(form.cleaned_data['password'])
else
?
super(UserAdmin, self).save_model(request, obj, form, change)
You are doing all this a bit too late. The user creation form won't even allow you to post a form without fields password1 and password2 filled in. If those fields are empty you will never even get to UserAdmin.save_model() method.
But you can override the user creation form's save() method, in it allow password fields to be empty and set random password if fields are empty (or use user's password if entered).
As a bonus, this code keeps in place form validation if user entered the password (password mismatch) as well as checking AUTH_PASSWORD_VALIDATORS (all except 'django.contrib.auth.password_validation.MinimumLengthValidator' since empty password field mean 0 chars).
### admin.py
from django import forms
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
class CustomUserCreationForm(UserCreationForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['password1'].required = False
self.fields['password2'].required = False
def _clean_form(self):
super()._clean_form()
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 != password2:
self.add_error('password2', self.error_messages['password_mismatch'])
def save(self, commit=True):
user = super().save(commit=False)
user_password = self.cleaned_data["password1"]
if not user_password:
user_password = User.objects.make_random_password()
user.set_password(user_password)
if commit:
user.save()
return user
class CustomUserAdmin(UserAdmin):
add_form = CustomUserCreationForm
# Re-register UserAdmin
admin.site.unregister(User)
admin.site.register(User, CustomUserAdmin)

how to make email field required in the django user admin

I want to make the email field required in the user admin add and
change pages. I read this post: Django User model email field: how to make it mandatory and I did this:
class MyUserCreationForm(UserCreationForm):
def __init__(self, *args, **kwargs):
super(MyUserCreationForm, self).__init__(*args, **kwargs)
# make user email field required
self.fields['email'].required = True
class UserAdmin(BaseUserAdmin):
form = MyUserCreationForm
add_form = MyUserCreationForm
add_fieldsets = ((None, {'fields': ('username', 'email',
'password1', 'password2'), 'classes': ('wide',)}),)
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
This works fine in add user, but in change user I get the user's
encrypted password shown in the password field, instead of what you
normally see:
algorithm: pbkdf2_sha256 iterations: 24000 salt: ****** hash:
**************************************
Raw passwords are not stored, so there is no way to see this user's
password, but you can change the password using this form.
And when I try to save from the change screen it says "Please correct
the errors below." even though there are no errors shown.
How can I fix these issues in the change form?
Have a look at the source code of the UserAdmin.
The UserAdmin uses a UserChangeForm as it's formproperty and a UserCreationForm as it's add_form property. But you have overridden both of them with a class derived from UserCreationForm which is okay for the create view but doesn't work for the update view .
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from django.contrib.auth.models import User
class EmailRequiredMixin(object):
def __init__(self, *args, **kwargs):
super(EmailRequiredMixin, self).__init__(*args, **kwargs)
# make user email field required
self.fields['email'].required = True
class MyUserCreationForm(EmailRequiredMixin, UserCreationForm):
pass
class MyUserChangeForm(EmailRequiredMixin, UserChangeForm):
pass
class EmailRequiredUserAdmin(UserAdmin):
form = MyUserChangeForm
add_form = MyUserCreationForm
add_fieldsets = ((None, {
'fields': ('username', 'email', 'password1', 'password2'),
'classes': ('wide',)
}),)
admin.site.unregister(User)
admin.site.register(User, EmailRequiredUserAdmin)
This should do the trick.

Force User to Provide Profile Before Saving in Django Admin

Am trying to force a user to provide a profile before they are saved from the django admin.
here is my profile model
class UserProfile(models.Model):
user=models.OneToOneField(AUTH_USER_MODEL,related_name='profile',primary_key=True)
#other fields
def get_user_info(user):
return UserProfile.objects.get(user=user)
#receiver(post_save, sender=AUTH_USER_MODEL)
def create_profile_for_new_user(sender, created, instance, **kwargs):
if created:
profile = UserProfile(user=instance)
profile.save()
Am using authtools so the user profile sender is AUTH_USER_MODEL.
If I add a new user from within admin,they are saved even if they have not provided or filled in the profile.
I want to prevent them from being saved until they fill in the profile fields.
Any insights on how to do this?
Here is my admin.py
from django import forms
from django.contrib.auth import get_user_model
from django.contrib.auth.forms import PasswordResetForm
from django.utils.crypto import get_random_string
from authtools.admin import NamedUserAdmin
from authtools.forms import UserCreationForm
User = get_user_model()
class UserCreationForm(UserCreationForm):
"""
A UserCreationForm with optional password inputs.
"""
def __init__(self, *args, **kwargs):
super(UserCreationForm, self).__init__(*args, **kwargs)
self.fields['password1'].required = False
self.fields['password2'].required = False
# If one field gets autocompleted but not the other, our 'neither
# password or both password' validation will be triggered.
self.fields['password1'].widget.attrs['autocomplete'] = 'off'
self.fields['password2'].widget.attrs['autocomplete'] = 'off'
def clean_password2(self):
password1 = self.cleaned_data.get("password1")
password2 = super(UserCreationForm, self).clean_password2()
if bool(password1) ^ bool(password2):
raise forms.ValidationError("Fill out both fields")
return password2
class UserAdmin(NamedUserAdmin):
"""
A UserAdmin that sends a password-reset email when creating a new user,
unless a password was entered.
"""
inlines = [ UserProfileInline, ]
add_form = UserCreationForm
add_fieldsets = (
(None, {
'description': (
"Enter the new user's name and email address and click save."
" The user will be emailed a link allowing them to login to"
" the site and set their password."
),
'fields': ('email', 'name',),
}),
('Password', {
'description': "Optionally, you may set the user's password here.",
'fields': ('password1', 'password2'),
'classes': ('collapse', 'collapse-closed'),
}),
)
def save_model(self, request, obj, form, change):
if not change and not obj.has_usable_password():
# Django's PasswordResetForm won't let us reset an unusable
# password. We set it above super() so we don't have to save twice.
obj.set_password(get_random_string())
reset_password = True
else:
reset_password = True
super(UserAdmin, self).save_model(request, obj, form, change)
if reset_password:
reset_form = PasswordResetForm({'email': obj.email})
assert reset_form.is_valid()
reset_form.save(
subject_template_name='registration/account_creation_subject.txt',
email_template_name='registration/account_creation_email.html',
)
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
The best way to do this is to create a custom middleware and check, if the logged in user has filled out the profile. If not, the middleware would redirect the user to a view, where he can fill out the profile.
Example:
class ProfileMiddleware(MiddlewareMixin):
def __init__(self, get_response=None):
self.get_response = get_response
def process_view(self, request, view_func, view_args, view_kwargs):
if not request.user.profile:
return HttpResponseRedirect(reverse('create-profile'))
return None
Haven't tested the code, but it should get you started. It's the cleanest way of doing this and it will also force the user to fill out the profile, otherwise he cannot open a page on the website.
Hope this helps.

Adding more validation to django admin user add forms

If we need to add more validation in django admin user add form like making the first name and last name and email compulsory.. Whats the way of achieving this??
You must create your own user form and adding your required fields:
class UserForm(forms.ModelForm):
class Meta:
model = User
def __init__(self, *args, **kwargs):
super(UserForm, self).__init__(*args, **kwargs)
self.fields['email'].required = True
self.fields['first_name'].required = True
self.fields['last_name'].required = True
Then override the form in your ModelAdmin:
class UserAdmin(admin.ModelAdmin):
form = UserForm
And then unregister the normal admin User before registering your own:
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
This extends Geoffroy's answer:
from django.contrib.auth.forms import UserCreationForm
class UserForm(UserCreationForm):
class Meta:
model = User
def __init__(self, *args, **kwargs):
super(UserForm, self).__init__(*args, **kwargs)
self.fields['email'].required = True
self.fields['first_name'].required = True
self.fields['last_name'].required = True
ModelAdmin:
from django.contrib.auth.admin import UserAdmin
class CustomUserAdmin(UserAdmin):
add_form = UserForm
add_fieldsets = (
(None, {'fields':('username','password1','password2','first_name','last_name','email'),}),)
And then unregister as above:
admin.site.unregister(User)
admin.site.register(User, CustomUserAdmin)
Just write your own form class and tell the ModelAdmin, to use that as the form.