2015-07-06 13 views
11

Vorrei scrivere una migrazione dei dati in cui modifico tutte le righe di una grande tabella in gruppi più piccoli per evitare problemi di blocco. Tuttavia, non riesco a capire come eseguire il commit manualmente in una migrazione di Django. Ogni volta che provo a correre commit ottengo:Esegui manualmente nella migrazione dei dati di Django

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

AFAICT, il database schema editor always wraps Postgres migrations in un atomic block.

Esiste un modo corretto per interrompere la transazione dall'interno della migrazione?

La mia migrazione simile a questa:

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), 
    ] 

sto usando Django 1.7 e Postgres 9.3. Funzionava con le versioni sud e precedenti di Django.

risposta

5

Da 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.

Così, invece di quello che hai:

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

Grazie. L'ho già provato, ma in realtà non rimuove il contesto atomico attorno alla migrazione (almeno per Postgres). – Pankrat

+0

Curioso, perché qui è il codice in django.db.migration.py: 'se non schema_editor.connection.features.can_rollback_ddl e operation.atomic:' - 'se non schema_editor.connection.features.can_rollback_ddl e operation.atomic: con atomic (schema_editor.connection.alias): ... '. Sei positivo, qualcos'altro non sta succedendo? Forse impostare un breakpoint lì (è la riga 109 in django 1.8)? –

+0

Sì, questo evita di rendere l'operazione atomica ma l'editor dello schema del database rende tuttora l'intera migrazione atomica: https://github.com/django/django/blob/stable/1.7.x/django/db/backends/schema.py # L85 – Pankrat

9

La migliore soluzione che ho trovato è uscendo manualmente la portata atomica prima di eseguire la migrazione dei dati:

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

A differenza della reimpostazione manuale di connection.in_atomic_block, ciò consente di utilizzare il gestore contesto atomic all'interno della migrazione. Non sembra essere un modo molto più sano.

Si può contenere il (certamente disordinata) transazione scoppiare la logica in un decoratore per essere utilizzato con l'operazione 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 

Aggiornamento

Django 1.10 sosterrà non-atomic migrations.

Problemi correlati