Как я могу обновить определенный ключ в HStoreField в django?
У меня есть модель Django с HStoreField, и я пытаюсь обновить определенный ключ данных в этом поле. Я стараюсь не загружать модель в память, так как данных достаточно, и это будет для большого количества экземпляров объектов.
У меня есть объект Result с полем data, и я пытаюсь обновить существующий ключ, я пробовал:
Result.objects.update(data__existingkey='new_value')
Но я просто получаю FieldDoesNotExist: ResultsContainer has no field named 'data__existingkey'
Я думал, это сработает как Result.objects.filter(data__existingkey='value')
работает отлично. Любые предложения будут оценены, большое спасибо
1 ответ
Первое место, которое я бы посмотрел, это django
HStoreField
документы. Не найдя там ничего подходящего, я бы предположил, что такая функция, если она существует, не реализована в Django.
Таким образом, мой поиск теперь нацелен на PostgreSQL.
hstore
документы. Просматривая этот документ, я не могу найти ни одной функции, целью которой явно является обновление определенного ключа hstore. Поэтому я подробно проверяю каждую функцию, чтобы выяснить, можно ли ее использовать для этой цели.
hstore || hstore → hstore
Concatenates two hstores.
'a=>b, c=>d'::hstore || 'c=>x, d=>q'::hstore → "a"=>"b", "c"=>"x", "d"=>"q"
Это бинго! С использованием
||
оператор, мы можем сделать что-то вроде
UPDATE "a" SET "data" = "a"."data" || hstore('existing_key=>new_value')
.
Теперь, поскольку это не реализовано в Django, давайте реализуем это сами:
import re
from django.contrib.postgres.fields import HStoreField
from django.db.models import Func, Value
def hstore_def_escape(s):
return re.sub(r'([\\"])', r'\\\1', s)
class HStore(Func)
function = 'hstore'
output_field = HStoreField()
def __init__(self, expression, **kwargs):
if isinstance(expression, dict):
expression = Value(
','.join(
'"{}" => "{}"'.format(hstore_def_escape(k), hstore_def_escape(v))
for k, v in expression.items()
)
)
super().__init__(expression, **kwargs)
class ConcatHStore(Func):
arg_joiner = ' || '
template = '%(expressions)s'
output_field = HStoreField()
def __init__(self, *expressions, **kwargs):
expressions = [
HStore(e) if isinstance(e, dict) else e
for e in expressions
]
super().__init__(*expressions, **kwargs)
Теперь вы можете сделать:
from django.db.models import F
Result.objects.update(data=ConcatHStore(F('data'), {'existing_key': 'new_value'}))