Does model inheritance *require* field to be named {model_name}_ptr?

In my project I was handling User profiles by creating a OneToOneField linking to Django’s User table, like so:

user = models.OneToOneField(settings.AUTH_USER_MODEL, verbose_name=_('User'), related_name='settings', auto_created=True, on_delete=models.CASCADE)

Now I’m switching to (my) library that handles this a bit differently: it uses model inheritance for user profiles, declares some fields of its own in an abstract class. I’m now basing my model upon the library’s. Me and my team decided this is the best way to implement user profiles and it simply differs from one of my oldest implementations in this particular project. The intent is, of course, to utilise the library’s APIs for handling profiles and gain from common development benefits.

Anyway, upon basing my Profile model on the new inheritance-based model, like so:

class UserSettings(BaseProfile):
    user = models.OneToOneField(settings.AUTH_USER_MODEL, verbose_name=_('User'), related_name='settings',
                                auto_created=True, on_delete=models.CASCADE, parent_link=True, primary_key=True)

when running makemigrations, django now complains that:

<class 'nalet.admin.UserSettingsInline'>: (admin.E202) 'nalet.UserSettings' has more than one ForeignKey to 'auth.User'.

I have investigated the issue and Django seems to be forcing this OneToOneField to have to be named user_ptr. Simply declaring a dummy user_ptr field eliminates issues with makemigrations command.

The documentation says nothing about the field having to be named exactly like that. From the docs I surmised that adding the parent_link=True should be enough, but it seems I was only reading what suited me.

OTOH, I was surprised that django didn’t complain that my dummy user_ptr = models.IntegerField(null=True) did not result in a name clash as there’s a check in django code if the field name had been used already.


  1. Is the documentation imprecise or does the code have a bug not using my customised link field, but accepting it when I override its own idea of the field name?
  2. Will my dummy field workaround even work or will it just mess everything up?

We would need to see your BaseProfile class to do anything other than guess. (And may end up needing to see more.)

One thing that looks “odd” to me is that you’ve got parent_link=True for a class defined as inheriting from BaseProfile, but the relationship class is referencing AUTH_USER_MODEL, not BaseProfile. If BaseProfile is defined as a concrete class, that could be an issue.

If BaseProfile already defines a OneToOne field to your User model, then that could also be a cause for the error you’re seeing.

@KenWhitesell : the BaseProfile model is an abstract one. It’s just plain old class BaseModel(User) with some boilerplate fields defined. I leave the user_ptr field creation to the deriving class here.

In addition, I also found that Django just wants to drop my pk field, but it does not attempt to re-link to the “new” primary key that user_ptr is. Obviously I’m doing something completely unsupported here.

I think I’m going to resort to a tri-phase migration swapping all the relations. Fortunatelly, it’s not a million-record database :slight_smile: