Code Monkey home page Code Monkey logo

django-tenant-users's Introduction

django-tenant-users

Build Status codecov PyPI Package Python Versions Django Versions PyPI Monthly Downloads

Welcome to django-tenant-users, the sidekick to django-tenants that sorts out user management in a multi-tenant Django world. ๐Ÿš€

Overview

Building on the solid foundation of django-tenants, django-tenant-users focuses on the nuances of user management across tenants. It ensures users can authenticate globally but have permissions tailored to each specific tenant. In essence, it's the bridge that sorts out the user stuff in a multi-tenant setup.

๐Ÿ” Key Features:

  • Global Authentication with Tenant-Specific Permissions: Authenticate once, act based on tenant rules.
  • Distinct Schema Permissions: Separate permissions for each tenant a user belongs to.
  • Seamless Integration: Works harmoniously with Django's built-in user and permissions frameworks.

Getting Started

  1. Installation: Dive into our Installation Guide to get django-tenant-users up and running.
  2. Usage: Check out the Using Guide to see how to make the most of the package.
  3. Core Concepts: If you're curious about the ideas driving our design, the Core Concepts page is a great place to start.

Contribute

We โค๏ธ contributions! Whether it's a bug report, a new feature, or feedback on the project, we'd love to hear from you.

License

django-tenant-users is open-source and is licensed under the MIT License.

django-tenant-users's People

Contributors

ajmatsick avatar crhowell avatar davidberardozzi avatar dependabot[bot] avatar dresdn avatar faisal-manzer avatar farhah avatar gdelfresno avatar jansalvador avatar jgentil avatar maximiliankindshofer avatar mkay849 avatar philippbosch avatar rmendes1 avatar tomturner avatar tyrion avatar viatrak avatar wizely99 avatar ysidromdenis avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

django-tenant-users's Issues

Interando com "django-rest-token"

Gentlemen,
I would like to adapt the project for use with a system using the "django-rest-framework".
Does anyone have any idea how to make the token generated on the specific tenant?
Thanks!

Example app is in previous version. An example in latest version is needed

It seems like current version of dtu-test-project is 1.8.

I am unable to run the app with following version - django-1.8, python-3.6 and django-tenants-1.2.1. I am able to run the server through python manage.py runserver command but unable to access any template at localhost:8000/ and localhost:8000/admin.

Current django version is 3.0. If the example app is in current version of django then it will be helpful.

Running tests fails when trying to create Tenant

I am trying to run tests on a project that uses django-tenant-schemas and django-tenant-users.

I've written a very basic test like this:

from tenant_schemas.test.cases import TenantTestCase
from .models import WorkshopCost

class BasicTest(TenantTestCase):
	def test_example(self):
		w = WorkshopCost.objects.create(cost=55.5)
		self.assertIs(w.cost, 55.5)

but when running ./manage.py test, I get this error:

