【问题标题】:Can I use prefetch_related to cache filtered querysets?我可以使用 prefetch_related 缓存过滤的查询集吗?
【发布时间】:2019-01-23 07:55:31
【问题描述】:

我正在使用 DRF 序列化一些相关模型。在下面我的玩具示例中,假设每个作者可以拥有一百万本书。显然,对所有“好”书进行数据库查询,然后对所有“坏”书进行另一个数据库查询效率低下。

这篇文章 [http://ses4j.github.io/2015/11/23/optimizing-slow-django-rest-framework-performance/] 提供了一些关于 prefetch_related 的建议。但我发现这仅在我随后调用.books.all() 而不是.books.filter() 时才有帮助,如下面的属性中发生的那样。

在 Django 中是否有任何 自动 方法来缓存书籍查询集,并且 没有 有后续过滤器再次访问数据库?

这里有一些代码:

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

这仍然给我额外的 db hits 以调用filter

【问题讨论】:

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


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    您需要首先评估您的查询集,以便被缓存。来自文档caching and querysets

    所以不是

    return queryset
    

    你可以的

    return [queryset]
    

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

    【讨论】:

      猜你喜欢
      • 2019-03-16
      • 2017-12-26
      • 2021-08-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-09-20
      • 2022-01-14
      • 2015-12-20
      相关资源
      最近更新 更多