django-tenants / django-tenants

Django tenants using PostgreSQL Schemas
MIT License
1.48k stars 338 forks source link

Can't Delete Record from a shared model that has FK in tenant from public schema #962

Open Elabbasy00 opened 1 year ago

Elabbasy00 commented 1 year ago

I Have a custom user model shared between tenants and public


HAS_MULTI_TYPE_TENANTS = True
MULTI_TYPE_DATABASE_FIELD = 'type'

TENANT_TYPES = {
    "public": {
        "APPS": [
            'django_tenants',
            'clients',
            'django.contrib.admin',
            'django.contrib.auth',
            'django.contrib.contenttypes',
            'django.contrib.sessions',
            'django.contrib.messages',
            'django.contrib.staticfiles',
            'rest_framework',
            'rest_framework.authtoken',
            'djoser',
            "corsheaders",

            'accounts',

        ],
        "URLCONF": "server.urls_public",
    },
    "menu": {
        "APPS": [
            'django.contrib.admin',
            'django.contrib.auth',
            'django.contrib.contenttypes',
            'django.contrib.sessions',
            'django.contrib.messages',
            'django.contrib.staticfiles',
            'rest_framework.authtoken',

            'accounts',
            'products',
        ],
        "URLCONF": "server.urls_menu",
    },
    "full-version": {
        "APPS": [
            'django.contrib.admin',
            'django.contrib.auth',
            'django.contrib.contenttypes',
            'django.contrib.sessions',
            'django.contrib.messages',
            'django.contrib.staticfiles',
            'rest_framework.authtoken',

            'accounts',
            'products',
            'order',

        ],
        "URLCONF": "server.urls_full",
    }
}

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

from the full-version order app, there is an FK to the user model that restrict me to delete any user from the public schema

 class Address(models.Model):
    user = models.ForeignKey(
        User, on_delete=models.SET_NULL, null=True, blank=True)

also, the issue appears in the menu type cuz the order models do not exist yet

  django.db.utils.ProgrammingError: relation "order_address" does not exist
  LINE 1: ...."user_type", "accounts_user"."phone_number" FROM "order_add...

any work around this issue?

Abdalla991 commented 1 year ago

I have the same issue, any solutions?

Elabbasy00 commented 1 year ago

I have the same issue, any solutions?

I have add the same apps to all tenants type and restrict access on urls

psunny28 commented 9 months ago

I have a work around

from django.db import connection
from django.db import models
from django.db.models.base import ModelBase
from django_tenants.utils import get_public_schema_name, get_tenant_types

class DjangoTenantSafeDeleteMetadata(ModelBase):
    def __new__(cls, name, bases, attrs, **kwargs):
        new_class = super().__new__(cls, name, bases, attrs, **kwargs)

        original_get_fields = new_class._meta.get_fields

        def new_get_field(self, *args, **kwargs):
            fields = original_get_fields(*args, **kwargs)
            schema_name = connection.schema_name
            if settings.HAS_MULTI_TYPE_TENANTS:
                apps = set(get_tenant_types().get(connection.schema_name, {}).get("APPS", []))
            else:
                apps = set(settings.SHARED_APPS if schema_name == get_public_schema_name() else settings.TENANT_APPS)

            return [f for f in fields if not f.related_model or f.related_model._meta.app_config.name in apps]

        new_class._meta.get_fields = new_get_field.__get__(new_class._meta)
        return new_class

class ModelMixin(models.Model, metaclass=DjangoTenantSafeDeleteMetadata):
    class Meta:
        abstract = True

class Order(ModelMixin):
    # your fields here
    pass

Try this out it works. I had the same problem I used this as work around.

Elabbasy00 commented 9 months ago

Should I add the model mixin to every model that has a foreign key to any other model not in his scheme?