Django ile 'toplu güncelleme' nasıl yapılır?


163

Bir tablo Django - ham SQL'de böyle bir şey güncellemek istiyorum:

update tbl_name set name = 'foo' where name = 'bar'

İlk sonucum böyle bir şey - ama bu kötü, değil mi?

list = ModelClass.objects.filter(name = 'bar')
for obj in list:
    obj.name = 'foo'
    obj.save()

Daha zarif bir yol var mı?


1
Toplu ek arıyor olabilirsiniz. Stackoverflow.com/questions/4294088/…
Pramod

Yeni veri eklemek istemiyorum - sadece var olanı güncelle
Thomas Schwärzl

3
Belki select_for_update yardımıyla? docs.djangoproject.com/tr/dev/ref/models/querysets/…
Jure C.

ModelClassYaklaşım hakkında kötü olmayan ne ? Sonra Django'ya aşağıdaki şekilde besleyin: stackoverflow.com/questions/16853649/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Yanıtlar:


256

Güncelleme:

Django 2.2 sürümü şimdi bir bulk_update var .

Eski cevap:

Aşağıdaki django belgeleri bölümüne bakın

Aynı anda birden fazla nesneyi güncelleme

Kısacası şunları kullanabilmelisiniz:

ModelClass.objects.filter(name='bar').update(name="foo")

FNesneleri, satırları arttırma gibi şeyler yapmak için de kullanabilirsiniz :

from django.db.models import F
Entry.objects.all().update(n_pingbacks=F('n_pingbacks') + 1)

Belgelere bakın .

Ancak aşağıdakilere dikkat edin:

  • Bu ModelClass.saveyöntem kullanılmaz (bu nedenle içinde bir mantık varsa tetiklenmez).
  • Hiçbir django sinyali yayınlanmayacaktır.
  • .update()Dilimlenmiş bir QuerySet üzerinde bir gerçekleştiremezsiniz , orijinal bir QuerySet üzerinde olması gerekir, böylece .filter()ve .exclude()yöntemlerine yaslanmanız gerekir .

27
Kullanmamanın bir sonucu olarak save(), ("değiştirilmiş" sütunlar) DateTimeFieldiçeren alanların auto_now=Truegüncellenmeyeceğini de unutmayın.
Arthur

3
Ancak ModelClass.objects.filter(name = 'bar').update(name="foo")toplu güncelleme amacını karşılamıyor, farklı kimlikler için farklı verilerim varsa, bunu döngü kullanmadan nasıl yapabilirim?
Shashank

@shihon Seni doğru anladığımdan emin değilim ama cevaba örnek ekledim.
jb.

@Shashank davanız için henüz bir çözüm buldunuz mu? Ben de aynı senaryoyu yaşıyorum.
Sourav Prem

F nesneleri .update yöntemindeki farklı modellere başvuruda bulunmak için kullanılamaz ... örneğin kullanamazsınız Entry.objects.all().update(title=F('blog__title')). Dokümanlar bundan biraz bahsediyor. Girişlerinizi güncellemek için başka bir modelden veri almak istiyorsanız, for döngüsü çalıştırmanız gerekir
sean.hudson

31

Kullanmayı düşünün django-bulk-updatebulundu GitHub'dan burada .

Yüklemek: pip install django-bulk-update

Uygulama: (doğrudan projeler ReadMe dosyasından alınan kod)

from bulk_update.helper import bulk_update

random_names = ['Walter', 'The Dude', 'Donny', 'Jesus']
people = Person.objects.all()

for person in people:
    r = random.randrange(4)
    person.name = random_names[r]

bulk_update(people)  # updates all columns using the default db

Güncelleme: Marc'ın yorumlarda belirttiği gibi, bu, binlerce satırı aynı anda güncellemek için uygun değildir. Her ne kadar daha küçük partiler 10'lar ila 100'ler için uygundur. Sizin için doğru olan partinin boyutu CPU'nuza ve sorgu karmaşıklığına bağlıdır. Bu araç, bir damperli kamyondan daha çok bir tekerlekli el arabası gibidir.


