如何通过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
相关产品推荐
相关产品推荐

