2016-12-20 6 views
3

Есть ли более эффективный способ сделать это, возможно, с выражениями F? Некоторые способы сокращения попадания в БД?Django: обновление атрибутов нескольких объектов

# 1st way hits DB twice per object 
def something(): 
    pks = [4, 2, 1, 3, 0] 
    for i in range(len(pks)): 
    mymodel.objects.get(pk=pks[i]).update(attr=i) 

# 2nd way hits DB once per obj and once for the queryset 
def something(): 
    pks = [4, 2, 1, 3, 0] 
    order = Case(*[When(pk=pk, then=pos) for pos, pk in enumerate(pks)]) 
    query = mymodel.objects.filter(pk__in=pks).order_by(order) 
    for i in range(len(query)): 
    query[i].attr = i 
    query[i].save() 

Редактирование для согласования переменных.

ответ

3

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

Следующий запрос будет очень быстрым, независимо от того, сколько записей у вас есть, потому что он извлекает один элемент из первичного ключа. Все РСУБД разработаны так, чтобы справляться с этим.

mymodel.objects.get(pk=a[i]) 

Хауэр вы можете сделать свою функцию более эффективной, как это:

def something(): 
    pks = [4, 2, 1, 3, 0] 
    for i in range(len(a)): 
    mymodel.objects.filter(pk=a[i]).update(attr=i) 

Теперь она попадает в БД только один раз для каждого объекта. Приведенный выше запрос просто переводится как

UPDATE mapp_mymodel SET attr=i where pk=1 
+0

Можете ли вы объяснить разницу между get и filter? Вы говорите, что получить очень быстро, потому что он извлекает элемент, но использует фильтр в вашем коде. Обычно я использовал фильтр, если мне нужно больше одного объекта и получить для одного объекта. –

+0

Разница между get и filter заключается в том, что получение получает объект, а затем делает обновление, как вы обнаружили. Фильтр фактически не извлекает ничего из базы данных, пока вы не возьмете фрагмент из него или не перейдете через него. – e4c5

+0

Я думал, что это ударит по БД 2 раза, потому что один раз, чтобы получить объект, затем еще раз обновить указанный объект. –

0

Если я не ошибаюсь, вы можете сделать что-то вроде

pks = [4, 2, 1, 3, 0] 
mymodel.object.filter(pk__in=pks).update(attr='test') 

Если вы пытаетесь использовать для счетчика цикла в качестве значения атрибута, в котором это не работает.

+0

В соответствии с документом OP. когда pk = i, attr = i (каждый attr отличается), поэтому я добавил, что предостережение от моего ответа. В приведенном выше запросе. attr = 1 для всех экземпляров – e4c5

+0

Думал, что это может быть так (отсюда последняя строка ответа). Но выложил это просто. Ваш ответ кажется более полезным. Спасибо .. – zubhav

+0

Да, я использую индекс как значение для attr. –

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