如何正确地将任意位置的对象插入到现有查询集中并按顺序更新顺序?

How to properly insert objects at an arbitrary position to an existing query set and update order sequentially?

我有两个模型 BookChapters 具有外键关系。

我正在尝试 INSERT Chapter 在按 order 字段排序的现有章节 QuerySet 之间,但在以下方面遇到问题:

  1. INSERT 之后按顺序维护 order
  2. 当切片操作变为负数时处理切片操作(QuerySet 不允许使用负索引)。
  3. 处理非顺序订单。

示例:

假设我已经有了第 1 到第 10 章。现在我需要在第 1 和第 2 章之间添加 新章节。所以 新章节 应该取代数据库中当前的 第 2 章 。所以 2 之后的所有章节(包括它本身)都应该有 order + 1order 来为新章节腾出空间。

我的代码:

models.py

class Book(PubDateUpdDateMixin, models.Model):
    title = models.CharField(max_length=80)
    slug = models.SlugField(unique=True, blank=True)
    description = models.CharField(max_length=3000)
    author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='authors')

class chapter(PubDateUpdDateMixin, models.Model):
    book = models.ForeignKey(Book, on_delete=models.CASCADE, related_name='chapters')
    title = models.CharField(max_length=40)
    content = models.TextField(max_length=30000)
    slug = models.SlugField(unique=True, blank=True)
    order = models.IntegerField(null=True)

views.py

class ChapterCreate(LoginRequiredMixin, CreateView):
    login_url = reverse_lazy('login')
    model = Chapter
    slug_url_kwarg = 'book_slug'
    form_class = ChapterForm
    template_name_suffix = '-create'

    def get_object(self):
        return Book.objects.get(slug=self.kwargs['book_slug'])

    def get_context_data(self, **kwargs):
        context = super(ChapterCreate, self).get_context_data(**kwargs)
        context['book'] = Book.objects.get(slug=self.kwargs['book_slug'])
        return context

    def form_valid(self, form):
        book = self.get_object()
        chapter = form.save(commit=False)
        chapter.book = book
        requested_order = form.cleaned_data['order']

        if requested_order == chapter.order:
            '''
            Reverse the queryset and slice it up to the requested order. 
            Add 1 to the chapter orders (chapter.order + 1).
            Save them to create a slot for the new chapter.
            '''
            chapters = course.chapters.order_by("-order")
            for l in chapters[:((len(chapters)+1)-requested_order)]:
                l.order += 1
                print(l.order)
                l.save()
        try:
            chapter.save()
        except IntegrityError as e:
            raise IntegrityError("Error: {}".format(e))

        return super(ChapterCreate, self).form_valid(form)

    def dispatch(self, *args, **kwargs):
        book = self.get_object()
        if not self.request.user.is_authenticated:
            messages.info(self.request, 'You need to be logged in to edit a chapter.')
        elif book.author != self.request.user:
            raise PermissionDenied
        return super(ChapterCreate, self).dispatch(*args, **kwargs)

这适用于顺序排序但不适用于

  1. 当切片索引变为负数时。
  2. 非顺序排序(当中间有缺失或删除的章节时)。

我错过了什么?有更好的方法吗?

如果顺序是连续的,你可以使用这样的东西:

# Increment all chapter orders greater or equal to the requested order:
book.chapters.filter(order__gte=requested_order).update(order=F("order") + 1)

# Now just set the order directly on the object:
chapter.order = requested_order
chapter.save()

Documentation for F expressions.

看起来这对你不起作用,你可能需要一章一章地更新:

for chapter_to_update in book.chapters.filter(
    order__gte=requested_order
).order_by('-order'):
    chapter_to_update.order += 1
    chapter_to_update.save()

chapter.order = requested_order
chapter.save()

如果你的书有很多章节,这会比较慢。