Объемное обновление Django с заменой строки
Я пытаюсь обновить и изменить строковое поле Django ORM. Эквивалентный SQL для этого:
UPDATE example_table SET string_field = REPLACE(string_field, 'old text', 'new text');
С этим запросом я ожидаю, что old text
и old text more text
будут заменены на new text
и new text more text
соответственно, для всех записей в столбце string_field
.
Массовое обновление() кажется многообещающим, но не позволяет мне изменять только часть поля, а Выражения F() реализуют только числовые изменения, а не замену строки. Я также рассмотрел использование необработанных запросов для запуска вышеуказанного SQL, но это похоже на боковую ручку (тем более, что F() существует для выполнения такая же функциональность для чисел), и я не мог заставить их фактически выполнить.
Я закончил с этим, но, похоже, стыдно выполнять все лишние запросы, когда я знаю, что для него требуется однострочный оператор SQL.
for entry in ExampleModel.objects.all():
entry.string_field = entry.string_field.replace('old text', 'new text', 1)
entry.save()
Эта функция еще не существует в Django ORM для строк? Есть ли что-то, что я забыл в документах?
Связанные вопросы SO:
Ответы
Ответ 1
Проверено с django 1.9
from django.db.models import F, Func, Value
ExampleModel.objects.filter(<condition>).update(
string_field=Func(
F('string_field'),
Value('old text'), Value('new text'),
function='replace',
)
)
ОБНОВЛЕНИЕ Django 2.1
https://docs.djangoproject.com/en/2.2/ref/models/database-functions/#replace
from django.db.models import Value
from django.db.models.functions import Replace
ExampleModel.objects.filter(<condition>).update(
string_field=Replace('name', Value('old text'), Value('new text'))
)
Ответ 2
Вы можете создать собственный F
-подобный объект для представления замены строки в SQL. Вот доказательство концепции:
from django.db.models.expressions import ExpressionNode
class StringReplaceF(ExpressionNode):
def __init__(self, field, replace_from, replace_to):
self.field = field
self.replace_from = replace_from
self.replace_to = replace_to
super(StringReplaceF, self).__init__()
def evaluate(self, evaluator, qn, connection):
return (
"REPLACE({}, %s, %s)".format(self.field),
(self.replace_from, self.replace_to)
)
>>> f = StringReplaceF('string_field', 'old text', 'new text')
>>> ExampleModel.objects.update(string_field=f)
Вам нужно будет немного поработать с классом, если вам нужно хорошо вести себя с другими объектами F
, но, опять же, существующие объекты F
, похоже, не работают со строками.
Ответ 3
Новое в Django 2.1 - Заменить функцию базы данных
Ваш пример теперь легче всего выразить через:
ExampleModel.objects.update(string_field=Replace('string_field', Value('old_text'), Value('new_text')))
Ответ 4
Django 2.2 поддерживает массовое обновление, можете ли вы использовать эту функцию.
Проверьте это: https://docs.djangoproject.com/en/2.2/ref/models/querysets/#django.db.models.query.QuerySet.bulk_create