ERROR: setUpClass (workshops.tests.BasicTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/django/db/backends/utils.py", line 65, in execute
    return self.cursor.execute(sql, params)
psycopg2.IntegrityError: null value in column "owner_id" violates not-null constraint
DETAIL:  Failing row contains (4, tenant.test.com, test, , 2018-05-04 20:54:04.159152+00, 2018-05-04 20:54:04.159162+00, , , null).


The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/tenant_schemas/test/cases.py", line 28, in setUpClass
    cls.tenant.save(verbosity=0)  # todo: is there any way to get the verbosity from the test command here?
  File "/usr/local/lib/python3.6/site-packages/tenant_users/tenants/models.py", line 80, in save
    super(TenantBase, self).save(*args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/tenant_schemas/models.py", line 67, in save
    super(TenantMixin, self).save(*args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/django/db/models/base.py", line 808, in save
    force_update=force_update, update_fields=update_fields)
  File "/usr/local/lib/python3.6/site-packages/django/db/models/base.py", line 838, in save_base
    updated = self._save_table(raw, cls, force_insert, force_update, using, update_fields)
  File "/usr/local/lib/python3.6/site-packages/django/db/models/base.py", line 924, in _save_table
    result = self._do_insert(cls._base_manager, using, fields, update_pk, raw)
  File "/usr/local/lib/python3.6/site-packages/django/db/models/base.py", line 963, in _do_insert
    using=using, raw=raw)
  File "/usr/local/lib/python3.6/site-packages/django/db/models/manager.py", line 85, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/django/db/models/query.py", line 1076, in _insert
    return query.get_compiler(using=using).execute_sql(return_id)
  File "/usr/local/lib/python3.6/site-packages/django/db/models/sql/compiler.py", line 1107, in execute_sql
    cursor.execute(sql, params)
  File "/usr/local/lib/python3.6/site-packages/django/db/backends/utils.py", line 65, in execute
    return self.cursor.execute(sql, params)
  File "/usr/local/lib/python3.6/site-packages/django/db/utils.py", line 94, in __exit__
    six.reraise(dj_exc_type, dj_exc_value, traceback)
  File "/usr/local/lib/python3.6/site-packages/django/utils/six.py", line 685, in reraise
    raise value.with_traceback(tb)
  File "/usr/local/lib/python3.6/site-packages/django/db/backends/utils.py", line 65, in execute
    return self.cursor.execute(sql, params)
django.db.utils.IntegrityError: null value in column "owner_id" violates not-null constraint
DETAIL:  Failing row contains (4, tenant.test.com, test, , 2018-05-04 20:54:04.159152+00, 2018-05-04 20:54:04.159162+00, , , null).

With @ivissani we've been thinking that maybe you need to subclass django-tenant-schema's TenantTestCase and modify the setUpClass method, so that it uses tenant_users.tenants.tasks. provision_tenant to create the test tenant?

python manage.py createsuperuser does not seem to work w/ django-tenant-users

Command line 'python manage.py createsuperuser' does not seem to work well when django-tenant-users is integrated with django-tenant-schemas.
'python manage.py createsuperuser' w/ public schema:
Enter Tenant Schema ('?' to list schemas): public
CommandError: Invalid tenant schema, 'public'

'python manage.py createsuperuser' w/ localhost schema:
Enter Tenant Schema ('?' to list schemas): localhost
tenant_users.tenants.models.SchemaError: Schema must be public for UserProfileManager user creation

This could be due to my configuration or integration.

How to add created user to specific tenant(s)?

So, I'm struggling with something that supposedly should be rather straight forward.

I have a public scheme and public_tenant.
I also have a tenant provisioned using a previously created TenantUser.

Now I wish to add a newly created user to that newly provisioned tenant.

Tried with the suggestion here but get error:
AttributeError: 'TenantUser' object has no attribute 'schema_name'

Then tried from the shell:

>>> cl = Client.objects.all()
>>> cl
<TenantQueryset [<Client: Public Tenant>, <Client: ExampleOrganization>]>
>>> user = TenantUser.objects.create_user(email="[email protected]", password='password', is_active=True, is_staff=True)
>>> user
<TenantUser: [email protected]>
>>> user.tenants.all()
<TenantQueryset [<Client: Public Tenant>]>
>>> user.tenants.add(cl[1])
>>> user.save()
>>> user.tenants.all()
<TenantQueryset [<Client: Public Tenant>, <Client: ExampleOrganization>]>

But then when I try to access the front of that tenant (Client) using that user cred, I get the following error
RelatedObjectDoesNotExist: TenantUser has no userprofile.

Can someone please explain me what am I doing wrong and how to fix it?

Cheers

Delete tenant

Hi, im try to delete a tenant and the schema in posrtgresql, can anyone help me whit this? thanks...

Create Multiple User per tenant

Hi

This package is really a great tool to implement authentication/authorization in multitenants. I am really thankfull.

I apologize if this concerns has already been addressed, at least did not find as far as I have searched. I expect some help or guidelines -

  1. How to assign a multiple user to a specific tenants schema ? I can assign only one.
  2. Can I implement or can they coexist, If I implement a token based authentication per tenants ? if so could you please put some guidelines ?
  3. How to create permission per tenancy ?

Many Thanks
Amin
[email protected]

pytest support?

For my django projects, I use pytest, not the django test runner, and it would be nice I think if the docs covered some usage hints for that and provided fixtures to support that combination.

Django 2.0 and django-tenant-schemas integration

Trying to add Django tenant users with Django 2.0 leads to these errors:

In tenant_users/permissions/models.py:

File "/usr/lib/python3.6/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 994, in _gcd_import
  File "<frozen importlib._bootstrap>", line 971, in _find_and_load
  File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 678, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "/home/cdb/Developpement/GCUP/TSGUPA/gup_main_backend/saas_legal_entities/models.py", line 3, in <module>
    from tenant_users.tenants.models import TenantBase
  File "/home/cdb/Developpement/GCUP/TSGUPA/gup_main_backend/venv/lib/python3.6/site-packages/tenant_users/tenants/models.py", line 15, in <module>
    from ..permissions.models import UserTenantPermissions, \
  File "/home/cdb/Developpement/GCUP/TSGUPA/gup_main_backend/venv/lib/python3.6/site-packages/tenant_users/permissions/models.py", line 98, in <module>
    class UserTenantPermissions(PermissionsMixin, AbstractBaseUserFacade):
  File "/home/cdb/Developpement/GCUP/TSGUPA/gup_main_backend/venv/lib/python3.6/site-packages/tenant_users/permissions/models.py", line 105, in UserTenantPermissions
    profile = models.OneToOneField(settings.AUTH_USER_MODEL)
TypeError: __init__() missing 1 required positional argument: 'on_delete'

And in tenant_users/tenants/models.py:

File "/usr/lib/python3.6/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 994, in _gcd_import
  File "<frozen importlib._bootstrap>", line 971, in _find_and_load
  File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 678, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "/home/cdb/Developpement/GCUP/TSGUPA/gup_main_backend/saas_legal_entities/models.py", line 3, in <module>
    from tenant_users.tenants.models import TenantBase
  File "/home/cdb/Developpement/GCUP/TSGUPA/gup_main_backend/venv/lib/python3.6/site-packages/tenant_users/tenants/models.py", line 58, in <module>
    class TenantBase(TenantMixin):
  File "/home/cdb/Developpement/GCUP/TSGUPA/gup_main_backend/venv/lib/python3.6/site-packages/tenant_users/tenants/models.py", line 66, in TenantBase
    owner = models.ForeignKey(settings.AUTH_USER_MODEL)
TypeError: __init__() missing 1 required positional argument: 'on_delete'

Refering to https://docs.djangoproject.com/en/2.0/ref/models/fields/#foreignkey it should be necessary to add the on_delete=models.CASCADE option on both tenants and permissions.

Everything is working like a charm after these modifications.
Thanks for this great work!

How does this work? I don't get it.

I got everything configured and setup a public tenant.

Is the system account suspended to not have a password and never be used?

When I create a user e.g. via the shell. The user the user can't login to the administrator site even if I declare him superuser.

I want to have users to be able to authenticate to one tenant but not to any other. And I want users to be able to authenticate at all tenants (superusers).

I have no clue how to do this but it seems that this is the right app for it.

So what am I doing wrong?

Unable to successfully login to django in any of public/other tenant

Read me or sample code never guided the exact format/order of instructions for tenant creations so I am trying mine according to given guidelines to create tenants with code in one of my shared app view

Created public tenant
public_owner = "owner@local" create_public_tenant("localhost", public_owner) tenant_admin_email = "admin@local" TenantUser.objects.create_superuser('123', tenant_admin_email)
Created my tenant
tenant_admin_email = "admin@" + tenant_name TenantUser.objects.create_superuser('123', tenant_admin_email) provision_tenant(tenant_name, tenant_name, tenant_admin_email)
It does all => creates tenant with schema and both the super users as well

But access to admin panel on public never succeeds (even login gets the user) with zero message
And in tenants it always gives message as "Please enter the correct Email Address and password"

My settings are as
`
MIDDLEWARE = (
'tenant_tutorial.middleware.TenantTutorialMiddleware',
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
# Uncomment the next line for simple clickjacking protection:
# 'django.middleware.clickjacking.XFrameOptionsMiddleware',
)

TENANT_MODEL = "customers.Client" # app.Model
TENANT_DOMAIN_MODEL = "customers.Domain" # app.Model
TENANT_USERS_DOMAIN = "localhost"
AUTH_USER_MODEL = 'users.TenantUser'
AUTHENTICATION_BACKENDS = (
'tenant_users.permissions.backend.UserBackend',
)
SESSION_COOKIE_DOMAIN = '.' + TENANT_USERS_DOMAIN

SHARED_APPS = (
'django_tenants',
'django.contrib.admin',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'tenant_users.permissions',
'tenant_users.tenants',
'customers', # you must list the app where your tenant model resides in
'users',
)

TENANT_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'tenant_users.permissions',
'django.contrib.admin',
'django.contrib.sessions',
'django.contrib.messages',
'tenant_only',
)

INSTALLED_APPS = list(set(TENANT_APPS + SHARED_APPS))
`

Cross domain auth

I'm completely new to tenant stuff, but your project seems like the exact solution I'm looking for (global users with authentication between multiple sites). However, all of the projects I see for this including django-tenants and this project seem to only allow subdomains from looking at the docs.

Have I mistunderstood this or are domains allowed? My use case is that if a user logs into main-site.com they would need to be logged into site1.com, site2.com, .... so on and so forth. Similarly, I'm looking to share cookies, think a shopping cart, between the sites as well.

My project has tightly coupled code for the different sites, where each site points to a different app, and the urls are simply routed to the correct app based upon the domain.

Before I started monkeying around with this, and potentially wasting a massive amount of time, I was wondering if this is even possible using django-tenant-users?

Thank you.

How to configure the permission system?

So when using django-tenant-users, the groups are now inside the tenant_app and the users in the shared_app.
I want to filter a user according to their group in the public shema
User.objects.filter (groups__name = "teacher")
Within each tenant, there are local groups and I will use them for permissions within the tenant.
The groups within the public tenant will be for permissions in shared apps.
So how do I filter the group from the user?

Global superuser to control all tenants

Hi guys,

I'm wondering how could I create a global superuser who can control all data in any tenants. They can login to tenants admin and has_perm always return True regardless of permissions table in tenant schema. Any idea is appreciated.

Thanks!

Example is not working properly

I'm trying to setup example but it is not working properly
DeepinScreenshot_select-area_20200228212843
Here is setting files

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
import os
import sys

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '%-@80c)i9@htvdo#ulmrail^%kizuoddr$(8d6*2r^eczhj#^1'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = ['*']

AUTH_USER_MODEL = 'users.TenantUser'

# Application definition

if os.environ.get('TENANT_APP', 'tenant_schemas') == 'django_tenants':
    TENANT_SCHEMAS = False
else:
    TENANT_SCHEMAS = True

MIDDLEWARE_CLASSES = (
    'tenant_schemas.middleware.TenantMiddleware' if TENANT_SCHEMAS else 'django_tenants.middleware.TenantMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'django.middleware.security.SecurityMiddleware',
)


SHARED_APPS = (
    # mandatory - django_tenants  -tenant_schemas
    'tenant_schemas' if TENANT_SCHEMAS else 'django_tenants',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    'tenant_users.permissions',
    'tenant_users.tenants',
    'core',
    'customers',
    'users',
)

TENANT_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'tenant_users.permissions',
)

