簡體   English   中英

Django - 在現有 QuerySet 前面添加對象

[英]Django - Add object in front of existing QuerySet

我正在構建一個library Django 應用程序,其中我有一個BooksView來顯示某個Author編寫的所有Book 在某些情況下,我想一個單一的Book之前在查詢集中的所有其他書來顯示。 當圖書的 id 出現在 URL 中時,就會發生這種情況,否則,所有圖書都應按時間順序列出。

書籍/urls.py

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

書籍/views.py

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 如何將對象推送到現有查詢集的前面?

這並不能准確回答您的問題,但我認為它可以讓您獲得所需的結果:不要擔心保留查詢集,而是將其轉換為列表。

這不是您想要為整個代碼庫中的每個查詢集做的事情,尤其是如果它是您以后可能想要構建並與其他過濾器、排除等組合的查詢集。 但是當您即將呈現頁面時(就像你在這里一樣)沒關系,因為無論如何你都會導致查詢集被評估。 你導致它發生的時間可能會提前幾微秒。

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 的子類)無論如何都會在您調用它時列出一個列表:

    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
        ]

https://github.com/encode/django-rest-framework/blob/master/rest_framework/serializers.py#L663

編輯 2

試試這個怎么樣? 通過在查詢集被評估到列表之前添加exclude ,您可以防止 O(n) 掃描列表以查找和刪除應該位於頂部的“主要”書籍。

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)

您可以使用按位| 運營商作為

from django.http.response import Http404


class BooksView(APIView):

    def get(self, request, author, book=None):
        author = get_object_or_404(Author, slug=author)
        book_qs = author.author_books
        if book:
            single_book_qs = Book.objects.filter(id=book) if not single_book_qs.exists(): raise Http404 book_qs = single_book_qs | book_qs

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

請注意,此解決方案的一個警告是,如果您使用order_by(...)方法,位置將根據 ORDER By 表達式進行更改。

更新 1

由於您使用的是order_by(...)表達式,因此您必須執行以下操作,

class BooksView(APIView):

    def get(self, request, author, book=None):
        author = get_object_or_404(Author, slug=author)
        book_qs = author.author_books.order_by("-pub_date")
        serialized_single_book = []
        if book:
            single_book = get_object_or_404(Book, id=book)
            book_qs.exclude(id=book) # to remove dups serialized_single_book = [BooksSerializer(single_book).data]

        serializer = BooksSerializer(book_qs, many=True)
        serialized_book_qs = serializer.data
        return Response([*serialized_single_book, *serialized_book_qs])

應該這樣做,只需使用QuerySet對象中的 union 方法,並從books集中排除我們嘗試訪問的books

我對代碼做了一些小的改動,但你不需要做更多的事情來完成你需要的。

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)

可能有點開箱即用,但如果您想將其保留為查詢集,那么這就是解決方案。 請注意,這解決了X 問題,而不是 Y 問題 目標是將一本書放在列表的最前面,這可以通過插入來實現,也可以通過基於注釋重新排序來實現。

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 執行相同的操作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM