Django - 使用unique_together =(ForeignKey,IntegerField)重新排序(更新)对象

时间:2016-06-10 11:56:33

标签: python django orm

我有一个代表Session的模型(比如一小时一班的小孩),它有许多必须跟随订单的关联Task s。更容易理解:

class Task(models.Model):
    (...)
    order = models.PositiveIntegerField('offset from the beginning of a Session')
    session = models.ForeignKey(Session, related_name='tasks')

    unique_together = ('session', 'order')
    ordering = ('order', )

假设我想要更新单个会话order(想象一下常见的拖放重新排序)。因此,通过更新一个任务的顺序,我需要重新排序其余的任务,以便唯一性不会制动。

图形示例(让我们说我希望pk=5014的任务位于第二个位置(order=1))

Initial state:                    Final state:

| pk   | order | name |           | pk   | order | name |
|------|-------|------|           |------|-------|------|
| 5011 | 0     | A    |           | 5011 | 0     | A    |
| 5012 | 1     | B    |   ---->   | 5014 | 1     | D    |
| 5013 | 2     | C    |           | 5012 | 2     | B    |
| 5014 | 3     | D    |           | 5013 | 3     | C    |

我的问题是如何使用Django ORM更新它(如果可能的话)或者这是一种优雅的方式,因为我只能想到用新订单保存所需的任务(比如订单= 50),然后重新排列其余任务,最后将所需顺序重新分配给所需任务。

是否有类似bulk_update的内容(经过长时间的研究后我没有发现任何相似内容),这样我就可以修改所有任务的order字段,然后立即保存所有内容?或者我必须自己照顾这个unique_together吗?

2 个答案:

答案 0 :(得分:2)

通常最好重用现有的解决方案:

https://github.com/bfirsh/django-ordered-model

虽然它仍然按顺序归结为2次保存:

https://github.com/bfirsh/django-ordered-model/blob/master/ordered_model/models.py#L122-L123

另见:

https://www.djangopackages.com/grids/g/model-ordering/

最好的办法是编写一个自定义SQL查询,类似于

https://dba.stackexchange.com/questions/131118/how-can-i-swap-two-values-from-particular-column-in-a-table-in-postgres

将DB约束作为示例中的unique_together也是个好主意。

答案 1 :(得分:0)

我会发布我开发的代码作为我的问题的解决方案;虽然我不是很开心(我认为必须以某种方式提供更好的解决方案),但万一它可以帮助某人。

我为模型Task

定义了此方法
def update_order(self, new_order):
    """
    Update the order of this task and reorder the rest of tasks
    of the session accordingly
    """
    current_order = self.order
    tasks = Task.objects.filter(session=self.session)
    current_highest_order = tasks.last().order
    if new_order > current_highest_order or new_order < 0:
        raise ValidationError(_('New order out of range'))
    operation = -1 if new_order < current_order else +1
    # set temporary order higher than any other to allow reordering the rest
    self.order = current_highest_order + 1
    self.save()
    # reassign the rest of tasks' order accordingly
    tasks = Task.objects.filter(session=self.session)
    for i in range(current_order, new_order, operation):
        task = tasks[i + operation] if operation == -1 else tasks[i]
        task.order -= operation
        task.save()
    # Restore a proper order value (the desired new_order) to this task
    self.order = new_order
    self.save()

任何评论都会非常感激。