TENANT_MODEL = 'customers.Client'
if not TENANT_SCHEMAS:
    TENANT_DOMAIN_MODEL = 'customers.Domain'


INSTALLED_APPS = list(SHARED_APPS) + \
    [app for app in TENANT_APPS if app not in SHARED_APPS]


AUTHENTICATION_BACKENDS = (
    'tenant_users.permissions.backend.UserBackend',
)

ROOT_URLCONF = 'dtu_test_project.urls_tenants'
PUBLIC_SCHEMA_URLCONF = 'dtu_test_project.urls_public'


TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'dtu_test_project.wsgi.application'


# Database
# https://docs.djangoproject.com/en/1.8/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'tenant_schemas.postgresql_backend' if TENANT_SCHEMAS else 'django_tenants.postgresql_backend',
        "NAME": "multi",  # Or path to database file if using sqlite3.
        "USER": "multi",
        "PASSWORD": "password",
        "HOST": "localhost",
        "PORT": "",  # Set to empty string for default.
    }
}


DATABASE_ROUTERS = (
    'tenant_schemas.routers.TenantSyncRouter' if TENANT_SCHEMAS else 'django_tenants.routers.TenantSyncRouter',
)

# Internationalization
# https://docs.djangoproject.com/en/1.8/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.8/howto/static-files/

STATIC_URL = '/static/'

TENANT_USERS_DOMAIN = "example.com"

SESSION_COOKIE_DOMAIN = '.example.com'

DEFAULT_FILE_STORAGE = 'tenant_schemas.storage.TenantFileSystemStorage'

Requirements.txt file

asgiref==3.2.3
autopep8==1.5
Django==2.2.5
django-tenant-schemas==1.10.0
django-tenant-users==0.3.12
entrypoints==0.3
flake8==3.7.9
mccabe==0.6.1
ordered-set==3.1.1
psycopg2-binary==2.8.4
pycodestyle==2.5.0
pyflakes==2.1.1
pytz==2019.3
six==1.14.0
sqlparse==0.3.0

Django Tenants

This project will work will also work on https://github.com/tomturner/django-tenants it was a clone of django-tenant-schemas when a group of us though it was a dead project.
It now has some enhancements like parallel migrations which django-tenant-schemas doesn't have (as far I know)

Any chance you could mention django-tenants version as well?
I will put a link back to you as well

Keep up the good work

reverse accessor name clashes on groups and userpermission

I am using django_tenants and tried to use this for user. But after following the steps mentioned- After runserver i am getting -

`File "/home/vikram/dev/ezeie/env/lib/python3.7/site-packages/django/core/management/base.py", line 436, in check
raise SystemCheckError(msg)
django.core.management.base.SystemCheckError: SystemCheckError: System check identified some issues:

ERRORS:
permissions.UserTenantPermissions.groups: (fields.E304) Reverse accessor for 'UserTenantPermissions.groups' clashes with reverse accessor for 'User.groups'.
HINT: Add or change a related_name argument to the definition for 'UserTenantPermissions.groups' or 'User.groups'.
permissions.UserTenantPermissions.user_permissions: (fields.E304) Reverse accessor for 'UserTenantPermissions.user_permissions' clashes with reverse accessor for 'User.user_permissions'.
HINT: Add or change a related_name argument to the definition for 'UserTenantPermissions.user_permissions' or 'User.user_permissions'.
user.User.groups: (fields.E304) Reverse accessor for 'User.groups' clashes with reverse accessor for 'UserTenantPermissions.groups'.
HINT: Add or change a related_name argument to the definition for 'User.groups' or 'UserTenantPermissions.groups'.
user.User.user_permissions: (fields.E304) Reverse accessor for 'User.user_permissions' clashes with reverse accessor for 'UserTenantPermissions.user_permissions'.
HINT: Add or change a related_name argument to the definition for 'User.user_permissions' or 'UserTenantPermissions.user_permissions'.

System check identified 4 issues (0 silenced).
`

