2015-07-06 1 views
11

Я хотел бы написать миграцию данных, где я изменяю все строки в большой таблице меньшими партиями, чтобы избежать проблем с блокировкой. Однако я не могу понять, как вручную выполнить перенос Django. Каждый раз я пытаюсь запустить commit я получаю:Зафиксировать вручную в миграции данных Django

TransactionManagementError: This is forbidden when an 'atomic' block is active.

AFAICT, в database schema editor always wraps Postgres migrations в качестве atomic block.

Есть ли разумный способ выйти из транзакции из-за миграции?

Моя миграция выглядит следующим образом:

def modify_data(apps, schema_editor): 
    counter = 0 
    BigData = apps.get_model("app", "BigData") 
    for row in BigData.objects.iterator(): 
     # Modify row [...] 
     row.save() 
     # Commit every 1000 rows 
     counter += 1 
     if counter % 1000 == 0: 
      transaction.commit() 
    transaction.commit() 

class Migration(migrations.Migration): 
    operations = [ 
     migrations.RunPython(modify_data), 
    ] 

Я использую Django 1.7 и Postgres 9.3. Это использовалось для работы с южными и более старыми версиями Django.

ответ

5

От the documentation about RunPython:

By default, RunPython will run its contents inside a transaction on databases that do not support DDL transactions (for example, MySQL and Oracle). This should be safe, but may cause a crash if you attempt to use the schema_editor provided on these backends; in this case, pass atomic=False to the RunPython operation.

Таким образом, вместо того, что у вас есть:

class Migration(migrations.Migration): 
    operations = [ 
     migrations.RunPython(modify_data, atomic=False), 
    ] 
+1

Спасибо. Я уже пробовал это, но на самом деле не удаляет атомный контекст вокруг миграции (по крайней мере, для Postgres). – Pankrat

+0

Любопытно, потому что вот код в django.db.migration.py: 'if not schema_editor.connection.features.can_rollback_ddl и operation.atomic:' - 'if not schema_editor.connection.features.can_rollback_ddl и operation.atomic: с атомарным (schema_editor.connection.alias): ... '. Вы позитивны, что-то еще не происходит? Может быть, установить там точку останова (это строка 109 в django 1.8)? –

+0

Да, это позволяет избежать атомарности операции, но редактор схемы базы данных по-прежнему делает весь перенос атома атомом: https://github.com/django/django/blob/stable/1.7.x/django/db/backends/schema.py # L85 – Pankrat

9

Лучший обходной путь я нашел вручную выхода из атомной сферы перед переносом данных:

def modify_data(apps, schema_editor): 
    schema_editor.atomic.__exit__(None, None, None) 
    # [...] 

В отличие от перезагрузки connection.in_atomic_block вручную это позволяет использовать контекстный менеджер atomic внутри миграции. Кажется, что не существует более здравого способа.

можно содержать (правда, грязный) сделка вспыхнуть логику декоратора для использования с RunPython работы:

def non_atomic_migration(func): 
    """ 
    Close a transaction from within code that is marked atomic. This is 
    required to break out of a transaction scope that is automatically wrapped 
    around each migration by the schema editor. This should only be used when 
    committing manually inside a data migration. Note that it doesn't re-enter 
    the atomic block afterwards. 
    """ 
    @wraps(func) 
    def wrapper(apps, schema_editor): 
     if schema_editor.connection.in_atomic_block: 
      schema_editor.atomic.__exit__(None, None, None) 
     return func(apps, schema_editor) 
    return wrapper 

Update

Джанго 1,10 будет поддерживать non-atomic migrations.

Смежные вопросы