citusdata / django-multitenant

Python/Django support for distributed multi-tenant databases like Postgres+Citus
MIT License
710 stars 116 forks source link

cannot create instance for subclass model of TenantModel #96

Closed pagezz-canway closed 1 year ago

pagezz-canway commented 3 years ago

python:3.6.6 django:2.2.6

models.py

from django.db import models
from django_multitenant.fields import TenantForeignKey
from django_multitenant.models import TenantModel

class Tenant(TenantModel):
    tenant_id = "id"
    name = models.CharField("tenant name", max_length=100)

class Business(TenantModel):
    tenant = models.ForeignKey(Tenant, blank=True, null=True, on_delete=models.SET_NULL)
    tenant_id = "tenant_id"
    bk_biz_id = models.IntegerField("business ID")
    bk_biz_name = models.CharField("business name", max_length=100)

class Template(TenantModel):
    tenant = models.ForeignKey(Tenant, blank=True, null=True, on_delete=models.SET_NULL)
    business = TenantForeignKey(Business, blank=True, null=True, on_delete=models.SET_NULL)
    name = models.CharField("name", max_length=100)
    created_by = models.CharField("created by", max_length=100)

when I run

In [1]: from home_application.models import *

In [2]: t1 = Tenant.objects.get(id=1)

In [3]: biz1 = Business(tenant=t1, bk_biz_id=2, bk_biz_name="A")

raise error

---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
/Users/page/.pyenv/versions/3.6.6/envs/test_django_multitenant_py3.6.6/lib/python3.6/site-packages/django_multitenant/utils.py in get_tenant_field(model_class_or_instance)
     51     try:
---> 52         return next(field for field in all_fields if field.column == tenant_column)
     53     except StopIteration:

StopIteration: 

During handling of the above exception, another exception occurred:

ValueError                                Traceback (most recent call last)
<ipython-input-3-4a184dd85d2f> in <module>()
----> 1 biz1 = Business(tenant=t1, bk_biz_id=2, bk_biz_name="A")

/Users/page/.pyenv/versions/3.6.6/envs/test_django_multitenant_py3.6.6/lib/python3.6/site-packages/django_multitenant/mixins.py in __init__(self, *args, **kwargs)
     56             UpdateQuery.update_batch = wrap_update_batch(UpdateQuery.update_batch)
     57 
---> 58         super(TenantModelMixin, self).__init__(*args, **kwargs)
     59 
     60     def __setattr__(self, attrname, val):

/Users/page/.pyenv/versions/3.6.6/envs/test_django_multitenant_py3.6.6/lib/python3.6/site-packages/django/db/models/base.py in __init__(self, *args, **kwargs)
    484             else:
    485                 if val is not _DEFERRED:
--> 486                     _setattr(self, field.attname, val)
    487 
    488         if kwargs:

/Users/page/.pyenv/versions/3.6.6/envs/test_django_multitenant_py3.6.6/lib/python3.6/site-packages/django_multitenant/mixins.py in __setattr__(self, attrname, val)
     60     def __setattr__(self, attrname, val):
     61 
---> 62         if (attrname in (self.tenant_field, get_tenant_field(self).name)
     63             and not self._state.adding
     64             and val

/Users/page/.pyenv/versions/3.6.6/envs/test_django_multitenant_py3.6.6/lib/python3.6/site-packages/django_multitenant/utils.py in get_tenant_field(model_class_or_instance)
     53     except StopIteration:
     54         raise ValueError('No field found in {} with column name "{}"'.format(
---> 55                          model_class_or_instance, tenant_column))
     56 
     57 

ValueError: No field found in Business object (None) with column name "1"
gurkanindibay commented 1 year ago

Hi, tenant_id is reserved keyword. You should not use this field name as tenant_id. You can use as below

class Tenant(TenantModel):
    tenant_id = "id"
    name = models.CharField("tenant name", max_length=100)

class Business(TenantModel):
    ten = models.ForeignKey(Tenant, blank=True, null=True, on_delete=models.SET_NULL)
    tenant_id = "ten_id"
    bk_biz_id = models.IntegerField("business ID")
    bk_biz_name = models.CharField("business name", max_length=100)

    class Meta:
        constraints = [
            models.UniqueConstraint(fields=['id', 'ten_id'], name='unique_business_ten')
        ]

class Template(TenantModel):
    tenant_id = "ten_id"
    ten = models.ForeignKey(Tenant, blank=True, null=True, on_delete=models.SET_NULL)
    business = TenantForeignKey(Business, blank=True, null=True, on_delete=models.SET_NULL)
    name = models.CharField("name", max_length=100)
    created_by = models.CharField("created by", max_length=100)