I am usingnDjango 2.2 with python 3.7.

AttributeError: can't set attribute

Created a fresh Django project to understand djang-tenant-users. The project has only two apps tenants and authy for the custom user.

After following the guide on how to use django-tenant-users, am getting the error AttributeError: can't set attribute when I run makemigrations or migrate_schemas.

  File "manage.py", line 39, in <module>
    main()
  File "manage.py", line 35, in main
    execute_from_command_line(sys.argv)
  File "/Volumes/storage/apps/venv/tenant_env/lib/python3.6/site-packages/django/core/management/__init__.py", line 371, in execute_from_command_line
    utility.execute()
  File "/Volumes/storage/apps/venv/tenant_env/lib/python3.6/site-packages/django/core/management/__init__.py", line 365, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/Volumes/storage/apps/venv/tenant_env/lib/python3.6/site-packages/django/core/management/base.py", line 288, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/Volumes/storage/apps/venv/tenant_env/lib/python3.6/site-packages/django/core/management/base.py", line 332, in execute
    self.check()
  File "/Volumes/storage/apps/venv/tenant_env/lib/python3.6/site-packages/django/core/management/base.py", line 364, in check
    include_deployment_checks=include_deployment_checks,
  File "/Volumes/storage/apps/venv/tenant_env/lib/python3.6/site-packages/django/core/management/base.py", line 351, in _run_checks
    return checks.run_checks(**kwargs)
  File "/Volumes/storage/apps/venv/tenant_env/lib/python3.6/site-packages/django/core/checks/registry.py", line 73, in run_checks
    new_errors = check(app_configs=app_configs)
  File "/Volumes/storage/apps/venv/tenant_env/lib/python3.6/site-packages/django/contrib/auth/checks.py", line 74, in check_user_model
    if isinstance(cls().is_anonymous, MethodType):
  File "/Volumes/storage/apps/venv/tenant_env/lib/python3.6/site-packages/django/db/models/base.py", line 480, in __init__
    _setattr(self, field.attname, val)
AttributeError: can't set attribute

Apps Layout

SHARED_APPS = (
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.humanize',  # Useful templates tags:

    'django_tenants',  # tomturner/django-tenants
    'tenant_users.permissions',
    'tenant_users.tenants',

    'tenants',
    'authy', 
)

TENANT_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'tenant_users.permissions',
)

INSTALLED_APPS = list(SHARED_APPS) + [app for app in TENANT_APPS if app not in SHARED_APPS]

Packages in Use

Django==2.0.6
django-tenants==2.0.0
django-tenant-users==0.3.11

FYI, if I remove the django-tenant-users settings and remain only with django-tenants settings, I don't get any error.

Update
I've narrowed down the issue to a field in my custom user model.

is_staff = models.BooleanField(default=False)

When that specific field is present, the above error occurs. When its not, makemigrations and other commands execute properly. Why would that be?

No example app

An example app would be very helpful. But there does not seem to be one.

Make PermissionsMixinFacade._get_tenant_perms public

I was trying to edit the staff status of an user through the shell but I got the AttributeError mentioned in issue #20 .

By reading a bit of the code, I managed to do what I needed with:

>>> from django.db import connection
>>> connection.set_tenant(my_tenant)
>>> perms = my_user._get_tenant_perms()
>>> perms.is_staff = True
>>> perms.save()

I have the following suggestions:

  • I think it would be good to raise a more helpful error message to instruct the user that the is_staff/is_superuser properties are not writable and point them in the right direction.
  • I think a good direction could be to make get_tenant_perms() public (i.e. removing the leading underscore) and maybe add some documentation about it in the README.
  • An other thing to consider could be to make get_tenant_perms a cached_property and name it for example tenant_perms (right now everytime I access is_staff a query is made which could be avoided)

An other solution instead of making _get_tenant_perms public, could be to enable transparently saving is_staff to the current UserTenantPermissions, but I don't think it is the best approach since it would require an implicit UPDATE query everytime we set is_staff/is_superuser (explicit is better than implicit)

I could probably submit a PR if you're interested
Thanks!

Error creating public tenant

Hi,

Just getting setup and using for the first time, and encountered the below error creating the public tenant, looks like might be a compatibility issue, is there a version of tenant-schemas I should stick to?

