Adding OneToOne extended User as Admin Inline

Hi all.

I’ve created a few more fields for user using OneToOne approach.

from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver

class Student(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    birth_date = models.DateField(null=True, blank=True)
    other_fields=etc...

@receiver(post_save, sender=User)
def create_student(sender, instance, created, **kwargs):
    if created:
        Student.objects.create(user=instance)

@receiver(post_save, sender=User)
def save_student(sender, instance, **kwargs):
    instance.student.save()

First issue: The fields don’s seem to be present on the object:

# python manage.py shell
>>> from django.contrib.auth.models import User
>>> users = User.objects.all().select_related('student')
>>> u = users[0]
>>> u.__dir__()
>>> ['_state', 'id', 'password', 'student', 'no birth_date etc']

Next issue: Trying to add to admin view:

class StudentInline(admin.StackedInline):
    model = Student
    extra = 1
class StudentAdmin(admin.ModelAdmin):
    inlines = [StudentInline]
admin.site.register(Student)

No errors but there’s nothing on the admin screen.

This approach:

class StudentInline(admin.TabularInline):
    model = Student

@admin.register(Student)
class StudentAdmin(admin.ModelAdmin):
    inlines = [StudentInline]

Returns the following error:

<class 'members.admin.StudentInline'>: (admin.E202) 'members.Student' has no ForeignKey to 'members.Student'

Most of the online examples are seeing are for extending the AbstractUser which I’m hoping is more complex than I need.

Derp. Spent a little more time with the fine manual and solved it:

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.models import User
from members.models import Student

# Define an inline admin descriptor for Student model
# which acts a bit like a singleton
class StudentInline(admin.TabularInline): # or StackedInline
    model = Student
    can_delete = False


class UserAdmin(BaseUserAdmin):
    inlines = [StudentInline]

# Re-register UserAdmin
admin.site.unregister(User)
admin.site.register(User, UserAdmin)

Maybe you are confusing explicit OneToOne relationship with model class inheritance?

If you had class Student(User), then you would see the user fields on the student class and instances. Under the hood, django would have a one-to-one from student to user (ref: Models | Django documentation | Django)

Model inheritance can be useful, but explicit relationships are (IMHO) cleaner.

1 Like