我正在使用DRF序列化一些相关模型。在下面的玩具示例中,假设每个作者可以有一百万本书。显然,对所有“好”书进行数据库查询,然后对所有“坏”书进行另一个数据库查询效率低下。
这篇文章[http://ses4j.github.io/2015/11/23/optimizing-slow-django-rest-framework-performance/]提供了有关prefetch_related
的一些建议。但是我发现,只有当我随后调用.books.all()
而不是下面的属性中的.books.filter()
时,这才有帮助。
Django中是否有任何 automatic 方法来缓存图书queryset,并且 没有后续的过滤器使其再次命中数据库?
以下是一些代码:
models.py:
class Author(models.Model):
name = models.CharField(max_length=100)
@property
def good_books(self):
return self.books.filter(is_good=True)
@property
def bad_books(self):
return self.books.filter(is_good=False)
class Book(models.Model):
title = models.CharField(max_length=100)
is_good = models.BooleanField(default=False)
author = models.ForeignKey(Author, related_name="books")
serializers.py:
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = ("title",)
class AuthorSerializer(serializers.ModelSerializer):
class Meta:
model = Author
fields = ("name", "good_books", "bad_books",)
good_books = BookSerializer(many=True, read_only=True, source="good_books")
bad_books = BookSerializer(many=True, read_only=True, source="bad_books")
@staticmethod
def setup_eager_loading(queryset):
queryset = queryset.prefetch_related("books")
return queryset
views.py:
class AuthorViewSet(viewsets.ReadOnlyModelViewSet):
serializer = AuthorSerializer
def get_queryset(self):
queryset = Author.objects.all()
queryset = self.get_serializer_class().setup_eager_loading(queryset)
return queryset
谢谢。
编辑:
使用Prefetch
:
@staticmethod
def setup_eager_loading(queryset):
queryset = queryset.prefetch_related(
Prefetch("books", queryset=Book.objects.filter(is_good=True), to_attr="good_books"),
Prefetch("books", queryset=Book.objects.filter(is_good=False), to_attr="bad_books"),
)
return queryset
这仍然使我对filter
的调用有额外的数据库命中率。
答案 0 :(得分:2)
您可以在视图级别进行预取,并将Prefetch
与to_attr
参数一起使用:
class AuthorViewSet(viewsets.ReadOnlyModelViewSet):
serializer = AuthorSerializer
def get_queryset(self):
queryset = Author.objects.prefetch_related(
Prefetch('books', queryset=Book.objects.filter(is_good=True), to_attr='good_books'),
Prefetch('books', queryset=Book.objects.filter(is_good=False), to_attr='bad_books')
)
return queryset
答案 1 :(得分:0)
您需要先评估您的查询集,才能进行缓存。来自文档caching and querysets
所以不是
return queryset
你可以做
return [queryset]
请注意,在某些情况下,不会缓存查询集。