Django中HTMLCalendar查找最近事件日期并高亮的问题排查
问题排查与修复方案
现有代码存在的核心问题
- 类方法缩进错误:
formatday、formatweek、formatmonth三个方法没有缩进进Calendar类内部,属于全局函数,调用时会直接报错 - 高亮逻辑完全错误:每次渲染日期都查询全局最近的未来事件,但没有判断当前渲染的日期是否是该事件的日期,导致所有带事件的日期都会被标红
- 重复查询严重:每次渲染日期都重复查询数据库,性能损耗极大,且查询未来事件时没有按时间排序,
first()返回的不一定是最近的事件 - HTML结构错误:
get_html_url属性只输出了<a>标签的开头,没有事件标题和闭合标签,最终生成的HTML存在语法错误 - 边界处理缺失:
__init__方法中year默认值为None,查询时会报错;使用datetime.now()没有考虑时区问题,可能出现日期匹配偏差 - 筛选逻辑矛盾:需求里明确用
__gte筛选包含今日的事件,实际代码用的__gt会过滤掉今日未到时间的有效事件
修复后代码
Model部分
from django.db import models from django.urls import reverse from django.utils import timezone class Events(models.Model): title = models.CharField(max_length=100) slug = models.SlugField() start_time = models.DateTimeField() end_time = models.DateTimeField() def __str__(self): return self.title @property def get_html_url(self): url = reverse('cal:events', args=(self.slug,)) # 补全事件标题和a标签闭合逻辑 return f'<a href="{url}">{self.title}</a>'
Calendar类部分
from calendar import HTMLCalendar from django.utils import timezone from .models import Events class Calendar(HTMLCalendar): def __init__(self, year=None, month=None): now = timezone.now() self.year = year if year else now.year self.month = month if month else now.month # 初始化时仅查询一次最近的未来事件,避免重复查询数据库 self.nearest_event = Events.objects.filter( start_time__gte=now ).order_by('start_time').first() super().__init__() # 格式化单日<td> def formatday(self, day, events): events_per_day = events.filter(start_time__day=day) d = '' if events_per_day.exists(): for event in events_per_day: d += f'{event.get_html_url}<br>' if day != 0: # 仅当当前日期等于最近事件的日期时才标红 if self.nearest_event and self.nearest_event.start_time.day == day \ and self.nearest_event.start_time.month == self.month \ and self.nearest_event.start_time.year == self.year: return f"<td>{d}<span style='color:red;' class='date'>{day}</span></td>" else: return f"<td>{d}<span style='color:aliceblue;' class='date'>{day}</span></td>" return '<td></td>' else: if day != 0: return f"<td><b><span class='date'>{day}</span></b></td>" return '<td></td>' # 格式化单周<tr> def formatweek(self, theweek, events): week = '' for d, weekday in theweek: week += self.formatday(d, events) return f'<tr> {week} </tr>' # 格式化整月日历 def formatmonth(self, withyear=True): events = Events.objects.filter(start_time__year=self.year, start_time__month=self.month) cal = f'<table border="0" cellpadding="0" cellspacing="0" class="calendar">\n' cal += f'{self.formatmonthname(self.year, self.month, withyear=withyear)}\n' cal += f'{self.formatweekheader()}\n' for week in self.monthdays2calendar(self.year, self.month): cal += f'{self.formatweek(week, events)}\n' return cal
内容的提问来源于stack exchange,提问作者Nick Vazovsky
相关产品推荐
相关产品推荐

