Coder Social home page Coder Social logo

lorinkoz / django-pgschemas Goto Github PK

View Code? Open in Web Editor NEW
108.0 108.0 18.0 946 KB

Django multi-tenancy through Postgres schemas

Home Page: https://django-pgschemas.readthedocs.io/

License: MIT License

Python 59.11% Makefile 0.15% PLpgSQL 40.74%
django multi-tenancy postgres python

django-pgschemas's People

Contributors

dependabot-preview[bot] avatar dependabot[bot] avatar kartstig avatar ktowen avatar lorinkoz avatar randlet 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

django-pgschemas's Issues

Support Django 4.1

Bumping to Django 4.1 is currently not possible as

django-pgschemas 0.9.0 depends on django<4.1 and >=3.1

subfolder approach won't work for me

I can get the subdomain approach to work but am having issues with the subfolder approach.

Using subdomain approach...
When I go to client1.localhost:8000 it takes me to the login screen for my app... http://client1.localhost:8000/accounts/login/?next=/
This works as expected.

Using subfolder approach...
When I go to clients.localhost:8000/client1/ it takes me to... http://clients.localhost:8000/accounts/login/?next=/client1/
This takes me to the "page not found error" and this message... No tenant for hostname 'clients.localhost'

How can I get the subfolder approach to work?

Any problems including ALL INSTALLED_APPS in public/ default?

As a newcomer to multitenancy in general and django-pgschemas in particular, is there any harm in including ALL of my INSTALLED_APPS in both/ either of the public or default tenants? I can see potential performance issues with having every table replicated inside all schemas, but I think this would only affect dynamic tenants? Put another way, how do I decide which apps should be "tenantized" ?

edit: also, how do I decide which apps go in public, and which in default?

Dynamically created urlconf not properly incorporating all definitions from original

I'm using a single domain with tenant folders. Ex. app.domain.com/tenant1, app.domain.com/tenant2, etc.

I use this https://docs.djangoproject.com/en/2.2/ref/urls/#handler404 to have a different 404 view.

After setting up django-pgschemas, I've noticed that handler404 works when I visit app.domain.com/asdadas, but if I get a 404 response with tenant folder, the handler404 view is not used. For example, when I visit app.domain.com/tenant1/asdasdas, it shows the default page not found error.

Please advice.

What to choose? django-tenant-schemas | django-tenants | django-pgschemas

Hi,

I post this as an issue because, as a newbie, I really think there is an issue to choose the right library for postgres multitenant schemas in Django. Django-tenant-schemas, Django-tenants, django-pgschemas... What to choose and why?

I love the features of django-pgschemas, it would be so cool to see them as options in django-tenants ( or as part of f.ex. a django-tenants 2.0 )

It is really amazing that people like you (@lorinkoz) , @tomturner and all contributors are sharing their work with the world. Big thanks for that! Yet, it could be so much more powerful if forces would be joined to create one project in stead of forks of forks, etc.

I just wanted to share, I know there is probably a reason for everything so, no offence.
Keep up the good work!

How to only show tenant models in admin for public (non-tenant) user?

Apologies if this is answered elsewhere but I've scoured the django-tenants issues list as well as here and stack overflow and turned up nothing.

What I want is to have a public admin user that has permissions only to modify tenants and domains, and then each tenant will have their own admin user for modifying their own data. I've been able to mostly solve this via a 'root' static tenant, but that inherits from default, meaning it gets all the apps/ models that are defined there.

TENANTS = {
    'public': {
        'APPS': [
            'django.contrib.contenttypes',
            'django.contrib.staticfiles',
            'django.contrib.auth',
            'django.contrib.admin',
            'django.contrib.sessions',
            #...
            'django_pgschemas',
            #...
            'myproj.accounts',
            'myproj.patrons',
        ],

        'TENANT_MODEL': 'patrons.Patron',
        'DOMAIN_MODEL': 'patrons.Domain',
    },

    'default': {
        'APPS': [
            'django.contrib.auth',
            'django.contrib.admin',
            'django.contrib.sessions',
            'django.contrib.staticfiles',
            'django.contrib.messages',

            'rest_framework.authtoken',

            'myproj.accounts', 
            'myproj.coolstuff',             # These should be available to all dynamic tenants
            'myproj.awesomethings',  # and editable in a tenant admin
        ],

        'URLCONF': ROOT_URLCONF,
    },

    'root': {
        'APPS': [
            'django.contrib.auth',
            'django.contrib.admin',
            'django.contrib.sessions',
            'django.contrib.staticfiles',
            'django.contrib.messages',

            'rest_framework.authtoken',

            'myproj.patrons',   # I only want the models here available to the root tenant; however coolstuff and awesomethings show up as well
        ],
        'DOMAINS': ['localhost'],
        'URLCONF': ROOT_URLCONF,
    }

}