create_public_tenant("localhost","demo")
()
{'force_insert': True, 'using': 'default'}
Traceback (most recent call last):
File "/Users/danielthornton/Documents/liclipse_workspace/teamtalk-env/lib/python3.7/site-packages/django/db/backends/utils.py", line 85, in _execute
return self.cursor.execute(sql, params)
psycopg2.IntegrityError: null value in column "paid_until" violates not-null constraint
DETAIL: Failing row contains (1, localhost, public, , 2018-09-12 15:54:44.797985+00, 2018-09-12 15:54:44.798003+00, Public Tenant, null, null, 2018-09-12, 1).

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
File "", line 1, in
File "/Users/danielthornton/Documents/liclipse_workspace/teamtalk-env/lib/python3.7/site-packages/tenant_users/tenants/utils.py", line 36, in create_public_tenant
owner=profile)
File "/Users/danielthornton/Documents/liclipse_workspace/teamtalk-env/lib/python3.7/site-packages/django/db/models/manager.py", line 82, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/Users/danielthornton/Documents/liclipse_workspace/teamtalk-env/lib/python3.7/site-packages/django/db/models/query.py", line 413, in create
obj.save(force_insert=True, using=self.db)
File "/Users/danielthornton/Documents/liclipse_workspace/teamtalk-env/lib/python3.7/site-packages/tenant_users/tenants/models.py", line 80, in save
super(TenantBase, self).save(*args, **kwargs)
File "/Users/danielthornton/Documents/liclipse_workspace/teamtalk-env/lib/python3.7/site-packages/tenant_schemas/models.py", line 69, in save
super(TenantMixin, self).save(*args, **kwargs)
File "/Users/danielthornton/Documents/liclipse_workspace/teamtalk-env/lib/python3.7/site-packages/django/db/models/base.py", line 717, in save
force_update=force_update, update_fields=update_fields)
File "/Users/danielthornton/Documents/liclipse_workspace/teamtalk-env/lib/python3.7/site-packages/django/db/models/base.py", line 747, in save_base
updated = self._save_table(raw, cls, force_insert, force_update, using, update_fields)
File "/Users/danielthornton/Documents/liclipse_workspace/teamtalk-env/lib/python3.7/site-packages/django/db/models/base.py", line 830, in _save_table
result = self._do_insert(cls._base_manager, using, fields, update_pk, raw)
File "/Users/danielthornton/Documents/liclipse_workspace/teamtalk-env/lib/python3.7/site-packages/django/db/models/base.py", line 868, in _do_insert
using=using, raw=raw)
File "/Users/danielthornton/Documents/liclipse_workspace/teamtalk-env/lib/python3.7/site-packages/django/db/models/manager.py", line 82, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/Users/danielthornton/Documents/liclipse_workspace/teamtalk-env/lib/python3.7/site-packages/django/db/models/query.py", line 1133, in _insert
return query.get_compiler(using=using).execute_sql(return_id)
File "/Users/danielthornton/Documents/liclipse_workspace/teamtalk-env/lib/python3.7/site-packages/django/db/models/sql/compiler.py", line 1285, in execute_sql
cursor.execute(sql, params)
File "/Users/danielthornton/Documents/liclipse_workspace/teamtalk-env/lib/python3.7/site-packages/django/db/backends/utils.py", line 100, in execute
return super().execute(sql, params)
File "/Users/danielthornton/Documents/liclipse_workspace/teamtalk-env/lib/python3.7/site-packages/django/db/backends/utils.py", line 68, in execute
return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
File "/Users/danielthornton/Documents/liclipse_workspace/teamtalk-env/lib/python3.7/site-packages/django/db/backends/utils.py", line 77, in _execute_with_wrappers
return executor(sql, params, many, context)
File "/Users/danielthornton/Documents/liclipse_workspace/teamtalk-env/lib/python3.7/site-packages/django/db/backends/utils.py", line 85, in _execute
return self.cursor.execute(sql, params)
File "/Users/danielthornton/Documents/liclipse_workspace/teamtalk-env/lib/python3.7/site-packages/django/db/utils.py", line 89, in exit
raise dj_exc_value.with_traceback(traceback) from exc_value
File "/Users/danielthornton/Documents/liclipse_workspace/teamtalk-env/lib/python3.7/site-packages/django/db/backends/utils.py", line 85, in _execute
return self.cursor.execute(sql, params)
django.db.utils.IntegrityError: null value in column "paid_until" violates not-null constraint
DETAIL: Failing row contains (1, localhost, public, , 2018-09-12 15:54:44.797985+00, 2018-09-12 15:54:44.798003+00, Public Tenant, null, null, 2018-09-12, 1).

Is UserProfile based on username or email address?

Maybe a stupid question but I couldn't find this in the documentation.

I want to have users log in with their email address and not a seperate username. Is UserProfile based on a username or email address?

Thanks,
Egbert

Admin site login per Tenant and changing User permissions

