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

如何通过Django REST Framework实现Django模板regroup标签的等效功能,为React提供博客归档分组数据

嘿,我明白你想要实现的归档侧边栏数据结构——按年份分组,每个年份下对应所有有文章的月份,对吧?之前你用Django模板的regroup标签做得很顺畅,现在转React+DRF确实需要调整数据格式。我来给你一个完全适配需求的方案:

解决方案

核心思路是复用你之前模板标签里的dates('publish', 'month')方法(它会自动去重,只返回有文章的月份),然后在DRF视图里直接把这些日期按年份分组,生成React能直接遍历的层级结构。

1. DRF视图实现

不需要修改现有模型,也不需要额外的序列化器,直接在视图里处理数据:

from rest_framework.decorators import api_view
from rest_framework.response import Response
from .models import Post

@api_view(["GET"])
def year_month_archive(request):
    # 获取所有存在发布文章的月份,按时间降序排列
    monthly_dates = Post.published.dates('publish', 'month', order='DESC')
    
    # 按年份分组整理数据
    archive_map = {}
    for date_obj in monthly_dates:
        year = date_obj.year
        # 封装月份的显示名称和路由所需的数字月份
        month_data = {
            "display_name": date_obj.strftime("%B %Y"),
            "year": year,
            "month_number": date_obj.month
        }
        # 如果年份还没在分组里,先初始化空列表
        if year not in archive_map:
            archive_map[year] = []
        archive_map[year].append(month_data)
    
    # 转换成数组格式,更符合React的遍历习惯
    formatted_archive = [
        {"year": year, "months": months_list}
        for year, months_list in archive_map.items()
    ]
    
    return Response({"archive": formatted_archive})

2. 返回的数据结构示例

这个视图会输出完全符合你需求的层级数据:

{
  "archive": [
    {
      "year": 2021,
      "months": [
        {
          "display_name": "August 2021",
          "year": 2021,
          "month_number": 8
        }
      ]
    },
    {
      "year": 2020,
      "months": [
        {
          "display_name": "December 2020",
          "year": 2020,
          "month_number": 12
        },
        {
          "display_name": "May 2020",
          "year": 2020,
          "month_number": 5
        },
        {
          "display_name": "April 2020",
          "year": 2020,
          "month_number": 4
        },
        {
          "display_name": "March 2020",
          "year": 2020,
          "month_number": 3
        },
        {
          "display_name": "February 2020",
          "year": 2020,
          "month_number": 2
        }
      ]
    },
    {
      "year": 2019,
      "months": [
        {
          "display_name": "September 2019",
          "year": 2019,
          "month_number": 9
        }
      ]
    }
  ]
}

为什么之前的方法没达到预期?

  • 方法1:你基于单篇文章的具体日期(比如2021-08-31)做分组,这会把每个文章的发布日期当成独立项,而不是合并同一月份的内容,自然得不到按月份聚合的结果。
  • 方法2:你把年份和月份名平放在一个列表里,没有层级关系,React渲染折叠面板时无法直接关联年份和对应的月份,需要额外处理,非常麻烦。

React端渲染参考

拿到数据后,你可以轻松渲染出和之前Django模板一样的折叠侧边栏:

function ArchiveSidebar({ archive }) {
  return (
    <div className="sidebar">
      <ul className="active list-unstyled">
        {archive.map(yearGroup => (
          <li key={yearGroup.year} className={yearGroup === archive[0] ? "active" : ""}>
            <h2 style={{ fontSize: "18px" }}>
              <a href={`/blog/archive-year/${yearGroup.year}`}>{yearGroup.year}</a>
              <i className="collapsing-icon fa fa-plus"></i>
            </h2>
            <div className="collapsing-content">
              <ul className="list-unstyled">
                {yearGroup.months.map(month => (
                  <li key={`${month.year}-${month.month_number}`}>
                    <a href={`/blog/archive_month_numeric/${month.year}/${month.month_number}`}>
                      {month.display_name}
                    </a>
                  </li>
                ))}
              </ul>
            </div>
          </li>
        ))}
      </ul>
    </div>
  );
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 11:54:11