This is working, but exposes the default apps to the root user, which I don't want, since only tenants will use coolstuff and awesomethings. It's almost like I need a dynamic default and a static default. Is there any way to achieve what I want (without resorting to a lot of custom admin code) ?

Custom Apps dont get migrated

Following the docs, and implemented the TENANTS dict which i later on applied the python manage.py migrateschema -s public and python manage.py migrateschema respectively but the apps i created do not show in either schemas including the shared app which hinders me from creating a client with the django shell.

` TENANTS = {
"www": {

    "APPS": [
        'django.contrib.admin',
        "django.contrib.auth",
        "django.contrib.sessions",
        "rae_profile",
    ],
    "DOMAINS": ["raedarr.com"],
    "URLCONF": "rae_profile.urls",
},
"public": {
    "APPS": [
        'django.contrib.admin',
        "django.contrib.auth",
        'django.contrib.sessions',
        "django.contrib.contenttypes",
        "django.contrib.staticfiles",
        "django_pgschemas",
        "pwa",
        "shared_app"
    ],
    "TENANT_MODEL": "shared_app.Client",
    "DOMAIN_MODEL": "shared_app.Domain",
},
"default": {
    "APPS": [
        'django.contrib.admin',
        "django.contrib.auth",
        "django.contrib.sessions",
        'django.contrib.sites',
        "post_office",
        'hr.apps.HrConfig',
        "chat.apps.ChatConfig",
        'crm',
        'payroll.apps.PayrollConfig',
        'invoice.apps.InvoiceConfig',
        'benefits.apps.BenefitsConfig',
        'transport.apps.TransportConfig',
        'leave.apps.LeaveConfig',
        'estate.apps.EstateConfig',
        'attendance.apps.AttendanceConfig',
        'revenue.apps.RevenueConfig',
        # 'allauth',
        # 'allauth.account',
        # 'allauth.socialaccount',
        # 'allauth.socialaccount.providers.asana',
        # 'allauth.socialaccount.providers.google',
        # 'allauth.socialaccount.providers.linkedin',
        # 'allauth.socialaccount.providers.linkedin_oauth2',
        # ...
    ],
    "URLCONF": "rae_profile.urls",
}

}`

Thanks

createrefschema error

I'm getting this error when running createrefschema. The error message doesn't make any sense to me. What names are clashing?

`(venv) C:\code\fin2>manage.py createrefschema
SystemCheckError: System check identified some issues:

CRITICALS:
?: (pgschemas.W004) Name clash found between static and dynamic tenants: {'public'}`

My settings looks like this...
`TENANTS = {
"public": { # always treated as a shared schema. Cannot be routed directly.
"APPS": [
"django.contrib.contenttypes",
"django.contrib.staticfiles",
"django_pgschemas",
'customers',
],
},
"default": { # represents all dynamic tenants
"TENANT_MODEL": "customers.Client",
"DOMAIN_MODEL": "customers.Domain",
"APPS": [
"django.contrib.auth",
"django.contrib.sessions",
"fin.apps.FinConfig",
"users.apps.UsersConfig",
'django_plotly_dash',
'constance.backends.database',
'constance',
'django.contrib.admin',
'django.contrib.messages',
'django.contrib.staticfiles',
'fontawesomefree',
'bootstrap_modal_forms',
'widget_tweaks',
'crispy_forms',
'flatpickr',
'bootstrap4',
'django_extensions',
'rest_framework',
],
"URLCONF": "mysite.urls",
"CLONE_REFERENCE": "refschema",
},
}

INSTALLED_APPS = []
for schema in TENANTS:
INSTALLED_APPS += [app for app in TENANTS[schema]["APPS"] if app not in INSTALLED_APPS]

ROOT_URLCONF = TENANTS["default"]["URLCONF"]`

Incorporate new features from django-tenants

Review and incorporate these:

The Django admin customizations are still out of the scope here.
The modification of the TenantClient.login feels out of scope, since this comes from a Django decision itself, it seems that this customization would be more appropriate to be carried out as part of test mocking.

