django-polymorphic
django-polymorphic copied to clipboard
Polymorphic custom user model with the django-helpdesk app, migration error
I have custom user db model Profile class (subclass PolymorphicModel, AbstractUser) with custom manager class (subclass PolymorphicManager, UserManager) and model UserProfile, CompanyProfile class (subclass Profile). I have installed django-helpdesk app. During apply initial migration, I get error message:
Running migrations:
Applying helpdesk.0002_populate_usersettings...
Traceback (most recent call last):
File "manage.py", line 26, in <module>
execute_from_command_line(sys.argv)
File "/home/test/.local/share/virtualenvs/klub-v-nnivlwsY/lib/python3.6/site-packages/django/core/management/__init__.py", line 381, in execute_from_command_line
utility.execute()
File "/home/test/.local/share/virtualenvs/klub-v-nnivlwsY/lib/python3.6/site-packages/django/core/management/__init__.py", line 375, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/home/test/.local/share/virtualenvs/klub-v-nnivlwsY/lib/python3.6/site-packages/django/core/management/base.py", line 323, in run_from_argv
self.execute(*args, **cmd_options)
File "/home/test/.local/share/virtualenvs/klub-v-nnivlwsY/lib/python3.6/site-packages/django/core/management/base.py", line 364, in execute
output = self.handle(*args, **options)
File "/home/test/.local/share/virtualenvs/klub-v-nnivlwsY/lib/python3.6/site-packages/django/core/management/base.py", line 83, in wrapped
res = handle_func(*args, **kwargs)
File "/home/test/.local/share/virtualenvs/klub-v-nnivlwsY/lib/python3.6/site-packages/django/core/management/commands/migrate.py", line 234, in handle
fake_initial=fake_initial,
File "/home/test/.local/share/virtualenvs/klub-v-nnivlwsY/lib/python3.6/site-packages/django/db/migrations/executor.py", line 117, in migrate
state = self._migrate_all_forwards(state, plan, full_plan, fake=fake, fake_initial=fake_initial)
File "/home/test/.local/share/virtualenvs/klub-v-nnivlwsY/lib/python3.6/site-packages/django/db/migrations/executor.py", line 147, in _migrate_all_forwards
state = self.apply_migration(state, migration, fake=fake, fake_initial=fake_initial)
File "/home/test/.local/share/virtualenvs/klub-v-nnivlwsY/lib/python3.6/site-packages/django/db/migrations/executor.py", line 245, in apply_migration
state = migration.apply(state, schema_editor)
File "/home/test/.local/share/virtualenvs/klub-v-nnivlwsY/lib/python3.6/site-packages/django/db/migrations/migration.py", line 124, in apply
operation.database_forwards(self.app_label, schema_editor, old_state, project_state)
File "/home/test/.local/share/virtualenvs/klub-v-nnivlwsY/lib/python3.6/site-packages/django/db/migrations/operations/special.py", line 190, in database_forwards
self.code(from_state.apps, schema_editor)
File "/home/test/.local/share/virtualenvs/klub-v-nnivlwsY/src/django-helpdesk/helpdesk/migrations/0002_populate_usersettings.py", line 32, in populate_usersettings
for u in User.objects.all():
File "/home/test/.local/share/virtualenvs/klub-v-nnivlwsY/lib/python3.6/site-packages/django/db/models/query.py", line 274, in __iter__
self._fetch_all()
File "/home/test/.local/share/virtualenvs/klub-v-nnivlwsY/lib/python3.6/site-packages/django/db/models/query.py", line 1242, in _fetch_all
self._result_cache = list(self._iterable_class(self))
File "/home/test/.local/share/virtualenvs/klub-v-nnivlwsY/lib/python3.6/site-packages/polymorphic/query.py", line 66, in _polymorphic_iterator
real_results = self.queryset._get_real_instances(base_result_objects)
File "/home/test/.local/share/virtualenvs/klub-v-nnivlwsY/lib/python3.6/site-packages/polymorphic/query.py", line 360, in _get_real_instances
pk_name = self.model.polymorphic_primary_key_name
AttributeError: type object 'Profile' has no attribute 'polymorphic_primary_key_name'
Helpdesk app 0002_populate_usersettings migration file:
# https://docs.djangoproject.com/en/1.7/topics/migrations/#data-migrations
def populate_usersettings(apps, schema_editor):
"""Create a UserSettings entry for each existing user.
This will only happen once (at install time, or at upgrade)
when the UserSettings model doesn't already exist."""
_User = get_user_model()
User = apps.get_model(_User._meta.app_label, _User._meta.model_name)
# Import historical version of models
UserSettings = apps.get_model("helpdesk", "UserSettings")
settings_pickled = picke_settings(DEFAULT_USER_SETTINGS)
for u in User.objects.all():
try:
UserSettings.objects.get(user=u)
except UserSettings.DoesNotExist:
UserSettings.objects.create(user=u, settings_pickled=settings_pickled)
Call this code line User.objects.all() raise AttributeError: type object 'Profile' has no attribute 'polymorphic_primary_key_name'.
I know this is quite old but I just tried to do a similar thing with a polymorphic Profile model and a custom user model that inherits from Profile (i.e. the same as described above). After getting the same error and seeing no solution to this issue, I decided to do a little digging.
Turns out, Django doesn't strictly use the models from your models.py files during migrations. I discovered this by placing a print(type(User)) statement in the offending migration file which returned <class '__fake__.User'>. Therefore the fake model did not use the polymorphic metaclass and therefore did not have the required attribute above.
Thankfully, there is a quick and easy fix for this issue: in you custom manager on the User model, make sure the attribute use_in_migrations is set to False. That way, the migration file will not use the custom polymorphic manager during the migration which is causing this error.
E.g.
from django.contrib.auth.models import AbstractUser, BaseUserManager
from django.db import models
from polymorphic.managers import PolymorphicManager
from polymorphic.models import PolymorphicModel
class UserManager(BaseUserManager, PolymorphicManager):
use_in_migrations = False
class CustomUser(AbstractUser, Profile):
objects = UserManager() # type: ignore[var-annotated]
If you rely on the polymorphic manager in your own migration files for some reason and therefore can't set use_in_migrations to False, you will need to contact the author of the offending migration file and ask them to properly import the user model via django's get_user_model function which I believe should also solve the issue.
Hope this helps someone.