citusdata / django-multitenant

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

Issue when running migration #36

Closed divick closed 4 years ago

divick commented 5 years ago

I am using Django-2.1 with last release of django-multitenant and I am writing a migration on a model which inherits from TenantModel. But when I run the migration, I get error that

AttributeError: type object 'Shipment' has no attribute 'tenant_id'

The model that I have is:

class Fulfillment(TenantModel):
  ....
  shipment = TenantForeignKey('Shipment', on_delete=models.CASCADE)
  tenant_id = 'store_id'

  class Meta:
       unique_together = ('id', 'store')

But if I set the tenant_id = 'store_id' explicitly in migration, it works fine. I am not sure why is it complaining and what is the right way to fix this. Any help is appreciated.

louiseGrandjonc commented 5 years ago

Hello,

I have a few questions:

Thank you

divick commented 5 years ago

@louiseGrandjonc. Thanks for your response. Please find questions to your answers below:

  • When you say you are using the latest release, is it the one on pypi? We haven't released on pypi the current master.

No I meant the actual release not what is on the master branch.

  • Is the Shipment table distributed?

You mean in split across shards? No it is a single database for now.

  • What did you add in the migration exactly?

My migration looks something like this:

from django.db import migrations

def set_subscription_on_fulfillment(apps, schema_editor):
    """
    Sets the subscription on fulfillment
    """
    Customer = apps.get_model('cratejoy', 'Customer')
    Fulfillment = apps.get_model('cratejoy', 'Fulfillment')
    Shipment = apps.get_model('cratejoy', 'Shipment')
    Subscription = apps.get_model('cratejoy', 'Subscription')
    Customer.tenant_id = 'store_id'
    Fulfillment.tenant_id = 'store_id'
    Shipment.tenant_id = 'store_id'
    Subscription.tenant_id = 'store_id'

    for fulfillment in Fulfillment.objects.all():
        if not fulfillment.subscription:
            fulfillment.subscription = fulfillment.order.subscriptions.last()
            fulfillment.save()

class Migration(migrations.Migration):

    dependencies = [
        ('cratejoy', '0032_fulfillment_subscription'),
    ]

    operations = [
        migrations.RunPython(set_subscription_on_fulfillment),
    ]

If I don't set model.tenant_id = 'store_id', it fails with the error that I mentioned in my original post.

louiseGrandjonc commented 4 years ago

Hi @divick,

sorry for the delay. I actually ran into a similar issue when creating a new feature.

The way django works, the models are initialized after running Migrations, during migrations, you have access to a fake model with only the models.fields, which explains why tenant_id is not accessible, also no functions or @attribute are accessible in the migration fake model

To be able to access the tenant_id, here was my solution: https://github.com/citusdata/django-multitenant/blob/master/django_multitenant/db/migrations/distribute.py#L31

It might be a bit overkill with what you have to do :)