django-multitenant
django-multitenant copied to clipboard
Suport for TenantForeignKey as recursive Model
Suport for models like this:
class Task(TenantModel):
tenant_id = 'business_id'
business = models.ForeignKey(Business, on_delete = models.CASCADE)
sub_task = TenantForeignKey(
'self',
on_delete=models.CASCADE,
db_index=False,
blank=True,
null=True
)
Hello @josseed,
Thank you for using django-multitenant. I added tests on TenantForeignKey on self in this branch: https://github.com/citusdata/django-multitenant/tree/self-tenant-foreign-key.
As you can see, in the model Task
, I added a parent
field similar to yours. All the tests are passing. Could you give me details on your issue?
Thank you
I'm having some hard time with TenantForeignKey('self') too.
Here are examples of my models and migration.
# models
class AbstractBaseModel(models.Model):
"""
The Abstract Base Model holds the common attributes and methods
used for the project.
"""
id = models.UUIDField(primary_key=True, unique=True, default=uuid.uuid4, editable=False)
class Meta:
abstract = True
class TenantAbstractModel(AbstractBaseModel, TenantModel):
tenant_id = 'org_id'
org = models.ForeignKey(Organization, on_delete=models.PROTECT)
class Meta:
abstract = True
class OrgUnity(TenantAbstractModel):
"""
OrgUnity is a organizational unity, like branches, departments, tribes, etc.
Basically a grouping of resources, it can be related to other OrgUnity.
"""
name = models.CharField(max_length=255)
description = models.TextField(blank=True)
parent_unity = TenantForeignKey('self', on_delete=models.PROTECT, db_index=False, blank=True, null=True)
# migration
...
migrations.CreateModel(
name='OrgUnity',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)),
('name', models.CharField(max_length=255)),
('description', models.TextField(blank=True)),
('org', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='organization.Organization')),
('parent_unity', django_multitenant.fields.TenantForeignKey(blank=True, db_index=False, null=True, on_delete=django.db.models.deletion.PROTECT, to='organization.OrgUnity')),
],
options={
'abstract': False,
},
bases=(django_multitenant.mixins.TenantModelMixin, models.Model),
),
...
When applying the migration I'm getting the following exception.
Operations to perform:
Apply all migrations: admin, archive, auth, contenttypes, organization, sessions
Running migrations:
Applying organization.0001_initial...Traceback (most recent call last):
File "/Users/johnny/Library/Caches/pypoetry/virtualenvs/ihuman-platform-UyS-abtH-py3.8/lib/python3.8/site-packages/django/db/backends/utils.py", line 86, in _execute
return self.cursor.execute(sql, params)
psycopg2.errors.InvalidForeignKey: there is no unique constraint matching given keys for referenced table "organization_orgunity"
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 "/Users/johnny/Library/Caches/pypoetry/virtualenvs/ihuman-platform-UyS-abtH-py3.8/lib/python3.8/site-packages/django/core/management/__init__.py", line 401, in execute_from_command_line
utility.execute()
File "/Users/johnny/Library/Caches/pypoetry/virtualenvs/ihuman-platform-UyS-abtH-py3.8/lib/python3.8/site-packages/django/core/management/__init__.py", line 395, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/Users/johnny/Library/Caches/pypoetry/virtualenvs/ihuman-platform-UyS-abtH-py3.8/lib/python3.8/site-packages/django/core/management/base.py", line 328, in run_from_argv
self.execute(*args, **cmd_options)
File "/Users/johnny/Library/Caches/pypoetry/virtualenvs/ihuman-platform-UyS-abtH-py3.8/lib/python3.8/site-packages/django/core/management/base.py", line 369, in execute
output = self.handle(*args, **options)
File "/Users/johnny/Library/Caches/pypoetry/virtualenvs/ihuman-platform-UyS-abtH-py3.8/lib/python3.8/site-packages/django/core/management/base.py", line 83, in wrapped
res = handle_func(*args, **kwargs)
File "/Users/johnny/Library/Caches/pypoetry/virtualenvs/ihuman-platform-UyS-abtH-py3.8/lib/python3.8/site-packages/django/core/management/commands/migrate.py", line 231, in handle
post_migrate_state = executor.migrate(
File "/Users/johnny/Library/Caches/pypoetry/virtualenvs/ihuman-platform-UyS-abtH-py3.8/lib/python3.8/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 "/Users/johnny/Library/Caches/pypoetry/virtualenvs/ihuman-platform-UyS-abtH-py3.8/lib/python3.8/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 "/Users/johnny/Library/Caches/pypoetry/virtualenvs/ihuman-platform-UyS-abtH-py3.8/lib/python3.8/site-packages/django/db/migrations/executor.py", line 247, in apply_migration
migration_recorded = True
File "/Users/johnny/Library/Caches/pypoetry/virtualenvs/ihuman-platform-UyS-abtH-py3.8/lib/python3.8/site-packages/django/db/backends/base/schema.py", line 115, in __exit__
self.execute(sql)
File "/Users/johnny/Library/Caches/pypoetry/virtualenvs/ihuman-platform-UyS-abtH-py3.8/lib/python3.8/site-packages/django_multitenant/backends/postgresql/base.py", line 86, in execute
super(DatabaseSchemaEditor, self).execute(statement)
File "/Users/johnny/Library/Caches/pypoetry/virtualenvs/ihuman-platform-UyS-abtH-py3.8/lib/python3.8/site-packages/django/db/backends/base/schema.py", line 142, in execute
cursor.execute(sql, params)
File "/Users/johnny/Library/Caches/pypoetry/virtualenvs/ihuman-platform-UyS-abtH-py3.8/lib/python3.8/site-packages/django/db/backends/utils.py", line 100, in execute
return super().execute(sql, params)
File "/Users/johnny/Library/Caches/pypoetry/virtualenvs/ihuman-platform-UyS-abtH-py3.8/lib/python3.8/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/johnny/Library/Caches/pypoetry/virtualenvs/ihuman-platform-UyS-abtH-py3.8/lib/python3.8/site-packages/django/db/backends/utils.py", line 77, in _execute_with_wrappers
return executor(sql, params, many, context)
File "/Users/johnny/Library/Caches/pypoetry/virtualenvs/ihuman-platform-UyS-abtH-py3.8/lib/python3.8/site-packages/django/db/backends/utils.py", line 86, in _execute
return self.cursor.execute(sql, params)
File "/Users/johnny/Library/Caches/pypoetry/virtualenvs/ihuman-platform-UyS-abtH-py3.8/lib/python3.8/site-packages/django/db/utils.py", line 90, in __exit__
raise dj_exc_value.with_traceback(traceback) from exc_value
File "/Users/johnny/Library/Caches/pypoetry/virtualenvs/ihuman-platform-UyS-abtH-py3.8/lib/python3.8/site-packages/django/db/backends/utils.py", line 86, in _execute
return self.cursor.execute(sql, params)
django.db.utils.ProgrammingError: there is no unique constraint matching given keys for referenced table "organization_orgunity"
After some digging, I think I found a way around.
In order for the self foreign key to work the table must exist first. So, I separated it in two migrations, the first one to create the table without te self reference and the second to include it.
As I advance in the current project using django-multitenant it seems to be a problem with migrations resolution order. Every now and then I stumble on the same problem with different models and different use cases, the only solution is to delete all the migrations and recreate them from scratch including the new falling model.