'Create an account from django-admin

In my CRM, the owner of the company gives the first password and email for new employees. Employee can change them later (I don't need to show them in admin, which is not allowed by django by default). But how to show the fields for giving the first password in admin? For examplle fields - "Password for new user" and "Again password for new user"

models.py

class CustomUser(AbstractUser):
    [...]


    def __str__(self):
        return self.email

admin.py

from django.contrib import admin
from .models import CustomUser


admin.site.register(CustomUser)

I need this effect:

enter image description here



Solution 1:[1]

When dealing when custom user models it's important to ensure that the forms used account for this password validation. You have two options to achieve that:

  • As your CustomUser class inherits from AbstractUser, you can make use of the forms that Django provides us to handle our user instances:
# forms.py

from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from .models import CustomUser


class CustomUserCreationForm(UserCreationForm):

    class Meta(UserCreationForm.Meta):
        model = CustomUser
        fields = UserCreationForm.Meta.fields + ('email',)


class CustomUserChangeForm(UserChangeForm):

    class Meta(UserChangeForm.Meta):
        model = CustomUser
        fields = UserChangeForm.Meta.fields # Includes all fields by default, no need to specify email

By looking at the docs we see that our CustomUserCreationForm will contain 4 fields (username, email, password1 and password2). CustomUserChangeForm will contain all fields in the CustomUser model but it will mask the password field so it won't be editable from the form itself. You can take a look at the source code for UserCreationForm and UserChangeForm to see how Django handles this forms. If the fields used don't suit your needs, I would go with the second approach that I will explain later where you will be able to customize your forms further.

Now we have to specify that we want to use those forms while editing our CustomUser instances in the admin:

# admin.py

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin

from .models import CustomUser
from .forms import CustomUserCreationForm, CustomUserChangeForm

@admin.register(CustomUser)
class CustomUserAdmin(BaseUserAdmin):
    # The forms to add and change user instances
    form = CustomUserChangeForm
    add_form = CustomUserCreationForm
    
    fieldsets = BaseUserAdmin.fieldsets

    # add_fieldsets is not a standard ModelAdmin attribute. UserAdmin
    # overrides get_fieldsets to use this attribute when creating a user.
    add_fieldsets = BaseUserAdmin.add_fieldsets + (
        (None, {'fields': ('email',)}),
    )

If we want to customize it better, we can go with the second option.

  • Create our forms almost from scratch to handle CustomUser instances. The process is the same as before, but the way to specify the forms and register them to the admin is different:
# forms.py

from django import forms
from django.contrib.auth.forms import ReadOnlyPasswordHashField
from django.core.exceptions import ValidationError

from .models import CustomUser


class CustomUserCreationForm(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 = CustomUser
        fields = ('email', 'is_superuser') 

    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

class CustomUserChangeForm(forms.ModelForm):
    """A form for updating users. Include the fields on
    the user, but replaces the password field with admin's
    disabled password hash display field.
    """
    password = ReadOnlyPasswordHashField()

    class Meta:
        model = CustomUser
        fields = ('email', 'password', 'is_superuser')

Keep in mind that the fields attribute is used to control which fields in your model need to be included in the corresponding forms, I just added email and is_superuser as examples but you should use the ones you need. Usually creation forms display only the necessary fields to create instances and update forms contain all editable fields in your model (in this case I would add more fields to CustomUserChangeForm but as I don't have much info about CustomUser I just added some fields I know the model has).

With that into account, now we have to specify to that we want to use these forms in our admin panel, just the same way we did before but changing fieldsets and add_fieldsets:

# admin.py

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin

from .models import CustomUser
from .forms import CustomUserCreationForm, CustomUserChangeForm

@admin.register(CustomUser)
class CustomUserAdmin(BaseUserAdmin):
    # The forms to add and change user instances
    form = CustomUserChangeForm
    add_form = CustomUserCreationForm
    
    fieldsets = (
        (None, {'fields': ('email', 'password')}),
        ('Permissions', {'fields': ('is_superuser',)}),
    )

    # add_fieldsets is not a standard ModelAdmin attribute. UserAdmin
    # overrides get_fieldsets to use this attribute when creating a user.
    add_fieldsets = (
        (None, {
            'classes': ('wide',),
            'fields': ('email', 'is_superuser', 'password1', 'password2'),
        }),
    )

You can read about Customizing authentication in Django if you need more context on this

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 mtzd