I'm trying to create a multi-tenant application where authentication happens through the django default user model. I've used my own custom user model. I want to use same database with multiple schemas.
User Model
class UsersManager(BaseUserManager):
def create_user(self, username, password=None, **kwargs):
if not username:
raise ValueError('Users must have a valid username.')
if not kwargs.get('email'):
raise ValueError('Users must have a valid email.')
if not kwargs.get('contact_number'):
raise ValueError('Users must have a valid contact number.')
account = self.model(
username=username, email = kwargs.get('email'), contact_number=kwargs.get('contact_number'),first_name=kwargs.get('first_name'),last_name=kwargs.get('last_name')
)
account.set_password(password)
account.save()
return account
def create_staffuser(self, username, password, **kwargs):
account = self.create_user(username, password, **kwargs)
account.is_admin = False
account.is_superuser = False
account.is_staff = True
account.is_agent = False
account.save()
return account
def create_superuser(self, username, password, **kwargs):
account = self.create_user(username, password, **kwargs)
account.is_admin = True
account.is_staff = True
account.is_superuser = True
account.is_agent = False
account.save()
return account
def create tenant_user(self, username, password, **kwargs):
"""Custom command to create a tenant user"""
... ?????????????????????
class User(AbstractBaseUser, PermissionsMixin):
first_name = models.CharField(max_length=50, default=None, null=True)
middle_name = models.CharField(max_length=50, default=None, null=True)
last_name = models.CharField(max_length=50, default = None, null=True)
username = models.CharField(unique=True, max_length=50)
email = models.EmailField(unique=True)
street_address = models.CharField(max_length=150)
city = models.CharField(max_length=50)
contact_number = models.CharField(max_length=40)
alternative_contact_number = models.CharField(max_length=40,default=None, null=True)
created_at = models.DateTimeField(default=timezone.now)
updated_at = models.DateTimeField(default=timezone.now)
objects = UsersManager()
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['email','first_name','last_name','city','contact_number']
is_staff = models.BooleanField(default=False)
is_admin = models.BooleanField(default=False)
is_agent = models.BooleanField(default=False)
is_customer = models.BooleanField(default=False)
is_tenant = models.BooleanField(default=False)
TenantModel
class Client(TenantMixin):
user = models.ManyToMany('User', related_name='hospital')
paid_until = models.DateField()
on_trial = models.BooleanField()
created_on = models.DateField(auto_now_add=True)
auto_create_schema = True
I'm creating many2many relation in TenantModel using User model. What i'm trying to achieve:
python manage.py create_tenant_user
It is all in the docs. You are missing the most important part of code which is your config settings.py
. If you want auth to be global then you need to put the auth in the shared schema as such:
settings.py
SHARED_APPS = [
'django_tenants',
'django.contrib.contenttypes',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'apps.client',
'apps.user_app_goes_here',
]
TENANT_APPS = [
'apps.other_tenant_specific_apps',
'more_3rd_party_apps',
]
Authentication happens where you define it. In the above setup I have tenant specific apps which are only accessible by specific tenants but auth
, user_model
and client
are handled on a shared schema.
Things you might run into:
If you do it this way you will have to write a custom middleware to redirect users to the correct domain (acting on the correct schema) if you are trying to have all users only able to access their own schema data. That said, you will need to log into the correct subdomain to access tenant specific data.
You do not need to create tenants through the CLI. This can also be done by loading the schema you want to write to. This is also all in the docs.
You will probably not find a ton of help on SO for this package - at least that has been my history with using it.