我可以使用prefetch_related来缓存过滤的查询集吗?

时间:2018-08-16 11:10:15

标签: django django-rest-framework django-queryset

我正在使用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的调用有额外的数据库命中率。

2 个答案:

答案 0 :(得分:2)

您可以在视图级别进行预取,并将Prefetchto_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]

请注意,在某些情况下,不会缓存查询集。