django-scopes icon indicating copy to clipboard operation
django-scopes copied to clipboard

Error when scoping the user model

Open andrewtmendoza opened this issue 4 years ago • 4 comments

Hi @raphaelm

First off, thanks for creating this package!

I'm having an issue when trying to apply scopes to the user model. I have a multi-tenancy application, and I only want users to be able to see other users in their same tenancy.

Here's a snippet of my core/models.py file based on the example in the docs:

from django.db import models
from django.contrib.auth.models import AbstractUser, UserManager

from django_scopes import ScopedManager
from phonenumber_field.modelfields import PhoneNumberField

from tenants.models import Tenant
from web.models import Sponsor


class LineOfBusiness(models.Model):
    LINE_OF_BUSINESS_CHOICES = [
        ("COMM", "Commercial"),
        ("EX", "Exchange"),
        ("MA", "Medicaid"),
        ("MC", "Medicare"),
        ("WC", "Workers' Compensation"),
    ]
    line_of_business = models.CharField(choices=LINE_OF_BUSINESS_CHOICES, max_length=5)

    def __str__(self):
        return self.get_line_of_business_display()


class User(AbstractUser):
    USER_TYPE_CHOICES = [
        ("C", "Customer"),
        ("E", "Employee"),
    ]
    tenant = models.ForeignKey(Tenant, on_delete=models.CASCADE)
    phone_number = PhoneNumberField(blank=True)
    line_of_business = models.ManyToManyField(LineOfBusiness)
    sponsors = models.ManyToManyField(Sponsor)
    user_type = models.CharField(choices=USER_TYPE_CHOICES, max_length=5)

    # https://github.com/raphaelm/django-scopes#scoping-the-user-model
    objects = ScopedManager(tenant='tenant', _manager_class=UserManager)

Here's the error I get when I try to create the migrations:

root@80f003450656:/django_scope_test# python manage.py makemigrations core
Traceback (most recent call last):
  File "manage.py", line 25, in <module>
    main()
  File "manage.py", line 21, in main
    execute_from_command_line(sys.argv)
  File "/usr/local/lib/python3.8/site-packages/django/core/management/__init__.py", line 401, in execute_from_command_line
    utility.execute()
  File "/usr/local/lib/python3.8/site-packages/django/core/management/__init__.py", line 395, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/usr/local/lib/python3.8/site-packages/django/core/management/base.py", line 328, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/usr/local/lib/python3.8/site-packages/django/core/management/base.py", line 369, in execute
    output = self.handle(*args, **options)
  File "/usr/local/lib/python3.8/site-packages/django/core/management/base.py", line 83, in wrapped
    res = handle_func(*args, **kwargs)
  File "/usr/local/lib/python3.8/site-packages/django/core/management/commands/makemigrations.py", line 164, in handle
    changes = autodetector.changes(
  File "/usr/local/lib/python3.8/site-packages/django/db/migrations/autodetector.py", line 43, in changes
    changes = self._detect_changes(convert_apps, graph)
  File "/usr/local/lib/python3.8/site-packages/django/db/migrations/autodetector.py", line 129, in _detect_changes
    self.new_apps = self.to_state.apps
  File "/usr/local/lib/python3.8/site-packages/django/utils/functional.py", line 48, in __get__
    res = instance.__dict__[self.name] = self.func(instance)
  File "/usr/local/lib/python3.8/site-packages/django/db/migrations/state.py", line 209, in apps
    return StateApps(self.real_apps, self.models)
  File "/usr/local/lib/python3.8/site-packages/django/db/migrations/state.py", line 272, in __init__
    self.render_multiple([*models.values(), *self.real_models])
  File "/usr/local/lib/python3.8/site-packages/django/db/migrations/state.py", line 307, in render_multiple
    model.render(self)
  File "/usr/local/lib/python3.8/site-packages/django/db/migrations/state.py", line 576, in render
    body.update(self.construct_managers())
  File "/usr/local/lib/python3.8/site-packages/django/db/migrations/state.py", line 535, in construct_managers
    as_manager, manager_path, qs_path, args, kwargs = manager.deconstruct()
  File "/usr/local/lib/python3.8/site-packages/django/db/models/manager.py", line 61, in deconstruct
    raise ValueError(
ValueError: Could not find manager Manager in django_scopes.manager.
Please note that you need to inherit from managers you dynamically generated with 'from_queryset()'.

Any help you can provide would be greatly appreciated!

andrewtmendoza avatar Dec 14 '20 02:12 andrewtmendoza

After experimenting further, it seems like django.contrib.auth.models.BaseUserManager allows the migrations to run successfully.

andrewtmendoza avatar Dec 14 '20 03:12 andrewtmendoza

Interesting :thinking: Have you tested that this only happens for the user model and not for other models? I haven't tried all this with the user model before, but I don't see why it should fail.

raphaelm avatar Dec 14 '20 08:12 raphaelm

@raphaelm what finally worked for me was this:

from django.db import models
from django.contrib.auth.models import AbstractUser, UserManager

from django_scopes import ScopedManager
from phonenumber_field.modelfields import PhoneNumberField

from tenants.models import Tenant
from web.models import Sponsor


class LineOfBusiness(models.Model):
    LINE_OF_BUSINESS_CHOICES = [
        ("COMM", "Commercial"),
        ("EX", "Exchange"),
        ("MA", "Medicaid"),
        ("MC", "Medicare"),
        ("WC", "Workers' Compensation"),
    ]
    line_of_business = models.CharField(choices=LINE_OF_BUSINESS_CHOICES, max_length=5)

    def __str__(self):
        return self.get_line_of_business_display()


class MyUserManager(UserManager):
    use_in_migrations = False


class User(AbstractUser):
    USER_TYPE_CHOICES = [
        ("C", "Customer"),
        ("E", "Employee"),
    ]
    tenant = models.ForeignKey(Tenant, on_delete=models.CASCADE)
    phone_number = PhoneNumberField(blank=True)
    line_of_business = models.ManyToManyField(LineOfBusiness)
    sponsors = models.ManyToManyField(Sponsor)
    user_type = models.CharField(choices=USER_TYPE_CHOICES, max_length=5)

    # https://github.com/raphaelm/django-scopes#scoping-the-user-model
    objects = ScopedManager(tenant='tenant', _manager_class=MyUserManager)

Any idea why use_in_migrations causes the error? I have scoping applied to other models (although they are not using a custom manager class), and they all work fine.

andrewtmendoza avatar Dec 16 '20 03:12 andrewtmendoza

I have the same issue with my User model!

torkashvand avatar Dec 18 '20 09:12 torkashvand