You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django Rest Framework含日期注解查询集响应过慢问题求助

问题:DRF中注解加日期过滤后响应超时,关联预加载引发性能问题

我在使用Django Rest Framework时,需要筛选出关联项数量大于0的EventVenue对象,但在annotate的Count过滤器中加入日期时间条件后,响应时间从毫秒级骤增至约90秒。

耗时代码

class EventVenueViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = EventVenue.objects.prefetch_related("eventblock_set__eventslot_set__event").annotate(
            num_slots=models.Count(
                "eventblock__eventslot",
                filter=Q(eventblock__eventslot__event=None) & Q(
                    eventblock__eventslot__start_time__gt=timezone.now()
                    + timedelta(days=2)
                )
            )
        ).filter(num_slots__gt=0)

移除日期过滤后的正常代码

class EventVenueViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = EventVenue.objects.prefetch_related("eventblock_set__eventslot_set__event").annotate(
            num_slots=models.Count(
                "eventblock__eventslot",
                filter=Q(eventblock__eventslot__event=None)
            )
        ).filter(num_slots__gt=0)

排查发现:移除prefetch_related("eventblock_set__eventslot_set__event")后,即使保留日期过滤响应也正常,但业务上需要这个预加载来返回关联对象,陷入两难。


解决思路

1. 拆分查询逻辑:先筛选再预加载

将注解筛选和关联预加载拆分为两步执行,避免两者逻辑冲突导致的性能损耗:

class EventVenueViewSet(viewsets.ReadOnlyModelViewSet):
    def get_queryset(self):
        # 第一步:筛选符合条件的场地
        filtered_venues = EventVenue.objects.annotate(
            num_slots=models.Count(
                "eventblock__eventslot",
                filter=Q(eventblock__eventslot__event=None) & Q(
                    eventblock__eventslot__start_time__gt=timezone.now() + timedelta(days=2)
                )
            )
        ).filter(num_slots__gt=0)
        # 第二步:对筛选结果执行预加载
        return filtered_venues.prefetch_related("eventblock_set__eventslot_set__event")

2. 优化数据库索引

给eventslot表的start_time、event_id、eventblock_id字段建立联合索引,让日期过滤的查询更快命中数据,减少全表扫描的开销。

3. 自定义Prefetch对象,缩小预加载范围

不要预加载所有关联数据,而是用Prefetch对象给预加载的查询集加上过滤条件,只加载业务需要的关联数据:

from django.db.models import Prefetch

class EventVenueViewSet(viewsets.ReadOnlyModelViewSet):
    def get_queryset(self):
        future_time = timezone.now() + timedelta(days=2)
        # 预加载符合日期条件的EventSlot及关联Event
        filtered_slots = EventSlot.objects.filter(
            event=None,
            start_time__gt=future_time
        ).select_related("event")
        # 预加载包含筛选后EventSlot的EventBlock
        filtered_blocks = EventBlock.objects.prefetch_related(
            Prefetch("eventslot_set", queryset=filtered_slots)
        )
        # 先筛选场地,再预加载过滤后的关联数据
        return EventVenue.objects.annotate(
            num_slots=models.Count(
                "eventblock__eventslot",
                filter=Q(eventblock__eventslot__event=None) & Q(
                    eventblock__eventslot__start_time__gt=future_time
                )
            )
        ).filter(num_slots__gt=0).prefetch_related(
            Prefetch("eventblock_set", queryset=filtered_blocks)
        )

4. 检查SQL执行计划

通过print(queryset.query)输出实际执行的SQL语句,查看是否存在不合理的JOIN顺序或全表扫描,针对性调整查询结构。


内容的提问来源于stack exchange,提问作者bodger

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 18:07:23