如何基于Serializer计算的小时范围过滤Django QuerySet?
现有如下
MyModelSerializer,通过SerializerMethodField计算hour字段(当前时间与created_at的时间差,以小时为单位):class MyModelSerializer(serailizers.ModelSerializer): hour = serializers.SerializerMethodField() def get_hour(self, obj): created_at = obj.created_at # datetime now = datetime.now() return (now - datetime).total_seconds() // 3600 class Meta: model = MyModel fields = "__all__"需要实现三个静态过滤功能:
- 过滤出
hour≤3的列表- 过滤出
hour在3-12之间的列表- 过滤出
hour>12的列表
当前过滤方式仅能匹配精确小时值,无法满足需求。期望通过如下参数实现对应过滤:upto_3h = request.GET.get("upto_3hr", False) if upto_3h: # filter which has hour less or equal to 3h between_3_to_12 = request.GET.get("between_3_to_12", False) if between_3_to_12: # filter accordingly above_12 = request.GET.get("above_12", False) if above_12: # filter accordingly请问如何实现上述基于小时范围的Django QuerySet过滤?
解决方案
第一步:修正Serializer的时间计算错误
原Serializer的get_hour方法存在两处问题:计算时间差时误用变量、未使用Django时区感知时间,修正后代码如下:
from django.utils import timezone from rest_framework import serializers from .models import MyModel class MyModelSerializer(serializers.ModelSerializer): hour = serializers.SerializerMethodField() def get_hour(self, obj): created_at = obj.created_at now = timezone.now() # 计算时间差并转为小时(向下取整) return (now - created_at).total_seconds() // 3600 class Meta: model = MyModel fields = "__all__"
第二步:在视图层实现QuerySet范围过滤
核心是直接通过数据库层面的时间比较完成过滤,避免先序列化再过滤的性能损耗,同时保证范围判断准确。注意request.GET获取的参数为字符串,需处理有效值判断:
from django.utils import timezone from rest_framework.views import APIView from rest_framework.response import Response from .models import MyModel from .serializers import MyModelSerializer from datetime import timedelta class MyModelListView(APIView): def get(self, request): queryset = MyModel.objects.all() # 过滤hour ≤3的记录(创建时间在3小时内) upto_3h = request.GET.get("upto_3hr", "").lower() if upto_3h in ('true', '1'): three_hours_ago = timezone.now() - timedelta(hours=3) queryset = queryset.filter(created_at__gte=three_hours_ago) # 过滤hour在3-12之间的记录(创建时间在3-12小时前) between_3_to_12 = request.GET.get("between_3_to_12", "").lower() if between_3_to_12 in ('true', '1'): twelve_hours_ago = timezone.now() - timedelta(hours=12) three_hours_ago = timezone.now() - timedelta(hours=3) queryset = queryset.filter(created_at__gte=twelve_hours_ago, created_at__lt=three_hours_ago) # 过滤hour >12的记录(创建时间超过12小时) above_12 = request.GET.get("above_12", "").lower() if above_12 in ('true', '1'): twelve_hours_ago = timezone.now() - timedelta(hours=12) queryset = queryset.filter(created_at__lt=twelve_hours_ago) serializer = MyModelSerializer(queryset, many=True) return Response(serializer.data)
补充说明
- 数据库层面过滤能减少数据查询量,比序列化后再过滤效率更高;
- 若需支持多参数同时生效,可根据业务需求调整逻辑(比如添加参数互斥判断,或合并过滤条件)。
内容的提问来源于stack exchange,提问作者Mark
相关产品推荐
相关产品推荐

