2015-07-06 13 views
11

Kilitleme sorunlarından kaçınmak için büyük gruplar halinde tüm satırları daha küçük gruplar halinde değiştirdiğim bir veri geçişi yazmak istiyorum. Ancak, bir Django geçişinde elle nasıl işleneceğini anlayamıyorum. Everytime commit alıyorum çalıştırmayı deneyin:Django veri geçişinde el ile teslim et

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

AFAICT bir atomic block içinde database schema editor always wraps Postgres migrations.

İşlemin içinden geçişi engellemenin aklı başında bir yolu var mı?

taşıma işlemim şöyle görünür:

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 ve Postgres 9.3 kullanıyorum. Bu, Django'nun Güney ve eski sürümleriyle çalışırdı. the documentation about RunPython itibaren

cevap

5

: Yani

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.

yerine ne var içinde:

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

Teşekkürler. Bunu çoktan denedim ama aslında göçün etrafındaki atomik bağlamı ortadan kaldırmıyor (en azından Postgres için). – Pankrat

+0

Meraklı, çünkü django.db.migration.py dosyasındaki kod: 'schema_editor.connection.features.can_rollback_ddl ve operation.atomic: '- eğer değilse schema_editor.connection.features.can_rollback_ddl ve operation.atomic: atomik değil (schema_editor.connection.alias): ... '. Başka bir şey olmadığına olumlu baktın mı? Belki orada bir kırılma noktası koydu (django 1.8'deki çizgi 109)? –

+0

Evet, bu işlem atomik yapmaktan kaçınıyor, ancak veritabanı şema editörü hala tüm taşıma atomunu yapıyor: https://github.com/django/django/blob/stable/1.7.x/django/db/backends/schema.py # L85 – Pankrat

9

el veri taşıma işleminden önce atom kapsamını çıkılıyor bulundu en iyi çözüm:

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

Bu, connection.in_atomic_block'u el ile sıfırlamanın tersine, bu, geçişin içinde atomic içerik yöneticisinin kullanımına izin verir. Çok daha iyi bir yol yok gibi görünüyor.

One (kuşkusuz dağınık) işlem RunPython operasyonla kullanılacak bir dekoratör mantığı patlak içerebilir:

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 

Güncelleme

Django 1.10 non-atomic migrations destekleyecektir.