我正在构建一个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()),
]books/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?如何将对象推送到现有查询集的前面?
发布于 2020-10-23 13:02:06
这并不能准确地回答您的问题,但我认为它将允许您获得预期的结果:不要担心保存查询集,而是将其转换为列表。
对于整个代码库中的每个查询集,这都不是您想要做的事情,特别是如果它是您以后可能想要构建并与其他过滤器、排除等一起构建和组合的查询集。但是,当您要呈现一个页面(就像您在这里)时,它是可以的,因为无论如何您都要对查询集进行评估。你会让它更快发生--也许是微秒。
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
]编辑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)发布于 2020-10-25 09:59:15
这应该可以做到,只需在QuerySet对象中使用union方法,并将我们试图从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)发布于 2020-10-27 09:17:18
也许有点出格了,但是如果你想把它保持为queryset,那么这就是解决方案。请注意,这解决了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的。
https://stackoverflow.com/questions/64174823
复制相似问题