Django REST Framework中KeyError问题排查及预期输出实现
Django REST Framework 日历事件接口KeyError排查与分组实现
问题背景
开发事件日历列表接口时出现KeyError错误,需实现按日期分组的事件列表输出,相关代码及错误如下:
原代码文件
Views.py
class EventCalenderEventListView(ListAPIView): permission_classes=[AllowAny,] serializer_class = serializer.EventCalenderSerializer def get_queryset(self, *args, **kwargs): data = json.loads(self.request.GET.get('filter')) category = data.get('category') query_list = Event.objects.all().values('start_date').distinct() date_list = [] for i in query_list: for key, value in i.items(): date_list.append(datetime.datetime.strftime(value,'%Y-%m-%d')) datas = [] result = {} for dates in date_list: result[dates]= [] query = Event.objects.filter(start_date=dates).order_by('start_date') for i in query: result[dates].append(i) datas.append(result) result = {} return datas
Models.py
class Event(BaseFields): name = models.CharField(max_length=250) category = models.ForeignKey(EventCategory,on_delete=models.CASCADE,null=True,blank=True) other_event_category = models.CharField(max_length=250,null=True,blank=True) meeting_link = models.CharField(max_length=250,null=True,blank=True) description = models.TextField(null=True,blank=True) start_date = models.DateField(null=True,blank=True) end_date = models.DateField(null=True,blank=True)
Serializer.py
class EventCalenderSerializer(ModelSerializer): category = SerializerMethodField() status = SerializerMethodField() class Meta: model = Event fields = fields=('id','name','category','start_date','end_date','status') def get_category(self, obj): category = {} if obj.category: category['id'] = obj.category.id category['name'] = obj.category.name return category else: category = "" return category def get_status(self,obj): current_date = datetime.strftime(datetime.today(),'%Y-%m-%d') start_date = datetime.strftime(obj.start_date,'%Y-%m-%d') if obj.end_date is not None: end_date = datetime.strftime(obj.end_date,'%Y-%m-%d') if start_date == end_date and start_date < current_date: return "completed" elif start_date < current_date and end_date < current_date: return "completed" elif start_date < current_date and end_date > current_date: return "progress" elif start_date == current_date: return "progress" else: return "upcoming" else: if start_date < current_date: return "completed" elif start_date == current_date: return "progress" else: return "upcomng"
错误说明
KeyError触发原因:视图get_queryset返回字典列表而非DRF要求的QuerySet对象,序列化器尝试按模型字段解析非模型数据时,无法匹配对应键值。
预期输出
[ { "2023-06-01": [ { "name": "event 1", "start_date": "2023-06-01", "end_date": "2023-06-07" } ] }, { "2023-06-02": [ { "name": "event 2", "start_date": "2023-06-02", "end_date": "2023-06-07" }, { "name": "event 3", "start_date": "2023-06-02", "end_date": "2023-06-07" } ] } ]
错误原因分析
- 视图返回数据类型错误:
ListAPIView要求get_queryset返回QuerySet,直接返回字典列表会导致序列化逻辑混乱,触发KeyError。 - 日期匹配类型不兼容:用字符串格式的日期筛选DateField类型的
start_date,可能导致查询失败,后续字典操作出错。 - 冗余查询性能低下:循环中多次执行数据库查询,效率极低且易引发数据不一致。
- 序列化器拼写错误:
get_status方法中返回的"upcomng"是拼写错误,应为"upcoming"。
修复方案与代码
核心调整
- 放弃
ListAPIView的默认序列化流程,重写get方法直接构造自定义响应。 - 一次性查询所有事件,在内存中按日期分组,减少数据库查询次数。
- 统一日期类型转换,避免字符串与DateField的匹配错误。
- 修正序列化器拼写错误。
修正后代码
Views.py
from django.http import JsonResponse from rest_framework.views import APIView from rest_framework.permissions import AllowAny import json from datetime import datetime from .models import Event from .serializer import EventCalenderSerializer class EventCalenderEventListView(APIView): permission_classes = [AllowAny,] def get(self, request, *args, **kwargs): # 处理筛选参数 filter_data = request.GET.get('filter') category = None if filter_data: try: data = json.loads(filter_data) category = data.get('category') except json.JSONDecodeError: pass # 查询所有事件,支持分类筛选 queryset = Event.objects.all().order_by('start_date') if category: queryset = queryset.filter(category__id=category) # 按日期分组 date_groups = {} for event in queryset: if not event.start_date: continue # 跳过无开始日期的事件 date_str = event.start_date.strftime('%Y-%m-%d') if date_str not in date_groups: date_groups[date_str] = [] # 序列化单个事件 serializer = EventCalenderSerializer(event) date_groups[date_str].append(serializer.data) # 转换为预期的列表格式 result = [{date: events} for date, events in date_groups.items()] return JsonResponse(result, safe=False)
Serializer.py
from rest_framework import serializers from .models import Event from datetime import datetime class EventCalenderSerializer(serializers.ModelSerializer): category = serializers.SerializerMethodField() status = serializers.SerializerMethodField() class Meta: model = Event fields = ('id','name','category','start_date','end_date','status') def get_category(self, obj): if obj.category: return {'id': obj.category.id, 'name': obj.category.name} return "" def get_status(self, obj): current_date = datetime.today().date() start_date = obj.start_date if not start_date: return "unknown" if obj.end_date: end_date = obj.end_date if start_date == end_date and start_date < current_date: return "completed" elif start_date < current_date and end_date < current_date: return "completed" elif start_date < current_date and end_date > current_date: return "progress" elif start_date == current_date: return "progress" else: return "upcoming" else: if start_date < current_date: return "completed" elif start_date == current_date: return "progress" else: return "upcoming" # 修正拼写错误
验证结果
修复后接口将返回符合预期的按日期分组的事件列表,且无KeyError错误。
内容的提问来源于stack exchange,提问作者Jerin Raju
相关产品推荐
相关产品推荐

