У меня есть атрибут на моей модели, чтобы позволить пользователю заказывать объекты. Я должен обновить порядок элемента в зависимости от списка, который содержит идентификаторы объекта в новом порядке; прямо сейчас я выполняю итерации по целому queryset и устанавливаю, каждый возражает после другого. Каков был бы самый легкий/самый быстрый способ сделать то же с целым queryset?
def update_ordering(model, order):
""" order is in the form [id,id,id,id] for example: [8,4,5,1,3] """
id_to_order = dict((order[i], i) for i in range(len(order)))
for x in model.objects.all():
x.order = id_to_order[x.id]
x.save()
Это невозможно сделать за одну операцию с набором запросов. Насколько я знаю, это невозможно сделать даже в одном запросе с сырым SQL. Таким образом, вам всегда будет нужен вызов обновления для каждого объекта, который необходимо обновить. Таким образом, и ваше решение, и решения Коллина Андерсона кажутся вполне оптимальными для вашего описания.
Однако каков ваш вариант использования? Неужели весь список будет меняться каждый раз? В большинстве случаев это кажется маловероятным. Я вижу несколько разных подходов.
Вы сохраняете поле порядка, как вы говорите, но генерируете различие для списка заказов: def
update_ordering(model, order):
""" order is in the form [id,id,id,id] for example: [8,4,5,1,3] """
original_order = model.objects.value_list('id', flat=True).order_by('order')
order = filter( lambda x: x[1]!=x[2], zip(xrange(len(order)), order, original_order))
for i in order:
model.objects.filter(id=i[1]).update(order=i[0])
Другой подход, в зависимости от того, что вы делаете, - это выполнить частичное обновление (например, с использованием AJAX), если это возможно, вместо того, чтобы обновлять весь переупорядоченный набор, просто обновляйте каждое обновление отдельно. Это часто увеличивает общую нагрузку, но со временем распределяет ее еще больше. Возьмем, к примеру, пошаговое перемещение 5-го элемента на место 2, это приведет к 3 заменам: (5,4); (4,3); (3,2). В результате будет 6 обновлений, тогда как при универсальном подходе потребуется всего 4. Но небольшие операции со временем будут разнесены.
Я не знаю, можно ли это сделать с помощью одного запроса, но это в любом случае эффективнее:
def update_ordering(model, order):
""" order is in the form [id,id,id,id] for example: [8,4,5,1,3] """
for id in model.objects.values_list('id', flat=True):
model.objects.filter(id=id).update(order=order.index(id))