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

如何在Django REST Framework中实现生成校历PDF的后端接口?

在Django REST Framework中实现学校日历PDF生成接口

一、选择PDF生成库

推荐两个主流工具,按需选择:

  • ReportLab:适合纯代码控制PDF排版,对结构化表格(比如日历)的样式调整更灵活。
  • WeasyPrint:支持通过HTML/CSS渲染PDF,如果你熟悉前端排版,用这个可以快速复用现有页面样式。

先安装对应依赖:

# 安装ReportLab
pip install reportlab

# 或安装WeasyPrint
pip install weasyprint

二、DRF接口实现步骤

1. 编写视图类

以下提供两种实现方式的完整代码示例:

方式一:用ReportLab生成日历PDF

from rest_framework.views import APIView
from rest_framework.response import Response
from reportlab.lib import colors
from reportlab.lib.pagesizes import letter
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph
from reportlab.lib.styles import getSampleStyleSheet
from io import BytesIO
from datetime import datetime, timedelta

class SchoolCalendarPDFView(APIView):
    def get(self, request):
        # 1. 生成目标月份的日历数据(示例:当前月份)
        today = datetime.today()
        year, month = today.year, today.month
        first_day = today.replace(day=1)
        last_day = (first_day + timedelta(days=32)).replace(day=1) - timedelta(days=1)
        
        # 构建日历表头与行数据
        headers = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]
        calendar_rows = [headers]
        
        current_day = first_day
        first_weekday = first_day.weekday()  # 0=周一,6=周日
        week_days = [""] * first_weekday  # 补全当月第一天前的空白
        
        while current_day <= last_day:
            week_days.append(str(current_day.day))
            # 周日换行,开启新的一周
            if current_day.weekday() == 6:
                calendar_rows.append(week_days)
                week_days = []
            current_day += timedelta(days=1)
        
        # 补全最后一行的空白
        if week_days:
            week_days += [""] * (7 - len(week_days))
            calendar_rows.append(week_days)
        
        # 2. 生成PDF内容
        buffer = BytesIO()
        doc = SimpleDocTemplate(buffer, pagesize=letter)
        elements = []
        
        # 添加标题
        styles = getSampleStyleSheet()
        title = Paragraph(f"{year}年{month}月学校日历", styles["Title"])
        elements.append(title)
        
        # 添加日历表格并设置样式
        table = Table(calendar_rows)
        table_style = TableStyle([
            ("BACKGROUND", (0,0), (-1,0), colors.darkgrey),
            ("TEXTCOLOR", (0,0), (-1,0), colors.whitesmoke),
            ("ALIGN", (0,0), (-1,-1), "CENTER"),
            ("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
            ("BOTTOMPADDING", (0,0), (-1,0), 12),
            ("BACKGROUND", (0,1), (-1,-1), colors.lightblue),
            ("GRID", (0,0), (-1,-1), 1, colors.black),
        ])
        table.setStyle(table_style)
        elements.append(table)
        
        # 构建PDF文件
        doc.build(elements)
        buffer.seek(0)
        
        # 3. 返回PDF响应
        response = Response(buffer.getvalue(), content_type="application/pdf")
        response["Content-Disposition"] = f'attachment; filename="school_calendar_{year}_{month}.pdf"'
        return response

方式二:用WeasyPrint生成(基于HTML模板)

首先创建HTML模板文件templates/calendar_template.html:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>{{ year }}年{{ month }}月学校日历</title>
    <style>
        .title {
            text-align: center;
            font-size: 24px;
            margin: 20px 0;
        }
        table {
            width: 100%;
            border-collapse: collapse;
        }
        th {
            background-color: #555;
            color: white;
            padding: 10px;
            text-align: center;
        }
        td {
            border: 1px solid #000;
            padding: 15px;
            text-align: center;
            background-color: #f8f8f8;
        }
        .holiday {
            background-color: #ffcccc;
            color: #d9534f;
            font-weight: bold;
        }
    </style>
</head>
<body>
    <h1 class="title">{{ year }}年{{ month }}月学校日历</h1>
    <table>
        <thead>
            <tr>
                <th>周一</th>
                <th>周二</th>
                <th>周三</th>
                <th>周四</th>
                <th>周五</th>
                <th>周六</th>
                <th>周日</th>
            </tr>
        </thead>
        <tbody>
            {% for row in calendar_rows %}
            <tr>
                {% for day in row %}
                <td {% if day in holidays %}class="holiday"{% endif %}>{{ day }}</td>
                {% endfor %}
            </tr>
            {% endfor %}
        </tbody>
    </table>
</body>
</html>

然后编写视图类:

from rest_framework.views import APIView
from rest_framework.response import Response
from weasyprint import HTML
from io import BytesIO
from django.template.loader import render_to_string
from datetime import datetime, timedelta

class SchoolCalendarPDFView(APIView):
    def get(self, request):
        # 1. 生成日历数据(逻辑同ReportLab示例)
        today = datetime.today()
        year, month = today.year, today.month
        first_day = today.replace(day=1)
        last_day = (first_day + timedelta(days=32)).replace(day=1) - timedelta(days=1)
        
        headers = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]
        calendar_rows = [headers]
        
        current_day = first_day
        first_weekday = first_day.weekday()
        week_days = [""] * first_weekday
        
        while current_day <= last_day:
            week_days.append(str(current_day.day))
            if current_day.weekday() == 6:
                calendar_rows.append(week_days)
                week_days = []
            current_day += timedelta(days=1)
        
        if week_days:
            week_days += [""] * (7 - len(week_days))
            calendar_rows.append(week_days)
        
        # 示例:添加节假日标记
        holidays = ["10", "11"]  # 假设10、11号是节假日
        
        # 2. 渲染HTML模板
        html_content = render_to_string("calendar_template.html", {
            "year": year,
            "month": month,
            "calendar_rows": calendar_rows,
            "holidays": holidays
        })
        
        # 3. 生成PDF
        buffer = BytesIO()
        HTML(string=html_content).write_pdf(buffer)
        buffer.seek(0)
        
        # 4. 返回响应
        response = Response(buffer.getvalue(), content_type="application/pdf")
        response["Content-Disposition"] = f'attachment; filename="school_calendar_{year}_{month}.pdf"'
        return response

2. 配置路由

在项目的urls.py中添加接口路由:

from django.urls import path
from .views import SchoolCalendarPDFView

urlpatterns = [
    # 其他路由...
    path("calendar/pdf/", SchoolCalendarPDFView.as_view(), name="school-calendar-pdf"),
]

三、扩展优化建议

  • 动态参数支持:通过查询参数指定年份和月份,比如/calendar/pdf/?year=2024&month=10,在视图中通过request.query_params获取并调整日历范围。
  • 权限控制:添加DRF权限类(如IsAuthenticated),限制仅授权用户访问接口。
  • 缓存优化:对已生成的PDF进行缓存,避免重复生成相同月份的日历,提升接口性能。
  • 事件集成:接入学校事件数据(如考试、放假),在日历中高亮标记特殊日期。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 00:23:14