django-scopes
django-scopes copied to clipboard
How to scope a many-to-many relationship
Hi @raphaelm - I'm working on a multi-tenancy app in which there is a many-to-many relationship between users and tenants.
Is this the correct way to represent it in django-scopes?
models.py
class MyUserManager(UserManager):
use_in_migrations = False
class User(AbstractUser):
USER_TYPE_CHOICES = [
("C", "Customer"),
("E", "Employee"),
]
tenants = models.ManyToManyField(Tenant)
phone_number = PhoneNumberField(blank=True)
line_of_business = models.ManyToManyField(LineOfBusiness)
sponsors = models.ManyToManyField("web.Sponsor")
user_type = models.CharField(choices=USER_TYPE_CHOICES, max_length=5)
# https://github.com/raphaelm/django-scopes#scoping-the-user-model
# https://stackoverflow.com/a/4508083/614770 - how to scope many to many field
objects = ScopedManager(tenant="tenants__id__contains", _manager_class=MyUserManager)
middleware.py
class ScopingMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
with scope(tenant=request.tenant.pk):
return self.get_response(request)
It seems to work most of the time, although I occasionally see a strange error with the User ModelForm where the Sponsor model data is retrieved from the incorrect tenant.
Actually, support for many to many relationships isn't intended (so far), so whatever works is more of a coincidence than a feature :(
I'm late to the show but you can just do this by:
objects = ScopedManager(tenant="tenants", _manager_class=MyUserManager)
And add a middleware:
class SetTenantMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
if request.user.is_authenticated:
if request.path.startswith("/admin_panel/"):
# no scoping in admin
with scopes_disabled():
response = self.get_response(request)
else:
with scope(tenant=request.user.tenant_id):
response = self.get_response(request)
else:
response = self.get_response(request)
return response
In case anyone else would encounter the same problem.