5

Running a database migration with RunPython on a second database fails

python3 manage.py migrate --database=app

The problem is that the apps.get_model method takes the default database which has already the newest migrations.

Does not work:

def copy_cpr_cents_to_euros(apps, schema_editor):
    User = apps.get_model('accounting', 'User')
    User.objects.filter(...);

Works:

def copy_cpr_cents_to_euros(apps, schema_editor):
    User = apps.get_model('accounting', 'User')
    User.objects.using('app').filter(...);

Is there a way to use the given database in the migration, so in this case "app" without making expliclitly declaring it, since it should work for both databases?

So something like:

User.objects.using(database_name).filter(...)
kadir
  • 1,417
  • 11
  • 35

2 Answers2

6
schema_editor.connection.alias

contains the string of the current database with which the migration was started.

So each RunPython-migration must use this alias to manually select the right db.

Example:

def copy_cpr_cents_to_euros(apps, schema_editor):
    User = apps.get_model('accounting', 'User')
    db = schema_editor.connection.alias
    User.objects.using('app').using(db).filter(...)
nickromano
  • 918
  • 8
  • 16
kadir
  • 1,417
  • 11
  • 35
  • 1
    This answer resolves the issue... but it's still incredible that the DB used when doing migration.RunPython is another one than the specified. – Llorenç Pujol Ferriol Jun 09 '22 at 13:16
  • @LlorençPujolFerriol I agree it's very unfortunate behaviour. I even believed it to be [a bug](https://code.djangoproject.com/ticket/34009), but apparently it's [expected behaviour](https://docs.djangoproject.com/en/4.1/ref/migration-operations/#runpython) (see the warning towards the bottom of that section). – marcelm Sep 15 '22 at 10:00
0

Decorators which can be used for RunPython function to specify against which DB it should be executed [Tested on Django 1.8]

def only_default_db_migration(func):
    return only_databases_migration('default')(func)


def only_databases_migration(*db_aliases):
    """Restrict running Data Migrations on wanted databases only"""
    def decorate(func):
        def run_python_func(apps, schema_editor):
            db_alias = schema_editor.connection.alias
            if db_alias in db_aliases:
                return func(apps, schema_editor)
            else:
                print(f'Skip RunPython {func.__name__!r} for db with alias {db_alias!r}')
        return run_python_func
    return decorate

Usage of only_default_db_migration


@only_default_db_migration
def migrate_spam_check_processed_at(apps, schema_editor):
    apps.get_model("queues","Queue").objects.all().update(check=F('created'))


class Migration(migrations.Migration):

    operations = [
        migrations.RunPython(migrate_spam_check_processed_at),
    ]
pymen
  • 5,737
  • 44
  • 35
  • That decorator looks nice, but it doesn't actually seem to solve the problem the OP was having (which is that the queries are not automatically run on the DB in `schema_editor.connection.alias`). – marcelm Sep 15 '22 at 09:45
  • @marcelm, pls have a look at my another answer for specific db migration https://stackoverflow.com/a/73761017/1731460 – pymen Sep 18 '22 at 08:01