Protect SchemaDescriptor.is_dynamic

In some contexts where querying database would mean additional time cost (management commands,) we're using raw schema descriptors to describe dynamic tenants. This breaks the contract and is currently failing silently. Let's protect this variable and raise an exception when accessed unsafely.

Tweak in --executor management command argument

In real world projects, the executor-centered strategy is not completely future-proof, so I don't expect a lot of activity in this front. Let's change the --executor [sequential|parallel] into just --parallel for convenience.

Integrate basic sharding

Is it possible to control schemas in more than one database with a smart routing?

Current challenges:

  • Active schema can no longer be tracked in the connection object.
  • Management commands now require a database parameter that may conflict in some cases with the existing commands.
  • Duplication of content types across databases, requires to review the caching behavior (although it seems multi-database friendly)
  • Requires API decision on how to map dynamic tenants with their database (could be a database field or a model method)
  • Requires API decision on how to specify the databases that can be used for dynamic tenants (public and CLONE_REFERENCE must always be on every database)

createrefschema does not carry over column_default values

I created a template schema with createrefschema. I then created a new tenant off that. When I attempted to make a new migration for my project and migrate, it fails when it gets to the new tenant with
django.db.utils.IntegrityError: duplicate key value violates unique constraint "django_migrations_pkey" DETAIL: Key (id)=(1) already exists.

Investigating it I can see that the column defaults aren't carried over in Postgres.

Use psycopg2-binary instead of psycopg2 or make it an optional dependency

In cerrtains Macs building the psycopg2 package throws errors and it is easier to manage with the binary

It'd be great if we could choose between psycopg2 or psycopg2-binary at the project level or at least choose to ignore the psycopg2 dependency. (poetry does not seem to have any docs on sub-dependency override)

Execution plan for management commands

Sometimes you want to run a command on all dynamic schemas, and for some reason the command halts before finishing. You don't want to start over again, how to "resume execution" and only run it on the missing schemas?

Is there any standard for storing execution plans? Any standard for the arguments one must use to play along with an execution plan?

How to get current schema/ tenant?

I have a number of celery tasks that (will) operate on individual tenants. I can write a wrapper to loop over these tasks, or use a request object to obtain the current tenant, but I'm curious about a general method for obtaining the contextually current tenant. This is especially useful for running such tasks from a management command - something like

./manage.py runschema my_command -s schema1  # starts, e.g., tasks.my_task.delay() to run within the celery process

my_command will fire off a task, but the task will of course run in a different process than the management command; so passing the tenant ID or schema name to the task will be required. How do I obtain the current tenant within my_command? I thought it would be on the connection but doesn't seem to be available there. I'm looking for something like this:

SELECT current_schema();

