[Fixed]-Django Multi-Table Inheritance VS Specifying Explicit OneToOne Relationship in Models

17👍

I’d like to expand on the solution by @thornomad.

Extending Django’s User class directly can cause all kinds of trouble with the internal django.auth mechanisms. What I’ve done in a similar situation is precisely what @thornomad suggests – I made my own UserProfile model linked one-to-one with the Django User model, in which I held additional user data and from which I inherited models for different types of users.

Something to fit what you described:

class UserProfile(models.Model):
    user = models.OneToOneField(User, blank=True, related_name='profile')
    class Meta:
        abstract = True


class PositionHolderUserProfile(UserProfile):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)
    landline = models.CharField(blank=True, max_length=20)    
    mobile = models.CharField(blank=True, max_length=20)
    created_by = models.ForeignKey(PositionHolderUserProfile, editable=False, blank=True, related_name="created_users")    
    modified_by = models.ForeignKey(PositionHolderUserProfile, editable=False, blank=True, related_name="modified_users")

class Principal(PositionHolderUserProfile):
    branchoffice = models.ForeignKey(BranchOffice)

class Administrator(PositionHolderUserProfile):
    superior = models.ForeignKey(Principal, related_name="subordinates")
    province = models.ForeignKey(Province)

class Coordinator(PositionHolderUserProfile):
    superior = models.ForeignKey(Administrator, related_name="subordinates")


class Company(UserProfile):
    name = models.CharField(max_length=50)

class Product(models.Model):
    name = models.CharField(max_length=50)
    produced_by = models.ForeignKey(Company)

class Buyer(UserProfile):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)
    products_bought = models.ManyToManyField(Product)

3👍

I recently switched over to using models that inherit off of contrib.auto.models.User. My general observation is that in theory they’re great, but sometimes they don’t get auto-magically handled like they’re supposed to.

I think your decision regarding inheritance vs. OneToOne comes down to this:

  • Do I want to have Django automatically do something right 95% of the time, and need to debug that other 5%

-OR-

  • Do I want to do something manually myself 100% of the time

If you haven’t seen it, the Scott Barham blog has a great post about inheriting off of User, and also building a custom back end to make sure that your custom object is being returned — Extending the Django User.

Additionally of interest would be the AutoOneToOne field provided by django-annoying. It’s sort of a hybrid of the two approaches here — there’s no inheritance taking place, but Django is taking care of creating the matching OneToOneField if it’s not present.

Also, thornomad does make a good point about the redundancy in your models. You could easily implement an abstract class to clean that up as so (assuming you’re doing manual OneToOne):

class BaseExtendedUser(models.Model):
    user = models.OneToOneField(User, blank=True, related_name='profile')
    landline = models.CharField(blank=True, max_length=20)    
    mobile = models.CharField(blank=True, max_length=20)
    created_by = models.ForeignKey(User, editable=False, blank=True, related_name="created_users")    
    modified_by = models.ForeignKey(User, editable=False, blank=True, related_name="modified_users")

    class Meta:
        abstract = True

class Administrator(BaseExtendedUser):
    province = models.ForeignKey(Province)

class Principal(BaseExtendedUser):
    branchoffice = models.ForeignKey(BranchOffice)

2👍

I don’t think I would inherit the User model, rather use a custom UserProfile – leaving the contrib.auth model alone. With the custom UserProfile model, you could setup a base user profile model that can be a part of all your different user types.

Just looking at it quickly, too, I would look carefully at any models that repeat all the same fields (like your last two Principle and Administrator models). Combining the built in group functionality with the user profile idea may do what you are looking for.

0👍

Please consider what happens in the data model when a Coordinator gets promoted to a Principal. I would not use inheritance in this case at all. Please reconsider the previous poster’s suggestion “Combining the built in group functionality with the user profile idea may do what you are looking for.”

0👍

Do you need objects of your user classes to act like an auth.User anywhere? That would be the most obvious reason to use inheritance over OneToOne. One pro of the OneToOne approach would be the ease in which you can change over to another User model, if that’s a concern.

The real issue I see with what you have above (by either method) is that there doesn’t appear to be anything stopping you from having a Principal object and an Administrator object share the same User. OneToOneField can only guarantee a one-to-one mapping between any two relations.

Leave a comment