Django Custom User --- Edit new CustomUser fields in admin
Asked Answered
A

3

12

I am trying to expand on the tutorial by William Vincent, posted below:

https://wsvincent.com/django-custom-user-model-tutorial/

I am trying to add new fields to the CustomerUser model I extended via the AbstractUser import from django.contrib.auth.models:

users/models.py:

from django.db import models
from django.contrib.auth.models import AbstractUser, UserManager

class CustomUserManager(UserManager):
    pass

class CustomUser(AbstractUser):
    bio         = models.TextField(max_length=500, blank=True)

    objects = CustomUserManager()

    def __str__(self):
        return self.username

I added the 'bio' field to the model above, but when I access a user via the django admin portal, I don't see the new 'bio' field in there with the default admin fields packaged with django: ie: Personal info: first name, last name, email address, etc.

My CustomUser app is registered to the admin portal like so (following the tutorial mentioned above):

As a test for myself, I was able to display the bio field successfully (showing blank as expected) in my list_display. To reiterate, my problem is I have no way of updating this field when I click to edit a user. The good news is that django picked up the migration of my new 'bio' field.

from django.contrib import admin
from django.contrib.auth import get_user_model
from django.contrib.auth.admin import UserAdmin

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

class CustomUserAdmin(UserAdmin):
    add_form = CustomUserCreationForm
    form = CustomUserChangeForm
    model = CustomUser
    list_display = ['username', 'email','is_staff', 'bio']

admin.site.register(CustomUser, CustomUserAdmin)

My guess is that the solution I am looking for has something to do with editing the admin forms. Here is what I have in my user app (from the tutorial).

users/forms.py:

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

class CustomUserCreationForm(UserCreationForm):

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

class CustomUserChangeForm(UserChangeForm):

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

Admittedly, I don't have a great understanding of what is happening in the form.py file just above. I suspect that it might have nothing to do with the actual user edit form that I am accessing via the admin portal, so I might just need to figure out how to make changes to the default django admin app.

As always, your help is much appreciated, thanks!

Ambidextrous answered 17/11, 2018 at 21:13 Comment(0)
U
13

The 'fieldsets +' approach is much better than having to write out all the default fields again.

fieldsets = UserAdmin.fieldsets + (
    (None, {'fields': ('some_extra_data',)}),
)
Unreserve answered 21/11, 2019 at 17:58 Comment(0)
H
13

Andy try adding this to your admin class:

fieldsets = (
        (('User'), {'fields': ('username', 'email','is_staff', 'bio')}),
    )

You can also add other sets for example another section that is all about permissions, and can display information about is_active, or groups. You can do this:

fieldsets = (
        (('User'), {'fields': ('username', 'email','is_staff', 'bio')}),
        (('Permissions'), {'fields': ('is_active','is_staff')}),
    )

You can just insert fieldsets underneath list_display. There is also a readonly_fields for fields that you do not want to be editable in the admin.

Helgeson answered 17/11, 2018 at 23:32 Comment(5)
Thank you very much! That is exactly what I was looking for.Ambidextrous
@Whodini @AndyG Thanks, but adding this fieldsets object apparently overwrites the default fields, which are structured by e.g. Personal info, Permissions, Important dates... How could one simply add a field to be changed on this page without deleting the existing structure and fields?Undersell
@Undersell - You can still have your structure as personal info, permissions, important dates using fieldsets. Using the example above you would simply replace ('User') in the first line with 'Personal info', followed by the 'fields': you want for that section, and so on so forth for each section. I prefer to go ahead and override it because it makes it easier to add or remove fields as needed, and I like the customizability of it. There is also the fields setting, but I can't remember how that works for the UserAdmin (fieldsets by default), as I've used fieldsets for quite a while now.Helgeson
To keep the UserAdmin structure: fieldsets = UserAdmin.fieldsets + ( (None, {'fields': ('bio',)}), )Assault
the is_stuff should not be in User fields, it only needs to be in Permissions, at last, it's better to use @Assault approach.Sinking
U
13

The 'fieldsets +' approach is much better than having to write out all the default fields again.

fieldsets = UserAdmin.fieldsets + (
    (None, {'fields': ('some_extra_data',)}),
)
Unreserve answered 21/11, 2019 at 17:58 Comment(0)
T
2

To add a "bio" field to the "Personal info" section instead of just to the end of the User admin, and to do so without specifying every existing field, you can construct a new fieldsets attribute from the parent class while only adding the new "bio" field to the "Personal info" section and preserving everything else.

If you simply append to the parent's fieldsets attribute, then the field will appear at the bottom of the page and not at the end of the "Personal info" section.

This code makes a copy of the parent's fieldsets attribute with everything preserved as-is except that the fields tuple in the "Personal info" section has "bio" appended.

in users/admin.py:

class CustomUserAdmin(UserAdmin):
    # Add a "bio" field to the User admin page.
    fieldsets = tuple(
        # For the "Personal info" fieldset, drill down to the fields,
        # preserving everything else.
        (fieldset[0], {
            # Preserve any entries in the dict other than "fields".
            **{key: value for (key, value) in fieldset[1].items() if key != 'fields'},
            # Add the "bio" field to the existing fields
            'fields': fieldset[1]['fields'] + ('bio',)
        })
        # Preserve any fieldsets other than "Personal info".
        if fieldset[0] == 'Personal info'
        else fieldset
        for fieldset in UserAdmin.fieldsets
    )

Note: The {**some_dict, new_key: new_value} syntax requires Python 3.5 or greater.

Tetralogy answered 29/4, 2021 at 1:1 Comment(1)
everyone else's answers put the field at the bottom of the page, but this answer was the only one that included the field in the Personal Info sectionYpsilanti

© 2022 - 2024 — McMap. All rights reserved.