(from https://www.postgresqltutorial.com/postgresql-schema/)

Honor TENANT_MODEL.Meta.ordering on commands

The _get_schemas_from_options function is using a set to store the possible tenants that will be affected by the command but it doesn't preserve the ordering when they are fetched from the database

Reverse seems to fail in some instances

I'm still digging into this, but it looks like when I use LoginRequiredMixin, the reverse for the login url returns incorrect value under certain circumstances. Here's the background:

settings.py

LOGIN_URL = "user_login"  # or whatever your login url name might be

urls.py (simplified)

/home/ goes to a simple TemplateView
/about/ goes to the same TemplateView, with LoginRequiredMixin
/accounts/ goes to the accounts app urls

accounts/urls.py

/login/ goes to the login authentication view, url name: "user_login"

I have set up two tenants.

Client1

domain: app.mydomain.com
folder: client1
schema_name: client1

Client2

domain: app.mydomain.com
folder: client2
schema_name: client2

If I start the server and go any view for Client1 (app.mydomain.com/client1/home/ or app.mydomain.com/client1/about/), all works well.

Now, if I go to home for Client2 (app.mydomain.com/client2/home/), no problem.

But as soon as I go to a view with LoginRequiredMixin (app.mydomain.com/client2/about/), I get problems. Instead of going to app.mydomain.com/client2/accounts/login/ I'm getting redirects to app.mydomain.com/client1/accounts/login/

This same thing happens in reverse if I start the server, go to a Client2 url, and then try to go to a Client1 url that uses LoginRequiredMixin. (goes to app.mydomain.com/client2/accounts/login/ rather than the expected app.mydomain.com/client1/accounts/login/)

I verified by copying LoginRequiredMixin to my own project and doing something like this within the get_login_url method (I know, debugging by print statement is evil, but it's easy to replicate):

def get_login_url(self):
    """
    Override this method to override the login_url attribute.
    """
    login_url = self.login_url or settings.LOGIN_URL
    print("get_login_url login_url: ", str(login_url))
    print("get_login_url reverse(login_url): ", reverse(str(login_url)))
    print("get_login_url schema_name: ", connection.schema.schema_name)

    if not login_url:
        raise ImproperlyConfigured(
            "{0} is missing the login_url attribute. Define {0}.login_url, settings.LOGIN_URL, or override "
            "{0}.get_login_url().".format(self.__class__.__name__)
        )
    return str(login_url)

Result (after going to a url for the second client from the initial example above):

get_login_url login_url: user_login
get_login_url reverse(login_url): /client1/accounts/login/
get_login_url schema_name: client2

Any recommendations? Anyone else experiencing this?

permission denied

running ...

"...makamigrations" and "./manage.py migrate -s public" runs successfully, but
"./manage.py migrate" produces the following:
"django.db.utils.ProgrammingError: permission denied for database webdb"

partial Traceback (most recent call last):
File "/var/www/vhosts/website/.pyenv/versions/3.9.1/lib/python3.9/site-packages/django/db/backends/utils.py", line 82, in _execute
return self.cursor.execute(sql)
psycopg2.errors.InsufficientPrivilege: permission denied for database webdb

Also, I don't see the additional static schemas, just 'public'

thanks in advance

Running tests does not use REFERENCE_SCHEMA in tenant creation

I'm finally getting around to putting some Django tests. I am successfully using a REFERENCE_SCHEMA in my project but I am not seeing it clone when it running with manage.py test --keepdb. My expectation is that the test schema/tenant would be using my clone schema. It runs the regular migrations route instead. I visited the docs but I couldn't find anything to help resolve this. Could this be a misconfiguration on my part?

Querying tenants in commands with pending migrations

When the tenant/domain model themselves have pending migrations, the query for them performed inside management commands is exploding. One variant is to run the migrations in the public schema explicitly first, but I think we can catch this exception and provide a helpful message to the user.

`DROP FUNCTION IF EXISTS` drops function in another schema too

How to reproduce

A db with two schemas:

  • public
  • reference_tenant

Setup the following migration script

from django.db import migrations
from django.db.migrations import RunPython

create = """
DROP FUNCTION IF EXISTS reproduce_error();
CREATE OR REPLACE FUNCTION reproduce_error()
    RETURNS TEXT AS
$func$
BEGIN
    RETURN (select version());
END
$func$ LANGUAGE plpgsql;
"""

delete = "DROP FUNCTION IF EXISTS reproduce_error();"


def forwards_func(_, schema_editor):
    schema_editor.execute(create, params=None)


def reverse_func(_, schema_editor):
    schema_editor.execute(delete, params=None)


class Migration(migrations.Migration):
    dependencies = [
        ("business", <former_script>),
    ]

    operations = [RunPython(forwards_func, reverse_func)]
python manage.py migrate business --schema public

The reproduce_error function is in the public schema.

python manage.py migrate business --schema reference_tenant

The reproduce_error function:

  • is in the reference_tenant schema,
  • is not in the public schema anymore.

Expected behavior

The reproduce_error function:

  • is in the reference_tenant schema,
  • is in the public schema.

Additional info

By removing DROP FUNCTION IF EXISTS reproduce_error(); in the create above, the issue is not reproduced.

(In case further interactive discussions will be necessary, the problem was written at https://gitter.im/django-pgschemas/community too)

Refactoring for v0.99

Refactor

  • Use contextvars instead of asgiref.Local
  • Move domain-related handling to routing module.
  • Make domain-related handling optional, together with all the related TENANTS settings.
  • Use TenantModel.routing
  • Implement middleware for session routing and headers routing
  • Move to pytest !!
  • Level up the typing game

more to come...

Migration executing in "public" tenant despite not being in APPS

Hi there!

I am in the process of lifting a py3.7/ django 3.2/ pgschemas 0.4.3 project up to py3.10/ django 4.1/ pgschemas 0.10.0 .

In the process, django wanted to create a bunch of new migrations for each app to update ID fields. No problem, I thought, except I'm now receiving the following error:

bash-5.1# ./manage.py migrate -s public
[sequential:public] Operations to perform:
[sequential:public]   Apply all migrations: accounts, admin, auth, authtoken, foo, contenttypes, django_celery_beat, bar, baz, boz, sessions, frob
[sequential:public] Running migrations:
[sequential:public]   Applying accounts.0003_auto_20220822_1143...Traceback (most recent call last):
  File "/usr/local/lib/python3.10/site-packages/django/db/backends/utils.py", line 89, in _execute
    return self.cursor.execute(sql, params)
psycopg2.errors.UndefinedTable: relation "authtoken_token" does not exist

This is odd because the app that defines that model is not included in the public schema:

TENANTS = {
        'public': {
                'APPS': [
                        'django.contrib.contenttypes',
                        'django.contrib.staticfiles',
                        'django.contrib.gis',
                        'django.contrib.auth',
                        'django.contrib.admin',
                        'django.contrib.sessions',

                       ...,

                        'rest_framework',
                        'rest_framework_gis',
#                        'rest_framework.authtoken',   see I am commented out

                       ...
                ],

                'TENANT_MODEL': 'patrons.Patron',
                'DOMAIN_MODEL': 'patrons.Domain',
        },

If I run manage.py migrate -s <my-tenant> everything works ok. Another odd part of all this is that authtoken_token isn't even mentioned in any accounts migrations, but evidently it defined its own ID-update migration which is where the error originates. So I'm not sure why it is getting reported by my migration.

Any ideas what I might be doing wrong? I am considering creating an interim release which includes rest_framework.authtoken as a separate migration step, but before going down that path I'm wondering if there's a better, cleaner approach.

Thanks in advance!

What is the correct set up for a universal admin, www and then all the tenants?

I've been playing around with this for a while and what I want to accomplish is multitenancy where each tenant can access their own admin. I've gotten as far as actually getting /admin to work but I can't create an admin user for the tenant. How is this done?

Also, what is the best way to set up the main application where I can manage the customers / tenants?

Here is my settings.py configuration:

TENANTS = {
    # ...
    "www": {
        "APPS": [
            "django.contrib.auth",
            "django.contrib.sessions",
            "django.contrib.admin",
            'django.contrib.messages',
            "django.contrib.staticfiles",
            # ...
            "website",
        ],
        "DOMAINS": ["localhost"],
        "URLCONF": "website.urls",
    },
    # ...
    "public": {
        "APPS": [
            "django.contrib.contenttypes",
            "django.contrib.staticfiles",
            # ...
            "django_pgschemas",
            "customers",
            # ...
        ],
    },
    # ...
    "default": {
        "TENANT_MODEL": "customers.Client",
        "DOMAIN_MODEL": "customers.Domain",
        "APPS": [
            "django.contrib.auth",
            "django.contrib.sessions",
            "django.contrib.admin",
            'django.contrib.messages',
            'django.contrib.staticfiles',
            # ...
            "frontend",
            "accounts",
            # ...
        ],
        "URLCONF": "frontend.urls",
    }
}

Whenever I try to createsuper on for the base, I get this:
django.db.utils.ProgrammingError: relation "auth_user" does not exist LINE 1: ...user"."is_active", "auth_user"."date_joined" FROM "auth_user...

I'm in dev and just experimenting so if I have to start over, I'm fine with it.

post_save signal does not execute

I was able to get django_pgschemas working nicely with no issues. However I noticed that since incorporating django-pgschemas into my project my post_save and post_delete signals no longer work. Are signals supported with django-pgschemas?

Here is my signals.py file:

from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver
from fin.fin_helpers import adjust_account_tran_balance

from fin.models import AccountTran, InvestAccount

@receiver(post_save, sender=AccountTran)
def save_account_tran(instance, **kwargs):
    print('--------------- in signal post_save ---------------')
    adjust_account_tran_balance(instance.account_id, instance.odate)

Support for django 4.0

Hi! In the process of moving some of my apps over to django 4, and it appears the latest version supported by pgschemas is 3.3. Any chance of a release which supports django 4 any time soon?

Logs with schema_name doesn't work for django logs

Discussed in #121

Originally posted by userjlegwork September 2, 2022
Using the function to retrieve the schema_name (from django_pgschemas.schema import get_current_schema) works great when called directly, but for django requests logs always prints "public". @lorinkoz any thoughs on this?

pgschemas not working with postgis backend

django==3.2.11
django-pgschemas==0.8.0
PGSCHEMAS_ORIGINAL_BACKEND=django.contrib.gis.db.backends.postgis

Observed:

./manage.py
Traceback (most recent call last):
  File "/opt/app/./manage.py", line 21, in <module>
    main()
  File "/opt/app/./manage.py", line 17, in main
    execute_from_command_line(sys.argv)
  File "/usr/lib/python3.9/site-packages/django/core/management/__init__.py", line 419, in execute_from_command_line
    utility.execute()
  File "/usr/lib/python3.9/site-packages/django/core/management/__init__.py", line 395, in execute
    django.setup()
  File "/usr/lib/python3.9/site-packages/django/__init__.py", line 24, in setup
    apps.populate(settings.INSTALLED_APPS)
  File "/usr/lib/python3.9/site-packages/django/apps/registry.py", line 114, in populate
    app_config.import_models()
  File "/usr/lib/python3.9/site-packages/django/apps/config.py", line 301, in import_models
    self.models_module = import_module(models_module_name)
  File "/usr/lib/python3.9/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
  File "<frozen importlib._bootstrap>", line 986, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 680, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 850, in exec_module
  File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed
  File "/usr/lib/python3.9/site-packages/django/contrib/auth/models.py", line 3, in <module>
    from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager
  File "/usr/lib/python3.9/site-packages/django/contrib/auth/base_user.py", line 48, in <module>
    class AbstractBaseUser(models.Model):
  File "/usr/lib/python3.9/site-packages/django/db/models/base.py", line 122, in __new__
    new_class.add_to_class('_meta', Options(meta, app_label))
  File "/usr/lib/python3.9/site-packages/django/db/models/base.py", line 326, in add_to_class
    value.contribute_to_class(cls, name)
  File "/usr/lib/python3.9/site-packages/django/db/models/options.py", line 207, in contribute_to_class
    self.db_table = truncate_name(self.db_table, connection.ops.max_name_length())
  File "/usr/lib/python3.9/site-packages/django/utils/connection.py", line 15, in __getattr__
    return getattr(self._connections[self._alias], item)
  File "/usr/lib/python3.9/site-packages/django/utils/connection.py", line 62, in __getitem__
    conn = self.create_connection(alias)
  File "/usr/lib/python3.9/site-packages/django/db/utils.py", line 204, in create_connection
    backend = load_backend(db['ENGINE'])
  File "/usr/lib/python3.9/site-packages/django/db/utils.py", line 111, in load_backend
    return import_module('%s.base' % backend_name)
  File "/usr/lib/python3.9/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "/usr/lib/python3.9/site-packages/django_pgschemas/postgresql_backend/base.py", line 7, in <module>
    from .introspection import DatabaseSchemaIntrospection
  File "/usr/lib/python3.9/site-packages/django_pgschemas/postgresql_backend/introspection.py", line 8, in <module>
    class DatabaseSchemaIntrospection(original_backend.DatabaseIntrospection):  # pragma: no cover
AttributeError: module 'django.contrib.gis.db.backends.postgis.base' has no attribute 'DatabaseIntrospection'

Expected:

manage.py commands (and django in general) function correctly.

Scheduled/ periodic tasks with celery

I have a bunch of scheduled and periodic tasks that currently run under celery. No problem for a single-tenant architecture, but how do I tell celery to run these tasks when it only knows about the public schema? Couple of thoughts:

  1. Set up separate celery apps, one for each schema/ tenant. This seems broken by design since now I will have to add code, not just data, for each tenant.
  2. Somehow loop through each schema within the celery task processor. This will require writing custom scheduling code, so right off the bat doesn't seem like a good idea, and also.. there's nothing that marks a (postgres) schema as being dynamic, since that's a django-pgschemas thing. So.. cycling through the rows in my Tenants table? Still requires modifying the celery processor itself (celery beat will have to know about schemas).
  3. Run a separate celery and beat instance for each tenant. This breaks the whole purpose of multi-tenancy.

To summarize: I want celery beat and other tasks to look at their tables within each pg schema, not just public.

Anything I'm missing? It looks like django-tenants has a "schema_context" context processor but I don't see that in django-pgschemas.

Static tenants only

What if you don't need dynamic tenants at all, or just want to start with a couple of static tenants?

Include CLONE_REFERENCE in dynamic schema_name validation

You are not supposed to create a dynamic tenant whose schema_name is the clone reference. If the clone reference was added as an afterthought, and there is already a dynamic tenant with that name, we'll let that explode, I guess?

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.