提升Django ORM性能

3
contents = Content.objects.filter(   
    is_excluded=False
).prefetch_related('comments', 'likes', 'dislikes').order_by('-id').all()[:500]
result = [(v, len(v.comments.all())) for v in contents if len(v.likes.all()) -len(v.dislikes.all()) > 24][:7]

我希望返回超过25个赞和最近500个内容的内容。
这个方法可以实现,但是是否有其他方法可以提高性能?

1个回答

4
你可以在数据库端进行筛选。这将防止为.prefetch_related(...)进行额外的查询,但也将限制用于传输数据的带宽
from django.db.models import <b>Count</b>

contents = Content.objects.annotate(
    <b>nlikes=Count('likes')</b>
).filter(
    is_excluded=False,
    <b>nlikes__gt=24</b>
).order_by('-id')[:500]

这样将获得500个主键最高且点赞数超过24的Content
例如,您可以使用以下方法筛选likesdislikes之间的平衡:
from django.db.models import <b>Count, Value</b>
from django.db.models.functions import <b>Coalesce</b>

contents = Content.objects.annotate(
    <b>nlikes=Coalesce(Count('likes', distinct=True), Value(0))-Coalesce(Count('dislikes', distinct=True), Value(0))</b>
).filter(
    is_excluded=False,
    <b>nlikes__gt=24</b>
).order_by('-id')[:500]

这里 distinct=True [Django-doc] 是必要的,因为有两个JOIN,如果没有唯一性过滤器,那么这两个聚合将成为彼此的倍增器

话虽如此,我不确定为什么要有两个相关模型来表示喜欢和不喜欢。为什么不用一个模型(比如 Thumb),用一个值+1代表喜欢,-1代表不喜欢呢?这样建模更简单,因为你可以使用Sum expression [Django-doc]


谢谢。但是这很复杂,因为我使用了预取相关和其他代码。我编辑了我的代码。你能审核一下吗? - HyeongJin Kim
@HyeongJinKim:你仍然可以在此查询集上使用.prefetch_related。但是,对于过滤目的,您不必这样做。 - Willem Van Onsem

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接