16
Django-bulk-update'i denedim ve kişisel olarak kullanmaktan vazgeçiyorum. Dahili olarak yaptığı şuna benzer tek bir SQL deyimi oluşturmaktır: UPDATE "table" SET "field" = CASE "id" WHEN% s THEN% s WHEN% s THEN% s [...] NEREDE ID ( % s,% s, [...]) ;. Bu, birkaç satır için uygundur (toplu güncelleyici gerekli olmadığında), ancak 10.000 ile sorgu o kadar karmaşıktır ki, postgres CPU'yu sorguyu% 100 oranında anlayarak, diske yazmayı kaydettiği zamandan daha fazla zaman harcar .
Marc Garcia

1
@MarcGarcia iyi bir nokta. Birçok geliştiricinin etkilerini bilmeden harici kütüphaneler kullandığını
gördüm

3
@MarcGarcia Toplu güncellemenin değerli olmadığını ve yalnızca binlerce güncelleme gerektiğinde gerçekten gerekli olduğunu kabul etmiyorum. Belirttiğiniz nedenlerle bir kerede 10.000 satır yapmak için kullanılması önerilmez, ancak aynı anda 50 satırı güncellemek için kullanmak, 50 ayrı güncelleme isteğiyle db'ye basmaktan çok daha etkilidir.
nu everest

3
Bulduğum en iyi çözümler şunlardır: a) tek bir işlem kullanarak performansı artıran @ transaction.atomic dekoratör kullanın veya b) geçici bir tabloda toplu bir ekleme yapın ve daha sonra geçici tablodan orijinal olana bir GÜNCELLEŞTİRİN.
Marc Garcia

1
Bu eski bir iş parçacığı olduğunu biliyorum, ama aslında CASE / NEREDE tek yolu değil. PostgreSQL için başka yaklaşımlar da vardır, ancak DB'ye özgüdür örn. Stackoverflow.com/a/18799497 Ancak bunun ANSI SQL'de mümkün olup olmadığından emin değilim
Ilian Iliev

21

Django 2.2 sürümü artık bir sahiptir bulk_updateyöntemi ( sürüm notları ).

https://docs.djangoproject.com/en/stable/ref/models/querysets/#bulk-update

Misal:

# get a pk: record dictionary of existing records
updates = YourModel.objects.filter(...).in_bulk()
....
# do something with the updates dict
....
if hasattr(YourModel.objects, 'bulk_update') and updates:
    # Use the new method
    YourModel.objects.bulk_update(updates.values(), [list the fields to update], batch_size=100)
else:
    # The old & slow way
    with transaction.atomic():
        for obj in updates.values():
            obj.save(update_fields=[list the fields to update])


8

Bir satır koleksiyonunda aynı değeri ayarlamak istiyorsanız , tek bir sorgudaki tüm satırları güncellemek için update () yöntemini herhangi bir sorgu terimiyle birlikte kullanabilirsiniz:

some_list = ModelClass.objects.filter(some condition).values('id')
ModelClass.objects.filter(pk__in=some_list).update(foo=bar)

Eğer isterseniz farklı değerlere sahip satırların bir koleksiyon güncelleştirmek Koşullara göre, en iyi durumda toplu güncellemeler değerlere göre yapabilirsiniz. Diyelim ki, X değerlerinden birine bir sütun ayarlamak istediğiniz 1000 satırınız var, sonra toplu işlemleri önceden hazırlayabilir ve ardından yalnızca X güncelleme sorgusunu çalıştırabilirsiniz (her biri esas olarak yukarıdaki ilk örnek biçimindedir) + ilk SELECT -sorgu.

Eğer her satır benzersiz bir değer gerektirir güncelleme başına bir sorgu önlemek için hiçbir yolu yoktur. Bu son durumda performansa ihtiyacınız varsa, belki de CQRS / Etkinlik kaynağı gibi diğer mimarilere bakın.


1

BT döndürür nesnelerin sayısı tabloda güncellenir.

update_counts = ModelClass.objects.filter(name='bar').update(name="foo")

Toplu güncelleme ve oluşturma hakkında daha fazla bilgi almak için bu bağlantıya başvurabilirsiniz. Toplu güncelleme ve Oluşturma

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.