Мне нужно вставить 8000+ записей в базу данных SQLite с помощью Django ORM. Эта операция должна выполняться как cronjob примерно раз в минуту.
На данный момент я использую цикл for для повторения всех элементов, а затем вставляю их один за другим.
Пример:
for item in items:
entry = Entry(a1=item.a1, a2=item.a2)
entry.save()
Каков эффективный способ сделать это?
Изменить: Небольшое сравнение между двумя способами вставки.
Без commit_manually decorator (11245 записей):
nox@noxdevel marinetraffic]$ time python manage.py insrec
real 1m50.288s
user 0m6.710s
sys 0m23.445s
Использование commit_manual decorator (11245 записей):
[nox@noxdevel marinetraffic]$ time python manage.py insrec
real 0m18.464s
user 0m5.433s
sys 0m10.163s
Примечание. Тест script также выполняет некоторые другие операции, кроме вставки в базу данных (загружает ZIP файл, извлекает XML файл из ZIP-архива, анализирует XML файл), поэтому время необходимый для выполнения, не обязательно означает время, необходимое для вставки записей.
Вы хотите проверить django.db.transaction.commit_manually
.
http://docs.djangoproject.com/en/dev/topics/db/transactions/#django-db-transaction-commit-manually
Итак, это будет что-то вроде:
from django.db import transaction
@transaction.commit_manually
def viewfunc(request):
...
for item in items:
entry = Entry(a1=item.a1, a2=item.a2)
entry.save()
transaction.commit()
Которое будет выполняться только один раз, вместо этого в каждом файле сохранения().
В django 1.3 были введены контекстные менеджеры. Итак, теперь вы можете использовать transaction.commit_on_success():
from django.db import transaction
def viewfunc(request):
...
with transaction.commit_on_success():
for item in items:
entry = Entry(a1=item.a1, a2=item.a2)
entry.save()
В django 1.4 добавлен bulk_create
, что позволяет создавать списки ваших объектов модели и затем совершать их все сразу.
ПРИМЕЧАНИЕ метод сохранения не будет вызываться при использовании массового создания.
>>> Entry.objects.bulk_create([
... Entry(headline="Django 1.0 Released"),
... Entry(headline="Django 1.1 Announced"),
... Entry(headline="Breaking: Django is awesome")
... ])
В django 1.6 был введен transaction.atomic, предназначенный для замены ныне устаревших функций commit_on_success
и commit_manually
.
из документации django для атома:
атом можно использовать как в качестве декоратора:
from django.db import transaction
@transaction.atomic
def viewfunc(request):
# This code executes inside a transaction.
do_stuff()
и как менеджер контекста:
from django.db import transaction
def viewfunc(request):
# This code executes in autocommit mode (Django default).
do_stuff()
with transaction.atomic():
# This code executes inside a transaction.
do_more_stuff()
Массовое создание доступно в Django 1.4:
https://django.readthedocs.io/en/1.4/ref/models/querysets.html#bulk-create
Возможно, вам лучше загружать детали - подготовьте файл и используйте инструмент массовой загрузки. Это будет значительно более эффективным, чем 8000 отдельных вставок.
Посмотрите этот. Это предназначено для использования готовых продуктов только с MySQL, но есть указатели на то, что делать для других баз данных.
Чтобы ответить на вопрос, в частности, в отношении SQLite, как я уже сказал, хотя я только что подтвердил, что bulk_create действительно обеспечивает огромное ускорение, существует ограничение с SQLite: "По умолчанию создается все объекты в одной партии, за исключением SQLite, где значение по умолчанию таково, что используется максимум 999 переменных для каждого запроса."
Цитата из документа - A-IV предоставила ссылку.
Что я должен добавить, так это то, что этот djangosnippets запись alpar также работает для меня. Это небольшая оболочка, которая разбивает большую партию, которую вы хотите обработать на более мелкие партии, управляя пределом переменных 999.
Вы должны проверить DSE. Я написал DSE для решения таких проблем (массивная вставка или обновления). Использование django orm является тупиковым, вы должны сделать это в простом SQL, и DSE позаботится о многом для вас.
Томас
Я рекомендую использовать простой SQL (не ORM), вы можете вставить несколько строк с одной вставкой:
insert into A select from B;
Часть выберите из B часть вашего sql может быть такой же сложной, как вы хотите, чтобы она дошла до того, как результаты совпали с столбцами в таблице A, и нет конфликтов ограничений.
Я столкнулся с одной и той же проблемой, и я не могу найти способ сделать это без большого количества вставок. Я согласен с тем, что использование транзакций, вероятно, является правильным способом его решения, но вот мой взлом:
def viewfunc(request):
...
to_save = [];
for item in items:
entry = Entry(a1=item.a1, a2=item.a2)
to_save.append(entry);
map(lambda x: x.save(), to_save);