Каков эффективный способ вставки тысяч записей в таблицу SQLite с использованием Django?

Я должен вставить более 8000 записей в базу данных SQLite, используя ORM Джанго. Эту операцию нужно запускать как cronjob примерно раз в минуту.
В данный момент я использую цикл for, чтобы перебрать все элементы и затем вставить их один за другим.
Пример:

for item in items:
    entry = Entry(a1=item.a1, a2=item.a2)
    entry.save()

Какой эффективный способ сделать это?

Редактировать: небольшое сравнение между двумя методами вставки.

Без commit_manually декоратора (11245 записей):

nox@noxdevel marinetraffic]$ time python manage.py insrec             

real    1m50.288s
user    0m6.710s
sys     0m23.445s

Использование commit_manually декоратора (11245 записей):

[nox@noxdevel marinetraffic]$ time python manage.py insrec                

real    0m18.464s
user    0m5.433s
sys     0m10.163s

Примечание. Тестовый сценарий также выполняет некоторые другие операции, помимо вставки в базу данных (загрузка файла ZIP, извлечение файла XML из архива ZIP, анализ файла XML), поэтому время, необходимое для выполнения, не обязательно представляет время, необходимое для вставки. записи.

7 ответов

Решение

Вы хотите проверить django.db.transaction.commit_manually,

http://docs.djangoproject.com/en/dev/topics/db/transactions/

Так что это будет что-то вроде:

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 были введены контекстные менеджеры. Итак, теперь вы можете использовать транзакции.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()

В Джанго 1.4, bulk_createбыл добавлен, что позволяет вам создавать списки объектов вашей модели и затем фиксировать их все сразу.

ПРИМЕЧАНИЕ. Метод save не будет вызываться при использовании массового создания.

>>> 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 был введен транзакции.atomic, призванные заменить устаревшие функцииcommit_on_successа такжеcommit_manually,

из документации Django по атомному:

atomic может использоваться как декоратор:

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's 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

Возможно, вам лучше будет выполнить массовую загрузку элементов - подготовьте файл и используйте инструмент массовой загрузки. Это будет значительно эффективнее, чем 8000 отдельных вкладышей.

Чтобы ответить на вопрос, в частности, касающийся SQLite, как я и просил, в то время как я только что подтвердил, что bulk_create обеспечивает огромное ускорение, есть ограничение с SQLite: "По умолчанию все объекты создаются в одном пакете, за исключением SQLite, где По умолчанию используется не более 999 переменных на запрос."

Цитируемый материал взят из документации --- A-IV предоставил ссылку.

Что я должен добавить, так это то, что эта запись djangosnippets от alpar, похоже, тоже работает для меня. Это небольшая оболочка, которая разбивает большую партию, которую вы хотите обработать, на меньшие партии, управляя пределом в 999 переменных.

Посмотрите на это. Он предназначен для использования "из коробки" только с MySQL, но есть указания, что делать с другими базами данных.

Вы должны проверить DSE. Я написал DSE для решения таких проблем (массовые вставки или обновления). Использование django orm - это тупик, вы должны делать это простым SQL, и DSE позаботится о многом за вас.

Томас

def order(request):    
    if request.method=="GET":
        cust_name = request.GET.get('cust_name', '')
        cust_cont = request.GET.get('cust_cont', '')
        pincode = request.GET.get('pincode', '')
        city_name = request.GET.get('city_name', '')
        state = request.GET.get('state', '')
        contry = request.GET.get('contry', '')
        gender = request.GET.get('gender', '')
        paid_amt = request.GET.get('paid_amt', '')
        due_amt = request.GET.get('due_amt', '')
        order_date = request.GET.get('order_date', '')
        print(order_date)
        prod_name = request.GET.getlist('prod_name[]', '')
        prod_qty = request.GET.getlist('prod_qty[]', '')
        prod_price = request.GET.getlist('prod_price[]', '')
        print(prod_name)
        print(prod_qty)
        print(prod_price)
        # insert customer information into customer table
        try:
            # Insert Data into customer table
            cust_tab = Customer(customer_name=cust_name, customer_contact=cust_cont, gender=gender, city_name=city_name, pincode=pincode, state_name=state, contry_name=contry)
            cust_tab.save()
            # Retrive Id from customer table
            custo_id = Customer.objects.values_list('customer_id').last()   #It is return
Tuple as result from Queryset
            custo_id = int(custo_id[0]) #It is convert the Tuple in INT
            # Insert Data into Order table
            order_tab = Orders(order_date=order_date, paid_amt=paid_amt, due_amt=due_amt, customer_id=custo_id)
            order_tab.save()
            # Insert Data into Products table
            # insert multiple data at a one time from djanog using while loop
            i=0
            while(i<len(prod_name)):
                p_n = prod_name[i]
                p_q = prod_qty[i]
                p_p = prod_price[i]
                # this is checking the variable, if variable is null so fill the varable value in database
                if p_n != "" and p_q != "" and p_p != "":
                    prod_tab = Products(product_name=p_n, product_qty=p_q, product_price=p_p, customer_id=custo_id)
                    prod_tab.save()
                i=i+1
def order(request):    
    if request.method=="GET":
        # get the value from html page
        cust_name = request.GET.get('cust_name', '')
        cust_cont = request.GET.get('cust_cont', '')
        pincode = request.GET.get('pincode', '')
        city_name = request.GET.get('city_name', '')
        state = request.GET.get('state', '')
        contry = request.GET.get('contry', '')
        gender = request.GET.get('gender', '')
        paid_amt = request.GET.get('paid_amt', '')
        due_amt = request.GET.get('due_amt', '')
        order_date = request.GET.get('order_date', '')
        prod_name = request.GET.getlist('prod_name[]', '')
        prod_qty = request.GET.getlist('prod_qty[]', '')
        prod_price = request.GET.getlist('prod_price[]', '')

        # insert customer information into customer table
        try:
            # Insert Data into customer table
            cust_tab = Customer(customer_name=cust_name, customer_contact=cust_cont, gender=gender, city_name=city_name, pincode=pincode, state_name=state, contry_name=contry)
            cust_tab.save()
            # Retrive Id from customer table
            custo_id = Customer.objects.values_list('customer_id').last()   #It is return Tuple as result from Queryset
            custo_id = int(custo_id[0]) #It is convert the Tuple in INT
            # Insert Data into Order table
            order_tab = Orders(order_date=order_date, paid_amt=paid_amt, due_amt=due_amt, customer_id=custo_id)
            order_tab.save()
            # Insert Data into Products table
            # insert multiple data at a one time from djanog using while loop
            i=0
            while(i<len(prod_name)):
                p_n = prod_name[i]
                p_q = prod_qty[i]
                p_p = prod_price[i]

                # this is checking the variable, if variable is null so fill the varable value in database
                if p_n != "" and p_q != "" and p_p != "":
                    prod_tab = Products(product_name=p_n, product_qty=p_q, product_price=p_p, customer_id=custo_id)
                    prod_tab.save()
                i=i+1

            return HttpResponse('Your Record Has been Saved')
        except Exception as e:
            return HttpResponse(e)     

    return render(request, 'invoice_system/order.html')

Я рекомендую использовать простой 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);
Другие вопросы по тегам