Django: 更新查询集对象的顺序属性

7 投票
2 回答
2879 浏览
提问于 2025-04-16 00:00

我在我的模型里有一个属性,可以让用户对对象进行排序。我需要根据一个包含对象新顺序的ID列表来更新这些对象的顺序;现在我正在遍历整个查询集,一个一个地设置对象的顺序。有没有什么简单又快速的方法,可以对整个查询集一次性完成这个操作呢?

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()

2 个回答

2

我不知道是否可以通过一个查询来做到这一点,但无论如何,这种方法更有效:

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))
6

这个事情不能通过一次查询操作完成。根据我所知道的,就连用原始SQL也做不到。所以你总是需要对每一个需要更新的对象进行单独的更新调用。因此,你和Collin Anderson的解决方案在你的描述中看起来都相当不错。

不过,你的使用场景是什么呢?每次真的会整个列表都变吗?在大多数情况下,这种可能性似乎很小。我能想到一些不同的处理方式。

你像你说的那样保存了顺序字段,但你需要为顺序列表生成一个差异: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次。但这些小操作会分散在时间上进行。

撰写回答