Как вставить элемент в Queryset, отсортированный по числовому полю, и увеличить значение этого поля для всех последующих элементов

Допустим, есть модель Django под названием TaskModel, которая имеет поле priority, и мы хотим вставить новый элемент и увеличить существующий элемент, который уже имеет приоритет, а также увеличить приоритет следующих элементов.

priority - это просто числовое поле без каких-либо специальных флагов, таких как уникальный или первичный/собственный ключ

queryset = models.TaskModel.objects.filter().order_by('priority')

Можно ли это сделать умным способом с помощью некоторых методов на самом Queryset?

Я считаю, что это можно сделать, используя выражения Django F и переопределив метод сохранения модели. Думаю, вы можете переопределить метод модели __init__, как в этом примере answer, но я думаю, что лучше использовать метод сохранения.

class TaskModel(models.Model):
    task = models.CharField(max_length=20)
    priority = models.IntegerField()
    
    # Override the save method so whenever a new TaskModel object is
    # added, this will be run.
    def save(self, *args, **kwargs):
    
        # First get all TaskModels with priority greater than, or
        # equal to the priority of the new task you are adding
        queryset = TaskModel.objects.filter(priority__gte=self.priority)

        # Use update with the F expression to increase the priorities
        # of all the tasks above the one you're adding
        queryset.update(priority=F('priority') + 1)

        # Finally, call the super method to call the model's
        # actual save() method
        super(TaskModel, self).save(*args, **kwargs)

    def __str__(self):
        return self.task

Помните, что это может создать пробелы в приоритетах. Например, что если вы создадите задачу с приоритетом 5, затем удалите ее, а затем добавите другую задачу с приоритетом 5? Я думаю, что единственным способом справиться с этим будет циклический просмотр набора запросов, возможно, с помощью функции, подобной приведенной ниже, в вашем представлении, и вызов ее всякий раз, когда создается новая задача или изменяется ее приоритет:

# tasks would be the queryset of all tasks, i.e, TaskModels.objects.all()
def reorder_tasks(tasks):
    for i, task in enumerate(tasks):
        task.priority = i + 1
        task.save()

Этот метод не так эффективен, но он не создаст пробелов. Для этого метода модель TaskModel можно вообще не изменять.

Ор возможно, вы также можете переопределить метод delete модели TaskModel, как показано в этом ответе, но у меня еще не было возможности проверить это.

Вернуться на верх