首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Django -在现有QuerySet前面添加对象

Django -在现有QuerySet前面添加对象
EN

Stack Overflow用户
提问于 2020-10-02 16:06:35
回答 3查看 1.3K关注 0票数 3

我正在构建一个library Django应用程序,其中我有一个BooksView来显示由某个Author编写的所有Book。在某些情况下,我希望在查询集中的所有其他书籍之前显示单个Book。当图书的id出现在URL中时,就会发生这种情况,否则,所有的书籍都应该按时间顺序列出。

图书/urls.py

代码语言:javascript
复制
urlpatterns = [

    # Books displayed in chronological order
    path("<slug:author>/books", views.BooksView.as_view()),

    # Books displayed in chronological order
    # but first book is the one with id `book`
    path("<slug:author>/books/<int:book>", views.BooksView.as_view()),
]

books/views.py

代码语言:javascript
复制
class BooksView(APIView):

    def get(self, request, author, book=None):

        author = get_object_or_404(Author, slug=author)
        books = author.author_books
        books = books.order_by("-pub_date")

        if book:
            book = get_object_or_404(Book, id=book)
            # TODO: add `book` in front of books QuerySet (and remove duplicated if any)

        serializer = BooksSerializer(books, many=True)
        return Response(serializer.data)

我如何完成我的TODO?如何将对象推送到现有查询集的前面?

EN

回答 3

Stack Overflow用户

发布于 2020-10-23 13:02:06

这并不能准确地回答您的问题,但我认为它将允许您获得预期的结果:不要担心保存查询集,而是将其转换为列表。

对于整个代码库中的每个查询集,这都不是您想要做的事情,特别是如果它是您以后可能想要构建并与其他过滤器、排除等一起构建和组合的查询集。但是,当您要呈现一个页面(就像您在这里)时,它是可以的,因为无论如何您都要对查询集进行评估。你会让它更快发生--也许是微秒。

代码语言:javascript
复制
class BooksView(APIView):

    def get(self, request, author, book=None):

        author = get_object_or_404(Author, slug=author)
        books = list(author.author_books)

        if book:
            book = Book.objects.get(id=book)
            # TODO: add `book` in front of books QuerySet (and remove duplicated if any)
            books = [x for x in books if not x == book] # first remove if dup
            books.insert(0, book) # now insert at front

        serializer = BooksSerializer(books, many=True)
        return Response(serializer.data)

编辑1

BooksSerializer (我怀疑它是BaseSerializer的子类)将在您调用它时立即列出一个列表:

代码语言:javascript
复制
    def to_representation(self, data):
        """
        List of object instances -> List of dicts of primitive datatypes.
        """
        # Dealing with nested relationships, data can be a Manager,
        # so, first get a queryset from the Manager if needed
        iterable = data.all() if isinstance(data, models.Manager) else data

        return [
            self.child.to_representation(item) for item in iterable
        ]

框架/序列化器.#L 663

编辑2

不如试试这个吧?通过在计算查询集之前将exclude添加到列表中,可以防止O(n)扫描到列表中,以查找和删除应该位于顶部的“主”书。

代码语言:javascript
复制
class BooksView(APIView):

    def get(self, request, author, book=None):

        author = get_object_or_404(Author, slug=author)
        books = author.author_books

        if book:
            book = Book.objects.get(id=book)
            # TODO: add `book` in front of books QuerySet (and remove duplicated if any)
            # ensure the queryset doesn't include the "main" book
            books = books.exclude(book_id=book.id)
            # evaluate it into a list just like the BookSerializer will anyhow
            books = list(books)
            # now insert the "main" book at the front of the list 
            books.insert(0, book)

        serializer = BooksSerializer(books, many=True)
        return Response(serializer.data)
票数 1
EN

Stack Overflow用户

发布于 2020-10-25 09:59:15

这应该可以做到,只需在QuerySet对象中使用union方法,并将我们试图从books查询集中访问的书籍排除在外。

我对代码做了一些小的修改,但是您不需要做更多的事情来完成您需要的事情。

代码语言:javascript
复制
class BooksView(APIView):

    def get(self, request, author, book=None):
        author = get_object_or_404(Author, slug=author)
        books = author.author_books.all().order_by('-pub_date')

        if book:
            # I am assuming this line if for validating that the request is valid.
            # I've changed the query to also include author's slug,
            # so the request doesn't get books not related to the author.
            book_obj = get_object_or_404(Book, id=book, author__slug=author)

            # Just Query the same book, and union it with the books queryset,
            # excluding the current book in the book_obj
            books = Book.objects.filter(id=book_obj.id).union(books.exclude(id=book_obj.id))

        serializer = BooksSerializer(books, many=True)
        return Response(serializer.data)
票数 1
EN

Stack Overflow用户

发布于 2020-10-27 09:17:18

也许有点出格了,但是如果你想把它保持为queryset,那么这就是解决方案。请注意,这解决了X问题,而不是Y问题。其目标是将一本书放在列表的第一位,这可以通过插入实现,也可以通过基于注释的重新排序来实现。

代码语言:javascript
复制
from django.db.models import Case, When, Value, IntegerField
from rest_framework import generics

from . import models, serializers


class BooksPerAuthor(generics.ListAPIView):
    serializer_class = serializers.BookWithoutAuthor

    def get_queryset(self):
        book_pk = self.kwargs.get("book", 0)
        queryset = (
            models.Book.objects.filter(author__slug=self.kwargs["author"])
            .annotate(
                promoted=Case(
                    When(pk=book_pk, then=Value(1)),
                    default=Value(0),
                    output_field=IntegerField(),
                )
            )
            .order_by("-promoted", "-pub_date")
        )
        return queryset

这不会捕捉到不存在的引用--不喜欢返回404作为列表视图,而且如果推广的书籍不在集合中,那么就没有真正的危害了。上面的许多例子都是以额外查询的(较小的)代价来处理404的。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/64174823

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档