tkhyn / django-gm2m

MIT License
35 stars 23 forks source link

makemigrations GM2MField exception when pointing to AUTH_USER_MODEL #45

Open tkhyn opened 5 years ago

tkhyn commented 5 years ago

Original report by Aleksander Kowalczyk (Bitbucket: alex_kowalczyk, ).


Cannot make GM2MField relation to AUTH_USER_MODEL. The following exception happens:

manage.py makemigrations

Traceback (most recent call last):
  File "manage.py", line 21, in <module>
    execute_from_command_line(sys.argv)
  File "lib/python3.7/site-packages/django/core/management/__init__.py", line 381, in execute_from_command_line
    utility.execute()
  File "lib/python3.7/site-packages/django/core/management/__init__.py", line 375, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "lib/python3.7/site-packages/django/core/management/base.py", line 316, in run_from_argv
    self.execute(*args, **cmd_options)
  File "lib/python3.7/site-packages/django/core/management/base.py", line 353, in execute
    output = self.handle(*args, **options)
  File "lib/python3.7/site-packages/django/core/management/base.py", line 83, in wrapped
    res = handle_func(*args, **kwargs)
  File "lib/python3.7/site-packages/django/core/management/commands/makemigrations.py", line 144, in handle
    ProjectState.from_apps(apps),
  File "lib/python3.7/site-packages/django/db/migrations/state.py", line 222, in from_apps
    model_state = ModelState.from_model(model)
  File "lib/python3.7/site-packages/django/db/migrations/state.py", line 422, in from_model
    fields.append((name, field.clone()))
  File "lib/python3.7/site-packages/django/db/models/fields/__init__.py", line 493, in clone
    name, path, args, kwargs = self.deconstruct()
  File "lib/python3.7/site-packages/gm2m/fields.py", line 100, in deconstruct
    % (setting_name, swappable_setting))
ValueError: Cannot deconstruct a GM2MField pointing to a model that is swapped in place of more than one model (None and AUTH_USER_MODEL)

The relation:

class SubscribedUser(Model):
    _TEXT_PROP = '{self.container_number} active={self.active} owner={self.owner} session={self.owner_session}'
    subscription = models.ForeignKey('Subscription', on_delete=models.CASCADE, null=False, blank=False, verbose_name=_("Subscription"))

    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    subscriber_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'subscriber_id')

class User(Model, AbstractUser):
    _TEXT_PROP = 'email'

    email = models.EmailField(_("Email Address"), unique=True)
    username = models.CharField(_("Username"), max_length=200, blank=True)
    firstname = models.CharField(_("First Name"), max_length=100, blank=True)
    lastname = models.CharField(_("Last Name"), max_length=100, blank=True)

class Subscription(Model):
    subscribers = GM2MField('User', related_name='+',
                            through=SubscribedUser, through_fields=('subscription', 'content_object', 'content_type', 'subscriber_id'))

For some reason the code expects the User model to provide value in setting_name attribute.

Adding setting_name='AUTH_USER_MODEL' attribute (that’s undocumented) to User class to satisfy the deconstruct function just changes the exception:

manage.py makemigrations

Traceback (most recent call last):
  File "manage.py", line 21, in <module>
    execute_from_command_line(sys.argv)
  File "lib/python3.7/site-packages/django/core/management/__init__.py", line 381, in execute_from_command_line
    utility.execute()
  File "lib/python3.7/site-packages/django/core/management/__init__.py", line 375, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "lib/python3.7/site-packages/django/core/management/base.py", line 316, in run_from_argv
    self.execute(*args, **cmd_options)
  File "lib/python3.7/site-packages/django/core/management/base.py", line 353, in execute
    output = self.handle(*args, **options)
  File "lib/python3.7/site-packages/django/core/management/base.py", line 83, in wrapped
    res = handle_func(*args, **kwargs)
  File "lib/python3.7/site-packages/django/core/management/commands/makemigrations.py", line 170, in handle
    migration_name=self.migration_name,
  File "lib/python3.7/site-packages/django/db/migrations/autodetector.py", line 44, in changes
    changes = self._detect_changes(convert_apps, graph)
  File "lib/python3.7/site-packages/django/db/migrations/autodetector.py", line 129, in _detect_changes
    self.new_apps = self.to_state.apps
  File "lib/python3.7/site-packages/django/utils/functional.py", line 37, in __get__
    res = instance.__dict__[self.name] = self.func(instance)
  File "lib/python3.7/site-packages/django/db/migrations/state.py", line 210, in apps
    return StateApps(self.real_apps, self.models)
  File "lib/python3.7/site-packages/django/db/migrations/state.py", line 271, in __init__
    self.render_multiple(list(models.values()) + self.real_models)
  File "lib/python3.7/site-packages/django/db/migrations/state.py", line 306, in render_multiple
    model.render(self)
  File "lib/python3.7/site-packages/django/db/migrations/state.py", line 574, in render
    return type(self.name, bases, body)
  File "lib/python3.7/site-packages/django/db/models/base.py", line 139, in __new__
    new_class.add_to_class(obj_name, obj)
  File "lib/python3.7/site-packages/django/db/models/base.py", line 305, in add_to_class
    value.contribute_to_class(cls, name)
  File "lib/python3.7/site-packages/gm2m/fields.py", line 213, in contribute_to_class
    self.remote_field.contribute_to_class(cls)
  File "lib/python3.7/site-packages/gm2m/relations.py", line 820, in contribute_to_class
    rel.contribute_to_class()
  File "lib/python3.7/site-packages/gm2m/relations.py", line 308, in contribute_to_class
    self.model, rel=self)
  File "lib/python3.7/site-packages/django/db/models/fields/related.py", line 79, in lazy_related_operation
    return apps.lazy_model_operation(partial(function, **kwargs), *model_keys)
  File "lib/python3.7/site-packages/django/db/models/fields/related.py", line 77, in <genexpr>
    model_keys = (make_model_tuple(m) for m in models)
  File "lib/python3.7/site-packages/django/db/models/utils.py", line 20, in make_model_tuple
    "must be of the form 'app_label.ModelName'." % model
ValueError: Invalid model reference '<class 'app.models.users.User'>'. String model references must be of the form 'app_label.ModelName'.
tkhyn commented 5 years ago

Original comment by Thomas Khyn (Bitbucket: tkhyn, GitHub: tkhyn).


Hi, thanks for the detailed report.

I am deeply sorry but unfortunately I am no longer using django-gm2m nor django and have little to no time to dive again into it, so it is highly unlikely I will be able to help you with that.

I’ll gladly accept any PR that solves the issue, though.

eriktelepovsky commented 10 months ago

Instad of:

subscribers = GM2MField('User', related_name='+',
                            through=SubscribedUser, through_fields=('subscription', 'content_object', 'content_type', 'subscriber_id'))

try:

subscribers = GM2MField(settings.AUTH_USER_MODEL, related_name='+',
                            through=SubscribedUser, through_fields=('subscription', 'content_object', 'content_type', 'subscriber_id'))