When trying to log in at tenants Admin page (Except Public's Admin) no user that I create gets to do it. I created 3 users by now in these different ways:

eviluser = TenantUser.objects.create_user(email="[email protected]", password='password', is_active=True)
staffuser = TenantUser.objects.create_user(email="[email protected]", password='password', is_staff=True, is_active=True)

and the third one using manage.py createsuperuser on the public schema (I'm unable to create a superuser in a schema that isn't public), I will refer to it as surperuser.

staffuser and superuser can login into the public tenant admin page but when trying to log into other tenants admin page I get this message:

imagen

I get an expected follow up error if i try with the same credentials saying "Please enter the correct Email Address and password for a staff account. Note that both fields may be case-sensitive."

Using python manage.py shell I have added both of these users to both tenats using the tenantObject.add_user(staffuser/superuser), in addition I have made staffuser the owner of the second tenant using superuser in the public tenant admin page but I still can't get any of the users to log into this tenant.

I have checked if they have been correctly added to the tenant, using the function tenant1.user_set.all() and it shows both users.

eviluser can't login into any admin page and I understand that this is because it has no staff permissions.

My question is how do I grant superuser and staffuser the permissions needed to login into other tenants admin site and also how can I change permissions once inside the admin site? (i.e. allowing staffuser to edit information in public tenant admin site or granting superuser all permissions in all tenant admin sites)

Here is my settings.py on this gist , I'm using django-tenant-users along with django-tenant-schemas.

Accessing the Model that captures the user-tenants association

I want to access the model capturing the list of Tenants a User has access to.

Looking at the code, UserProfile.tenants is a ManyToMany entry and so the UserTenants model code is not accessible. To make it accessible, I've tried, overriding the tenants field in my User Model and added a through=UserTenant entry and created the relevant UserTenant model.

tenants = models.ManyToManyField(
    settings.TENANT_MODEL,
    verbose_name=_('tenants'),
    blank=True,
    help_text=_('The tenants this user belongs to.'),
    related_name="user_set",
    through='UserTenant',    <===========Added this to refer to the UserTenant model below
)
class UserTenant(models.Model):
    """
    Tenants that the user has been added to
    """
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    tenant = models.ForeignKey(Tenant, on_delete=models.CASCADE)
    is_active = models.BooleanField(default=True) 

However, this brings up new errors.

from tenant_users.tenants.utils import create_public_tenant
create_public_tenant("evil.corp", "[email protected]")

This bring the error..

AttributeError: Cannot use add() on a ManyToManyField which specifies an intermediary model. Use authentication.UserTenant's Manager instead.

Or am I going at it the wrong way? Is there an easier way to access this model?

Am using django-tenants with Django Rest Framework and building custom permissions for the API calls so I want to reference the model as below:

class UserTenantRole(models.Model):
    """
    Roles given to a user within a tenant
    """
    user_tenant = models.ForeignKey(UserTenant, on_delete=models.CASCADE)  <====Access it here
    role = models.ForeignKey(Role, on_delete=models.CASCADE)
    is_active = models.BooleanField(default=True)

CircularDependencyError between users and tenants

I have to separate applications - tenance and users models.

Tenance.models.py


from tenant_users.tenants.models import TenantBase

class Tenant(TenantBase):
    name = models.CharField(max_length=100)

    def __str__(self):
        return self.name

Users.models.py

from django.db import models
from tenant_users.tenants.models import UserProfile

# Create your models here.
class TenantUser(UserProfile):
    name = models.CharField("Name", max_length=100, blank=True)

After preparing models and running ./manage.py migrate_schemas --shared I catch this error:

django.db.migrations.exceptions.CircularDependencyError: users.0001_initial, tenance.0001_initial

I cleared migrations and tried again but still catching this type of errors.
Should I split the .models file or is there any other solution?

Testing

When I'm writing tests for all my apps (models/forms/views) I assume I need to first set up the public tenant, and a test tenant to run all my tenant_apps on?... Do you have any experience writing tests with DTU and django-tenants?

Error creating Tenants in app or command line

Tenants can not be created when using 'create_tenant' when using "./manage.py create_tenant". I even tried

These are the fields appearing...

schema name: test
Tenant URL Name: test
owner: testowner
modified:
name: test
description: test

which then returns: "ValueError: Cannot assign "'testowner": "Client.owner" must be a "TenantUser" instance." is the result.

I also tried to create a tenant with "tenant = Client(schema_name='tenant1', name='My First Tenant')..." and got...

IntegrityError: null value in column "owner_id" violates not-null constraint
DETAIL: Failing row contains (4, tenant1, , 2019-08-15 23:59:12.722278+00, 2019-08-15 23:59:12.721594+00, My First Tenant, null, 2019-08-15, null).

How should I handle the 'owner' field, which is a Foreign Key, at the command line? There
are no tests.

--------- Full traceback
Traceback (most recent call last):
File "./manage.py", line 33, in
execute_from_command_line(sys.argv)
File "/usr/local/lib/python3.7/site-packages/django/core/management/init.py", line 381, in execute_from_command_line
utility.execute()
File "/usr/local/lib/python3.7/site-packages/django/core/management/init.py", line 375, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/usr/local/lib/python3.7/site-packages/django/core/management/base.py", line 323, in run_from_argv
self.execute(*args, **cmd_options)
File "/usr/local/lib/python3.7/site-packages/django/core/management/base.py", line 364, in execute
output = self.handle(*args, **options)
File "/usr/local/lib/python3.7/site-packages/django_tenants/management/commands/create_tenant.py", line 58, in handle
tenant = self.store_tenant(**tenant_data)
File "/usr/local/lib/python3.7/site-packages/django_tenants/management/commands/create_tenant.py", line 85, in store_tenant
tenant = get_tenant_model().objects.create(**fields)
File "/usr/local/lib/python3.7/site-packages/django/db/models/manager.py", line 82, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/usr/local/lib/python3.7/site-packages/django/db/models/query.py", line 420, in create
obj = self.model(**kwargs)
File "/usr/local/lib/python3.7/site-packages/django/db/models/base.py", line 483, in init
_setattr(self, field.name, rel_obj)
File "/usr/local/lib/python3.7/site-packages/django/db/models/fields/related_descriptors.py", line 211, in set
self.field.remote_field.model._meta.object_name,
ValueError: Cannot assign "'testowner'": "Client.owner" must be a "TenantUser" instance.

django.db.utils.ProgrammingError: relation "accounts_client" does not exist

I am trying to create tenant account and have following model:-

import uuid
from django.db import models
from tenant_users.tenants.models import TenantBase
from django_tenants.models import DomainMixin


class Company(models.Model):
    company_id = models.UUIDField(
        default=uuid.uuid4, editable=False, primary_key=True)
    company_name = models.CharField(max_length=50)
    no_of_employees = models.IntegerField(default=0)


class Domain(DomainMixin):
    company = models.OneToOneField(Company, on_delete=models.CASCADE)


class Client(TenantBase):
    id = models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True)
    start_date = models.DateTimeField(auto_now_add=True)
    user = models.OneToOneField(
        "users.User", on_delete=models.CASCADE, related_name='customer')
    plan = models.OneToOneField("plan.Plan", on_delete=models.CASCADE)
    device = models.ForeignKey(
        "device.Device", null=True, on_delete=models.CASCADE)
    domain = models.OneToOneField(Domain, on_delete=models.CASCADE)

and

import uuid
from django.db import models

# Create your models here.


class Device(models.Model):
    device_id = models.UUIDField(
        default=uuid.uuid4, editable=False, primary_key=True)
    ip = models.GenericIPAddressField(null=True)
    wires = models.IntegerField(default=0)

When I try to run ./manage.py migrate_schemas --tenant I am getting following error:-

Traceback (most recent call last):
  File "/home/vineet/projects/env-cosgrid/lib/python3.6/site-packages/django/db/backends/utils.py", line 86, in _execute
    return self.cursor.execute(sql, params)
psycopg2.errors.UndefinedTable: relation "accounts_client" does not exist
LINE 1: SELECT "accounts_client"."schema_name" FROM "accounts_client...
                                                    ^


