Як "масово оновити" з Django?


163

Я хотів би оновити таблицю з Django - щось подібне у сирому SQL:

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

Мій перший результат - це щось подібне - але це противно, чи не так?

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

Чи є більш елегантний спосіб?


1
Можливо, ви шукаєте пакетну вставку. Погляньте на stackoverflow.com/questions/4294088/…
Pramod

Мені не подобається вставляти нові дані - просто оновлюйте наявні.
Thomas Schwärzl

3
Можливо, за допомогою select_for_update? docs.djangoproject.com/en/dev/ref/models/querysets/…
Jure C.

Що не противно в ModelClassпідході? Потім годувати Джанго як: stackoverflow.com/questions/16853649 / ...
Чіро Сантіллі郝海东冠状病六四事件法轮功

Відповіді:


256

Оновлення:

Версія Django 2.2 тепер має bulk_update .

Стара відповідь:

Дивіться наступний розділ документації щодо джанго

Оновлення декількох об'єктів одночасно

Коротше кажучи, ви повинні використовувати:

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

Ви також можете використовувати Fоб'єкти, щоб виконувати такі дії, як приріст рядків:

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

Дивіться документацію .

Однак зауважте, що:

  • Це не буде використовувати ModelClass.saveметод (тому якщо у вас є певна логіка, вона не буде спрацьовувати).
  • Сигнали джанго не випромінюються.
  • Ви не можете виконувати .update()нарізаний QuerySet, він повинен бути на оригінальному QuerySet, тому вам потрібно буде спиратися на методи .filter()та .exclude()методи.

27
Слід також зазначити , що , як наслідок , не використовуючи save(), DateTimeFieldполя з auto_now=True( «модифікованими» стовпців) не оновлюватися.
Артур

3
Але ModelClass.objects.filter(name = 'bar').update(name="foo")не відповідає меті масового оновлення, якщо у мене різні дані для різних ідентифікаторів, то як я можу це зробити, не використовуючи цикл?
Шашанк

@shihon Я не впевнений, чи правильно я тебе зрозумів, але я додав приклад у відповідь.
jb.

@Shashank Ви знайшли рішення щодо своєї справи ще? У мене теж такий самий сценарій.
Сурав Прем

F-об'єкти не можуть бути використані для посилання на різні моделі в .update методі ... наприклад, ви не можете використовувати Entry.objects.all().update(title=F('blog__title')). Документи мало про це згадують. Якщо ви хочете витягнути дані з іншої моделі, щоб оновити записи, вам доведеться запустити цикл
sean.hudson

31

Подумайте про використання django-bulk-updateзнайденого тут на GitHub .

Встановити: pip install django-bulk-update

Реалізація: (код взято безпосередньо з файлу ReadMe проектів)

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

Оновлення: Як Марк зазначає у коментарях, це не підходить для оновлення тисяч рядків одночасно. Хоча він підходить для менших партій від 10 до 100. Розмір пакету, який підходить саме вам, залежить від вашого процесора та складності запиту. Цей інструмент більше нагадує колісний курган, ніж самоскид.


16
Я спробував django-bulk-update, і я особисто не встигаю його використовувати. Все, що це робиться внутрішньо, - це створити єдиний оператор SQL, який виглядає приблизно так: UPDATE "таблиця" SET "поле" = CASE "id" WHEN% s THEN% s WHEN% s THEN% s [...] WHERE id in ( % s,% s, [...]) ;. Це начебто добре для кількох рядків (коли масове оновлення не потрібно), але з 10 000 запит настільки складний, що postgres витрачає більше часу з процесором на 100% розуміння запиту, ніж час, який заощаджує запис на диск .
Марк Гарсія

1
@MarcGarcia хороший момент. Я виявив, що багато розробників використовують зовнішні бібліотеки, не знаючи їх впливу
Dejell

3
@MarcGarcia Я не погоджуюся, що масове оновлення не є цінним і дійсно потрібне лише тоді, коли потрібні тисячі оновлень. Використовувати його для виконання 10 000 рядків відразу не доцільно з вказаних вами причин, але використовувати його для оновлення 50 рядків одночасно набагато ефективніше, ніж потрапляння на db за допомогою 50 окремих запитів на оновлення.
nu everest

3
Найкращі рішення, які я знайшов, це: a) використовувати @action.atomic decorator, який покращує продуктивність за допомогою однієї транзакції, або b) внести об'ємну вставку у тимчасову таблицю, а потім ОНОВЛЕННЯ з тимчасової таблиці до початкової.
Марк Гарсія

1
Я знаю, що це стара тема, але насправді CASE / WHERE - це не єдиний спосіб. Для PostgreSQL є й інші підходи, але вони є специфічними для БД, наприклад, stackoverflow.com/a/18799497 Однак я не впевнений, чи можливо це в ANSI SQL
Іліан Ілієв

21

Версія Django 2.2 тепер має bulk_updateметод ( примітки до випуску ).

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

Приклад:

# 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

Якщо ви хочете встановити однакове значення для колекції рядків , ви можете використовувати метод update () у поєднанні з будь-яким терміном запиту, щоб оновити всі рядки в одному запиті:

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

Якщо ви хочете оновити колекцію рядків з різними значеннями залежно від певної умови, ви можете в кращому випадку провести пакетне оновлення відповідно до значень. Скажімо, у вас є 1000 рядків, де ви хочете встановити стовпчик на одне із значень X, тоді ви можете заздалегідь підготувати партії, а потім виконати лише X-оновлення-запити (кожен по суті має форму першого прикладу вище) + початковий SELECT -запит.

Якщо кожен рядок вимагає унікального значення , немає можливості уникнути одного запиту за оновлення. Можливо, подивіться на інші архітектури, такі як пошук CQRS / подій, якщо вам потрібна продуктивність в цьому останньому випадку.


1

ІТ-повернення кількість об'єктів оновлюється в таблиці.

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

Ви можете перейти за цим посиланням, щоб отримати додаткову інформацію про масове оновлення та створення. Масове оновлення та створення

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.