Массовое обновление поля модели на основе условия
У меня есть модель из 5 столбцов с примерно 10 000+ строк данных. Теперь я хочу обновить один столбец из всех строк на основе значений других столбцов.
Современная функция django для массового обновления - bulk_update(). Вот пример:
myModel = MyModel.objects.all()
for l in myModel:
if c1=='ChkVal1' and c2 < c3:
c4='Value 1'
elif c1=='ChkVal2' and c2 b> c3:
c4='Value 2'
else
c4='Value 3'
MyModel.objects.bulk_update(myModel, update_fields = ['c4'])
Помните:
- Вы не можете обновить первичный ключ модели.
- Не вызывается метод save() каждой модели, и сигналы pre_save и post_save не посылаются.
- При обновлении большого количества столбцов в большом количестве строк генерируемый SQL может быть очень большим. Избежать этого можно, указав подходящий размер партии (batch_size).
- Обновление полей, определенных в предках, наследующих многотабличное наследование, повлечет за собой дополнительный запрос на каждого предка.
- Когда отдельная партия содержит дубликаты, только первый экземпляр в этой партии приведет к обновлению.
Параметр batch_size управляет тем, сколько объектов сохраняется в одном запросе. По умолчанию все объекты обновляются в одном пакете, за исключением SQLite и Oracle, которые имеют ограничения на количество переменных, используемых в запросе
.
На мой взгляд, для вашего сценария "Я хочу обновить один столбец из всех строк на основе значений других столбцов" , лучшим вариантом будет использование Условного обновления
Предположим такую модель:
class Person(models.Model):
f1 = models.IntegerField()
f2 = models.IntegerField()
f3 = models.IntegerField()
f4 = models.IntegerField()
Обновить его можно следующим образом:
rom django.db.models import F, When, Case
new_value = Case(
When(f1__gte=F('f2'), then=F('f1')),
default=F('f2')
)
Person.objects.update(f3=new_value)
Поднимите простой sql-запрос обновления:
>>> import logging
>>> l = logging.getLogger('django.db.backends')
>>> l.setLevel(logging.DEBUG)
>>> l.addHandler(logging.StreamHandler())
>>> Person.objects.update(f3=c)
(0.000) UPDATE "tt_person" SET "f3" = CASE WHEN ("tt_person"."f1" >= "tt_person"."f2") THEN "tt_person"."f1" ELSE "tt_person"."f2" END; args=()
0