Opened 5 years ago

Closed 5 years ago

Last modified 5 years ago

#30431 closed Bug (worksforme)

Migration hangs on non default database.

Reported by: mandm Owned by: nobody
Component: Migrations Version: dev
Severity: Normal Keywords: migrations, uuid, uuidfield, database router, multiple database
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by mandm)

I'm trying to add a UUIDField in an existing user model following this method (with unique=True & default=uuid.uuid4). https://docs.djangoproject.com/en/1.11/howto/writing-migrations/#migrations-that-add-unique-fields

This works with a single database but hangs in the following scenarios with multiple databases :

  • Hangs
    • python manage.py migrate --database=nondefault
    • also hangs with .using('nondefault') in the query in gen_uuid function
  • Works
    • python manage.py migrate
    • python manage.py migrate --database=default

It seems to always hang at this statement UserModel.objects.all() when a non-default database is used either via --database option in migrate or via .using('nondefault') in query.

Here's a snippet of the code I've used. I would be glad to provide additional information to reproduce the issue.

User model :

class CustomUser(AbstractUser):
    address = models.TextField()
    uniq_id = models.UUIDField(unique=True, default=uuid.uuid4) 

    class Meta:        
        db_table = 'customuser'

Migration file

def gen_uuid(apps, schema_editor):
    UserModel = apps.get_model('myapp', 'customuser')
    for row in UserModel.objects.all():     # .objects.using('nondefault').all(): -- HANGS ! 
        row.uniq_id = uuid.uuid4()
        row.save(update_fields=['uniq_id']) 

class Migration(migrations.Migration):
    dependencies = [
        ('myapp', '003_previous_migration'),
    ]

    operations = [
        migrations.AddField(
            model_name='customuser',
            name='uniq_id',
            field=models.UUIDField(default=uuid.uuid4, null=True),
        ),
        migrations.RunPython(gen_uuid, reverse_code=migrations.RunPython.noop),
        migrations.AlterField(
            model_name='customuser',
            name='uniq_id',
            field=models.UUIDField(default=uuid.uuid4, unique=True),
        ),
    ]

Change History (5)

comment:1 by mandm, 5 years ago

Description: modified (diff)

comment:2 by mandm, 5 years ago

Description: modified (diff)
Summary: Migration hangs when using UUIDField with unique default value & multiple databasesMigration hangs on non default database

comment:3 by mandm, 5 years ago

Description: modified (diff)

comment:4 by Mariusz Felisiak, 5 years ago

Resolution: worksforme
Status: newclosed
Summary: Migration hangs on non default databaseMigration hangs on non default database.
Version: 1.11master

Thanks for the report, but described case works for me.

First of all you should use schema_editor.connection.alias in gen_uuid() to fix it for multiple databases (see RunPython), .e.g.

def gen_uuid(apps, schema_editor):
    UserModel = apps.get_model('myapp', 'customuser')
    db_alias = schema_editor.connection.alias
    for row in UserModel.objects.using(db_alias).all():
        row.uniq_id = uuid.uuid4()
        row.save(update_fields=['uniq_id']) 

Secondly in mentioned documentation we have three separate migrations not one. It depends on database but in e.g. PostgreSQL you will not be able to alter column and update date in a single transaction. My advice is to split migration files and use schema_editor.connection.alias.

Please use one of support channels if you need furher help.

comment:5 by mandm, 5 years ago

Thank you @felixxm for the suggestions. A quick checks shows that schema_editor.connection.alias does fix the problem, but this solution only works in conjunction with routers i.e. unless I use allow_migrate, an accidental migration will affect the default database. I'll try the support channels to see if there's a way to use the non-default database directly in a migration without resorting to routers.

Note: See TracTickets for help on using tickets.
Back to Top