Search code examples
pythondjangodjango-modelsdjango-rest-frameworkdjango-migrations

Django migration with 2 DBs?


I've the databases db1 and db2. The schemas of both DBs should be created with the same migration script.

The Django docs mention DATABASE_ROUTERS and RunPython, but I didn't manage to get it working so far. The function is called, but migrations.CreateModel() has no impact when called from the function: the table is not created.

Migration script:

# 0001_initial.py

def forwards(apps, schema_editor):
    if schema_editor.connection.alias == 'db1':
        print('This line is called!')
        migrations.CreateModel(
            name='MyModel',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                # ...
            ]
        )

class Migration(migrations.Migration):
    initial = True
    
    dependencies = [
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
    ]

    operations = [
        migrations.RunPython(forwards, hints={'target_db': 'db1'}),
    ]

Router:

DatabaseRouter:
    # ...
    def allow_migrate(self, db, app_label, model_name=None, **hints):
        if 'target_db' in hints:
            return db == hints['target_db']
        return True

Command:

python manage.py migrate --database=db1

Any idea? Thanks in advance!


Solution

  • migrations.CreateModel does not create a model in the database upon instantiation. It is supposed to be present in the operations list where the migrations system will then use it to create the model. Furthermore you shouldn't be writing this migration manually anyway! Just write the code for the model in models.py:

    class MyModel(models.Model):
        # Your fields, etc.
    

    Next since your question asks how to control which model is migrated to which database. These tasks are supposed to be done in the Database router, particularly in the allow_migrate method. You can check the values passed for db, app_label, and model_name return whether the model should be migrated there. For example:

    class MyRouter:
        def db_for_read(self, model, **hints):
            ...
    
        def db_for_write(self, model, **hints):
            ...
    
        def allow_relation(self, obj1, obj2, **hints):
            ...
    
        def allow_migrate(self, db, app_label, model_name=None, **hints):
            # Suppose I want to migrate all models from the app 'some_app' to 'db1' only.
            if app_label in ['some_app']:
                return db == 'db1'
            # Suppose I want to migrate 'MyModel' to 'db2' only.
            if model_name == 'mymodel':  # 'mymodel' is lowercase 'MyModel'
                return db == 'db2'
            return None