The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "./manage.py", line 21, in <module>
    main()
  File "./manage.py", line 17, in main
    execute_from_command_line(sys.argv)
  File "/home/vineet/projects/env-cosgrid/lib/python3.6/site-packages/django/core/management/__init__.py", line 401, in execute_from_command_line
    utility.execute()
  File "/home/vineet/projects/env-cosgrid/lib/python3.6/site-packages/django/core/management/__init__.py", line 395, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/home/vineet/projects/env-cosgrid/lib/python3.6/site-packages/django/core/management/base.py", line 328, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/home/vineet/projects/env-cosgrid/lib/python3.6/site-packages/django/core/management/base.py", line 369, in execute
    output = self.handle(*args, **options)
  File "/home/vineet/projects/env-cosgrid/lib/python3.6/site-packages/django_tenants/management/commands/migrate_schemas.py", line 63, in handle
    executor.run_migrations(tenants=tenants)
  File "/home/vineet/projects/env-cosgrid/lib/python3.6/site-packages/django_tenants/migration_executors/standard.py", line 8, in run_migrations
    tenants = tenants or []
  File "/home/vineet/projects/env-cosgrid/lib/python3.6/site-packages/django/db/models/query.py", line 280, in __bool__
    self._fetch_all()
  File "/home/vineet/projects/env-cosgrid/lib/python3.6/site-packages/django/db/models/query.py", line 1261, in _fetch_all
    self._result_cache = list(self._iterable_class(self))
  File "/home/vineet/projects/env-cosgrid/lib/python3.6/site-packages/django/db/models/query.py", line 184, in __iter__
    for row in compiler.results_iter(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size):
  File "/home/vineet/projects/env-cosgrid/lib/python3.6/site-packages/django/db/models/sql/compiler.py", line 1103, in results_iter
    results = self.execute_sql(MULTI, chunked_fetch=chunked_fetch, chunk_size=chunk_size)
  File "/home/vineet/projects/env-cosgrid/lib/python3.6/site-packages/django/db/models/sql/compiler.py", line 1151, in execute_sql
    cursor.execute(sql, params)
  File "/home/vineet/projects/env-cosgrid/lib/python3.6/site-packages/django/db/backends/utils.py", line 100, in execute
    return super().execute(sql, params)
  File "/home/vineet/projects/env-cosgrid/lib/python3.6/site-packages/django/db/backends/utils.py", line 68, in execute
    return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
  File "/home/vineet/projects/env-cosgrid/lib/python3.6/site-packages/django/db/backends/utils.py", line 77, in _execute_with_wrappers
    return executor(sql, params, many, context)
  File "/home/vineet/projects/env-cosgrid/lib/python3.6/site-packages/django/db/backends/utils.py", line 86, in _execute
    return self.cursor.execute(sql, params)
  File "/home/vineet/projects/env-cosgrid/lib/python3.6/site-packages/django/db/utils.py", line 90, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "/home/vineet/projects/env-cosgrid/lib/python3.6/site-packages/django/db/backends/utils.py", line 86, in _execute
    return self.cursor.execute(sql, params)
django.db.utils.ProgrammingError: relation "accounts_client" does not exist
LINE 1: SELECT "accounts_client"."schema_name" FROM "accounts_client...

Also I try to run ./manage.py migrate_schemas --shared, I am getting following error:-

[standard:public] === Starting migration
[standard:public] Operations to perform:
[standard:public]   Apply all migrations: accounts, admin, auth, contenttypes, device, permissions, plan, sessions, users
[standard:public] Running migrations:
[standard:public]   Applying device.0001_initial...
[standard:public]  OK
[standard:public]   Applying accounts.0001_initial...
Traceback (most recent call last):
  File "/home/vineet/projects/env-cosgrid/lib/python3.6/site-packages/django/db/backends/utils.py", line 86, in _execute
    return self.cursor.execute(sql, params)
psycopg2.errors.UndefinedTable: relation "device_device" does not exist


The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "./manage.py", line 21, in <module>
    main()
  File "./manage.py", line 17, in main
    execute_from_command_line(sys.argv)
  File "/home/vineet/projects/env-cosgrid/lib/python3.6/site-packages/django/core/management/__init__.py", line 401, in execute_from_command_line
    utility.execute()
  File "/home/vineet/projects/env-cosgrid/lib/python3.6/site-packages/django/core/management/__init__.py", line 395, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/home/vineet/projects/env-cosgrid/lib/python3.6/site-packages/django/core/management/base.py", line 328, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/home/vineet/projects/env-cosgrid/lib/python3.6/site-packages/django/core/management/base.py", line 369, in execute
    output = self.handle(*args, **options)
  File "/home/vineet/projects/env-cosgrid/lib/python3.6/site-packages/django_tenants/management/commands/migrate_schemas.py", line 49, in handle
    executor.run_migrations(tenants=[self.PUBLIC_SCHEMA_NAME])
  File "/home/vineet/projects/env-cosgrid/lib/python3.6/site-packages/django_tenants/migration_executors/standard.py", line 11, in run_migrations
    run_migrations(self.args, self.options, self.codename, self.PUBLIC_SCHEMA_NAME)
  File "/home/vineet/projects/env-cosgrid/lib/python3.6/site-packages/django_tenants/migration_executors/base.py", line 36, in run_migrations
    MigrateCommand(stdout=stdout, stderr=stderr).execute(*args, **options)
  File "/home/vineet/projects/env-cosgrid/lib/python3.6/site-packages/django/core/management/base.py", line 369, in execute
    output = self.handle(*args, **options)
  File "/home/vineet/projects/env-cosgrid/lib/python3.6/site-packages/django/core/management/base.py", line 83, in wrapped
    res = handle_func(*args, **kwargs)
  File "/home/vineet/projects/env-cosgrid/lib/python3.6/site-packages/django/core/management/commands/migrate.py", line 233, in handle
    fake_initial=fake_initial,
  File "/home/vineet/projects/env-cosgrid/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/vineet/projects/env-cosgrid/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/vineet/projects/env-cosgrid/lib/python3.6/site-packages/django/db/migrations/executor.py", line 247, in apply_migration
    migration_recorded = True
  File "/home/vineet/projects/env-cosgrid/lib/python3.6/site-packages/django/db/backends/base/schema.py", line 115, in __exit__
    self.execute(sql)
  File "/home/vineet/projects/env-cosgrid/lib/python3.6/site-packages/django/db/backends/base/schema.py", line 142, in execute
    cursor.execute(sql, params)
  File "/home/vineet/projects/env-cosgrid/lib/python3.6/site-packages/django/db/backends/utils.py", line 100, in execute
    return super().execute(sql, params)
  File "/home/vineet/projects/env-cosgrid/lib/python3.6/site-packages/django/db/backends/utils.py", line 68, in execute
    return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
  File "/home/vineet/projects/env-cosgrid/lib/python3.6/site-packages/django/db/backends/utils.py", line 77, in _execute_with_wrappers
    return executor(sql, params, many, context)
  File "/home/vineet/projects/env-cosgrid/lib/python3.6/site-packages/django/db/backends/utils.py", line 86, in _execute
    return self.cursor.execute(sql, params)
  File "/home/vineet/projects/env-cosgrid/lib/python3.6/site-packages/django/db/utils.py", line 90, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "/home/vineet/projects/env-cosgrid/lib/python3.6/site-packages/django/db/backends/utils.py", line 86, in _execute
    return self.cursor.execute(sql, params)
django.db.utils.ProgrammingError: relation "device_device" does not exist

I have following in settings.py:-

"""
Django settings for cosgrid project.

Generated by 'django-admin startproject' using Django 3.0.6.

For more information on this file, see
https://docs.djangoproject.com/en/3.0/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.0/ref/settings/
"""

import os

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '*

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []


# Application definition

