2016-03-28 3 views
2

У меня есть Django код, подобный этому:Оптимизация Django получить запросы

for obj in some_list: 
    m1obj = Model1.objects.get(a=obj.a, b=obj.b, c=obj.c) 
    Model2(m1=m1obj, d=obj.d, e='foo').save() 

Я оптимизировать вставку в Model2 используя bulk_create, однако, это все еще крайне медленно из-за get от Model1 (~ 45sec для вставок 3k).

Я также попытался добавить:

class Meta: 
    index_together = [ 
     ('a', 'b', 'c'), 
    ] 
    unique_together = [ 
     ('a', 'b', 'c'), 
    ] 

unique_together помогает немного, похоже, не имеют большого влияния index_together.

У меня есть громоздкий обходной путь для этого дела:

  1. фильтр Model1 получая все объекты, я обязательно заказанные одним или несколькими ключами, например order_by('a', 'b'), и убедитесь, что Django кэширует результат, например. len()
  2. Использование двоичного поиска (from bisect import bisect_left), чтобы найти первый a затем b ... и т.д. (хотя есть гораздо меньше b s и c S так просто итерация то же самое.

Это сокращает время вставки чтобы Jus более 3 секунды!

там должно быть лучше, чище и ремонтопригодны способ сделать это. Любые предложения? есть ли способ фильтрации/получить (бойко) в результатах запроса кэша Джанго?

EDIT: Изменено d='foo' к d=obj.d - навалом получить потребности быть отображаемыми в кортеже она принадлежит в противном случае я не может создать запись model2.

ответ

0

Вы можете сделать один запрос (как описано here), который будет извлекать только те результаты, которые вам нужны, поэтому вам не нужно сортировать и бинарный поиск позже.

Я не тестировал его, поэтому не знаю, будет ли он быстрее, чем то, что вы уже делаете, или нет. Также как SQL-запрос будет большим (в соответствии с количеством записей в some_list), поэтому этот запрос может вызвать ошибку, если он превышает размер, определенный в настройках MySQL параметром max_allowed_packet (по умолчанию 16 МБ, как упоминалось, here).

import operator 
from django.db.models import Q 
query = reduce(operator.or_, (Q(a=obj.a, b=obj.b, c=obj.c) for x in values)) 
model1_objs = Model1.objects.filter(query) 

Затем вы можете сделать bulk_create с Model2.

Model2.objects.bulk_create([ 
    Model2(m1=m1, d='foo', e='bar') 
    for m1 in model1_objs 
]) 
+0

К сожалению, ваше предложение гораздо медленнее, чем делать 'получить()' последовательно. Я остановил его через 3 минуты. Кроме того, как я упоминал в своем редактировании, это могло бы (должно быть?) Работать, если параметры «d» и «e» были постоянными. Поскольку это не так, у меня нет способа сопоставить 'model1_objs' с правильным' obj.d', поскольку запросы БД не гарантируют порядок. – mibm

0

Сколько строк у Model1? если он относительно небольшой (менее 50 тыс.), вы можете получить все с использованием фильтра, а затем сравнить кортеж в python.

Как насчет «some_list» - это небольшой список (менее 100), если вы можете ключевое слово Q фильтровать все сразу.

first = some_list.pop() 
conditions = Q(a=first.a, b=first.b, c=first.c) 
for obj in some_list: 
    conditions |= Q(a=obj.a, b=obj.b, c=obj.c) 

Model1.objects.filter(conditions) # this will get your all the Model1 from ur list 

Q объекта Ref: https://docs.djangoproject.com/en/1.9/ref/models/querysets/#q-objects

+0

Это решение такое же, как и предложение @ muhammad-tahir. Для 3K строк я убил запрос через несколько минут. Для небольших групп это не намного быстрее (это вообще), чем отдельные запросы. Я улучшил обходной путь, чтобы создать кортеж для таблицы row map, который является быстрым и читаемым, но если строки будут 100K, а не 3K, я могу столкнуться с проблемами памяти ... – mibm

Смежные вопросы