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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.09 18:18:03