0

My Profile model has a OneToOne relation with Django's built-in User model.

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    verified = models.BooleanField(default=False)

If I want to change user's password or properties like Active or Superuser I have to do it in one Change User page, and to edit verified property I have to go to another.

Is there any way to merge this:

enter image description here

And this:

enter image description here

Into one form so I can edit everything about a user in one page?

Edit 1:

As you guys suggested the StackedInline approach, let's see how it turns out.

Please first look at Django's default Admin site (first screenshot above):

  1. Everything is grouped in sections and sections have titles.
  2. Look at how the password information is displayed.
  3. There's a link to change the password.

Now I implement the StackedInline solution.

Please note that this is in the admin.py of my myapp:

from django.contrib import admin
from .models import Profile
from django.contrib.auth.models import User

# Register your models here.


class ProfileInline(admin.StackedInline):
    model = Profile

class UserAdmin(admin.ModelAdmin):
    inlines = (ProfileInline, )


admin.site.unregister(User)
admin.site.register(User, UserAdmin)

Now let's look at Admin site:

enter image description here

  1. Everything is scattered. Sections and their titles are gone (Personal info, Permissions, etc).
  2. Password field shows the hashed password. All other information is gone.
  3. There's no link to change the password.

Edit 2:

To solve the problem of Edit 1 I look at the source code of Django (https://github.com/django/django/blob/main/django/contrib/auth/admin.py) and add update my code as below:

class UserAdmin(admin.ModelAdmin):
    inlines = (ProfileInline, )
    fieldsets = (
        (None, {"fields": ("username", "password")}),
        (("Personal info"), {"fields": ("first_name", "last_name", "email")}),
        (
            ("Permissions"),
            {
                "fields": (
                    "is_active",
                    "is_staff",
                    "is_superuser",
                    "groups",
                    "user_permissions",
                ),
            },
        ),
        (("Important dates"), {"fields": ("last_login", "date_joined")}),
    )
    add_fieldsets = (
        (
            None,
            {
                "classes": ("wide",),
                "fields": ("username", "password1", "password2"),
            },
        ),
    )

    filter_horizontal = (
        "groups",
        "user_permissions",
    )
    
admin.site.unregister(User)
admin.site.register(User, UserAdmin)

Now I have two sections in the Admin site:

The section on the top shows almost everything (except that the password field is still different and there's no link to change the password and also the verified field is not there) but sections and titles are back.

Then there's this additional and completely unnecessary part:

enter image description here enter image description here

As you can see:

  1. All fields of information about the user is repeated
  2. Look at the password field
  3. Information is not grouped in sections with titles
  4. verified filed appears.
Gonçalo Peres
  • 11,752
  • 3
  • 54
  • 83
Omid Shojaee
  • 333
  • 1
  • 4
  • 20

3 Answers3

1

OP can use one of the InlineModelAdmin objects such as StackedInline. This allows one to create inline forms providing one the ability to edit models on the same page as a parent model.

Adapting for OP's case, it would be something like

from django.contrib import admin

class ProfileInline(admin.StackedInline):
    model = Profile

class UserAdmin(admin.ModelAdmin):
    inlines = [
        ProfileInline,
    ]

admin.site.register(User, UserAdmin)

Now OP's admin site is set up to edit Profile objects inline from the User detail page.

Gonçalo Peres
  • 11,752
  • 3
  • 54
  • 83
  • @OmidShojaee What I answered, based on the question and the initial requirements, was the way to solve the problem. The new Edit is raising different requirements and by itself can be understood as follow up questions and that is not a good practice. [See here why](https://meta.stackoverflow.com/a/266768/5675325) (those are called [chameleon questions](https://meta.stackoverflow.com/q/332820/7109869)). You're welcome to ask a new question. Most likely you'll get the needed help here. – Gonçalo Peres Sep 25 '22 at 13:12
  • The initial requirement still stands: add one field to the "Create and Edit" pages of a user. I never asked for a solution that messes up those pages completely. After applying your solution the whole thing is unusable because I'm not able to update user's password anymore. Maybe I should've mentioned that I don't want to lose Admin site's functionality. I just thought that's obvious. – Omid Shojaee Sep 25 '22 at 14:30
  • @OmidShojaee First of all the question has two images and asks how can the field be added. No effort shown from your side at all. You got the answer in how to add the field. And it worked since that's the standard way to solve it. However, now you also want to ensure a particular customization. That's the natural development process. One problem might lead to another. You're welcome to use any combination of features you deem fit for that, such as `fields`, `list_display`, `fieldsets`, `ordering`, ... or even other strategies, or ask a different question if you don't know how. – Gonçalo Peres Sep 25 '22 at 15:12
  • Actually I tried ```fieldsets``` before and faced the same issue with password field So I appreciate if you look at that too: https://stackoverflow.com/questions/73816296/password-field-is-visible-and-not-encrypted-in-django-admin-site – Omid Shojaee Sep 25 '22 at 17:25
0

User model Extension vs Inheritance.

Your profile model only add some elements to user. In this case Model inheritance can be better.

# models.py
class Profile(user):
    verified = models.BooleanField(default=False)

after that you can achieve all fields for user and for profile:

# admin.py
class ProfileAdmin(ModelAdmin):
    fields = '__all__'

if you don't want to switch on Model inheritance.

You can use InlineModel in UserAdmin to change related model.

# admin.py
class ProfileInline(StackedInline):
    model=Profile

class UserAdmin(ModelAdmin):
    inlines = (ProfileInline, )
Maxim Danilov
  • 2,472
  • 1
  • 3
  • 8
-1

you can use override AbstractUser from model Django so you can merge user in one place like this:


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

class CustomUser(AbstractUser):
   verified = models.BooleanField(default=False)

and you need to add this line to Django settings.py file so that Django knows to use the new User class:


AUTH_USER_MODEL = 'users.CustomUser'

then make sure to migrate :


(env)$ python manage.py makemigrations
(env)$ python manage.py migrate
Ahmad Akel Omar
  • 317
  • 2
  • 11