SHARED_APPS = [
    'django_tenants',
    'django.contrib.auth',
    'django.contrib.admin',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    'tenant_users.permissions',
    'tenant_users.tenants'
    'accounts',
    'users',
    'plan'
]

TENANT_APPS = [
    'device',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'tenant_users.permissions',
]


INSTALLED_APPS = list(SHARED_APPS) + \
    [app for app in TENANT_APPS if app not in SHARED_APPS]

MIDDLEWARE = [
    'django_tenants.middleware.main.TenantMainMiddleware',
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'cosgrid.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'cosgrid.wsgi.application'


# Database
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django_tenants.postgresql_backend',
        'NAME': '***',
        'USER': '***',
        'PASSWORD': '***',
        'HOST': '***',
        'PORT': '***'
    }
}


# Password validation
# https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/3.0/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.0/howto/static-files/

STATIC_URL = '/static/'

SESSION_COOKIE_DOMAIN = '.mydomain.com'

TENANT_USERS_DOMAIN = "example.com"

AUTH_USER_MODEL = 'users.User'

TENANT_MODEL = 'accounts.Client'

TENANT_DOMAIN_MODEL = 'accounts.Domain'

AUTHENTICATION_BACKENDS = (
    'tenant_users.permissions.backend.UserBackend',
)

DATABASE_ROUTERS = (
    'django_tenants.routers.TenantSyncRouter',
)

Could not figure out why this is happening. Is it a issue with django-tenant-users because I have seen similar unsolved errors on stackoverflow which are trying to implement multi-tenancy in django

More complete example

@Viatrak This looks like a nice addition to django-tenants, but do you have a more complete example than the dtu_test_project showing how to implement the authentication system with tenant specific apps? For example, a simple interface for each tenant to have their own addressbook? i.e.: Tenant1.mysite.com can have a different set of addresses than Tenant2.mysite.com, and users of both tenants can login using an email address from mysite.com and be redirected to their respective tenant subdomain. Any help is appreciated!

Log In Failed

Hi, I register users to my tenant in public and extra tenant but doesn't matter I can't log in on the admin site... Some help here please.

I suggest a small change in the documentation

I had many problems trying to find out why, when I logged in correctly, django returned to the login page without any errors. The problem was here
SESSION_COOKIE_DOMAIN = '.mydomain.com'
When entering the web without "www", there is no point before the domain, so you can only enter the admin with subdomain but not the admin of the general website (without subdomain), I suggest changing this line in the documentation to
SESSION_COOKIE_DOMAIN = 'mydomain.com'
To take the first steps without errors

Public schema admin cant be accessed

I have correctly set up the system, but whenever I tried to login on my public schema admin page it constantly redirects me back to the login page.
How can I use the admin multi site to solve this issue, cos it was mentioned in the doc that one can only use admin with admin multisite.

Register a user from inside a tenant

Why not register a user from a tenant?
https://github.com/Corvia/django-tenant-users/blob/master/tenant_users/tenants/models.py#L200
In addition to normal registration I want to offer a registration from inside the tenant like to mytenant.example.com/register.
I understand that you need to create permissions in public and in tenant, what is the way to go here?

I started by overriding the create_user for something like this:
def create_user (self, email = None, password = None, is_staff = False, current_tenant, ** extra_fields)
But then I would have to assign the public and tenant group permissions in the same view and it is a little messy.
Another option is to create the public permissions with the normal create_user, and if you get a current_tenant different than "public", create the tenant permissions in a post_save.
Can you guide me which way to go ?, it is probably a good improvement for tenant to have their own user registry

where is the field is_staff?

I do not understand where the field is_staff in the configured user, or do I have to add it to my CustomUser manually?
in:

users/models.py

from tenant_users.tenants.models import UserProfile

class TenantUser(UserProfile):
    name = models.CharField(
        _("Name"),
        max_length = 100,
        blank = True,
    )

UserProfile does not have the field is_staff

https://github.com/Corvia/django-tenant-users/blob/master/tenant_users/tenants/models.py
class UserProfile(AbstractBaseUser, PermissionsMixinFacade):

UserProfile inherits from AbstractBaseUser but it does not have the is_staff field either (AbstractUser yes)

https://github.com/django/django/blob/master/django/contrib/auth/base_user.py#L47

So, what am I missing?, where is the field is_staff? or maybe I need to add it manually to my CustomUser?
I'm a little lost at this point

Django Tenants - Profile ID already exists

Hi! I'm trying to use django-tenants with django-tenant-users, everything with configuration is ok, but when I try to provide a tenant to user, show an integrity error, says to me that profile_id = pk already exists...

Error in apps that try to use user's groups attribute

Django's built-in user class has an attribute called groups that is a many-to-many field to the Group model that some apps use for user permissions. After switching my user model over to UserProfile as described in setup, apps that access groups break. I get this error:
django.core.exceptions.FieldError: Unknown field(s) (groups) specified for TenantUser
TenantUser is my user model name. I already tried dropping the database and uninstalling and reinstalling everything.
I think I am misunderstanding how to set up django-tenant-users. How do I fix this? Thanks!

Unable to access Admin Page

HI,

I am running the project in local machine and after setting up django-tenants and django-tenant-users, the admin page is not showing up, syaing page not found. urls.py is below:
from django.contrib import admin
from django.urls import path

urlpatterns = [
path('^admin/', admin.site.urls),
]

I am trying to access it by: http://0.0.0.0:8000/admin

getting error:

=====

Request Method: GET
http://localhost:8000/

No tenant for hostname "localhost"

Is there anything specific I need to do in settings or URLs or anywhere ?

The help would be highly appreciated.

Thanks
Amin
[email protected]

@Viatrak @mezitax

Lots of SQL Queries

This is a huge bottle neck to such an amazing plugin. The problem is that when I access my django admin there are atleast 400 SQL queries executed on every click and most of them being permission and user related stuff.
Please fix this issue, I have attached a screenshot of DjDt below.

Screenshot from 2020-12-17 00-20-44

the use of UserProfile is not DRY

My customuser inherits from AbstractUser, this has the username, lastname and email fields.
For the verification of emails, I use the function of django-allauth that is cleaner and more orderly.
If my customuser inherits from UserProfile, I would have to create these fields manually and consider it a "dirty" change.
Is it possible to inherit UserPorfile from some mixin that does not overwrite these fields and only change its behavior ?, maybe a proxi model?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.