Django: Пакетное обновление/создание с использованием update_or_create?
У меня есть данные в базе данных, которые нужно обновлять периодически. Источник данных возвращает всю доступную информацию на текущий момент, включая новые данные, которых еще нет в базе.
Когда я перебираю данные из источника, я не хочу выполнять тысячи отдельных записей, если это возможно.
Существует ли что-то вроде update_or_create
, но работающее с пакетами?
Одной из идей было использовать update_or_create
в сочетании с ручными транзакциями, но я не уверен, приведет ли это к очередной записи отдельных записей или же объединит все в один SQL-инсерта.
Или, аналогично, может ли использование @commit_on_success()
в функции с update_or_create
внутри цикла сработать?
Я не делаю ничего особенного с данными, кроме перевода и сохранения их в модели. Ничто не зависит от существования этой модели во время цикла.
1 ответ(ов)
Вопрос касался пакетных обновлений в PostgreSQL, и, как уже упоминал @imposeren, начиная с версии 9.5 в Postgres появилась возможность использовать команды upsert. В MySQL 5.7 такая возможность тоже присутствует (см. http://dev.mysql.com/doc/refman/5.7/en/insert-on-duplicate.html), в зависимости от ваших точных требований. Тем не менее, вероятно, проще будет использовать курсор базы данных. Ничего плохого в этом нет, он предназначен для случаев, когда ORM недостаточен.
Вам подойдет что-то вроде следующего. Это псевдокод, так что не стоит просто копировать и вставлять его, но концепция в нем ясна:
class GroupByChunk(object):
def __init__(self, size):
self.count = 0
self.size = size
self.toggle = False
def __call__(self, *args, **kwargs):
if self.count >= self.size: # Позволяет использовать размер 0
self.toggle = not self.toggle
self.count = 0
self.count += 1
return self.toggle
def batch_update(db_results, upsert_sql):
with transaction.atomic():
cursor = connection.cursor()
for chunk in itertools.groupby(db_results, GroupByChunk(size=1000)):
cursor.execute_many(upsert_sql, chunk)
Предположения здесь следующие:
db_results
— это какой-то итератор результатов, либо в виде списка, либо в виде словаря.- Результат из
db_results
можно напрямую использовать в SQL-выражении. - Если любое из пакетных обновлений завершается неудачей, будет откат всех обновлений. Если вы хотите сделать откат для каждого пакета, просто переместите блок
with
немного ниже.
'pip' не распознан как командa внутреннего или внешнего формата
Как выполнить поиск в стиле getattr() в шаблоне Django
PIL / JPEG библиотека: "декодировщик JPEG недоступен"
"_set" в объекте queryset в Django
Python sqlite3.OperationalError: таблица не найдена