如何编写DRF Serializer输出适配react-table的扁平化序列化数据
纠正后的问题标题:Django Rest Framework序列化打平一对多关系 + React Table渲染带分组表头表格方案
问题1:如何编写Serializer直接输出符合要求格式的数据?
你期望的打平格式本质是把Item和关联的PZItem做左外连接合并,没有关联PZItem的Item也保留一条记录,不需要用嵌套Serializer实现,直接在视图层构造查询集即可,性能远高于你当前用SerializerMethodField实现的N+1查询方案:
from itertools import chain from rest_framework.response import Response from rest_framework.views import APIView class FlatItemListView(APIView): def get(self, request, *args, **kwargs): # 第一步:查询所有有有效库存的PZItem,带出关联Item字段 valid_pz_items = PZItem.objects.filter( quantity_available_to_order__gt=0 ).values( "item__title", "item__category", "id", "quantity_available_to_order" ) # 格式转换为期望的key pz_item_list = [ { "title": item["item__title"], "category": item["item__category"], "pzitem_id": item["id"], "pzitem_quantity_available_to_order": item["quantity_available_to_order"], } for item in valid_pz_items ] # 第二步:查询所有没有关联有效PZItem的Item items_without_valid_pz = Item.objects.exclude( pz_items__quantity_available_to_order__gt=0 ).values("title", "category") # 补全空字段 empty_pz_item_list = [ { "title": item["title"], "category": item["category"], "pzitem_id": None, "pzitem_quantity_available_to_order": None, } for item in items_without_valid_pz ] # 合并两个列表返回 return Response(chain(pz_item_list, empty_pz_item_list))
如果一定要用Serializer实现,可以重写ListSerializer的to_representation方法,逻辑和上面一致即可。
问题2:基于当前的原始数据结构,如何用react-table渲染表格并插入分组表头?
方案1:用你现有的前端转换函数+react-table分组功能
你写的前端转换逻辑完全可用,前端转换的开销可以忽略,还能减少接口传输的数据冗余,结合react-table的useGroupBy钩子就能自动生成分组表头:
import { useTable, useGroupBy } from 'react-table' // 优化后的转换函数,避免全局变量污染 const transformData = (rawData) => { const newData = [] rawData.forEach((item) => { const { pzitems, title, category } = item if (pzitems.length) { pzitems.forEach(pzitem => { newData.push({ title, category, pzitem_id: pzitem.id, pzitem_quantity_available_to_order: pzitem.quantity_available_to_order }) }) } else { newData.push({ title, category, pzitem_id: null, pzitem_quantity_available_to_order: null }) } }) return newData } // 表格组件 function ItemTable({ rawData }) { const data = transformData(rawData) const columns = [ { Header: '商品名称', accessor: 'title', groupBy: true // 开启按商品名称分组 }, { Header: '分类ID', accessor: 'category' }, { Header: 'PZItem ID', accessor: 'pzitem_id' }, { Header: '可订购数量', accessor: 'pzitem_quantity_available_to_order' } ] const { getTableProps, getTableBodyProps, headerGroups, rows, prepareRow } = useTable({ columns, data }, useGroupBy) return ( <table {...getTableProps()} style={{ width: '100%', borderCollapse: 'collapse' }}> <thead> {headerGroups.map(headerGroup => ( <tr {...headerGroup.getHeaderGroupProps()}> {headerGroup.headers.map(column => ( <th {...column.getHeaderProps()} style={{ border: '1px solid #ddd', padding: '8px' }}> {column.render('Header')} </th> ))} </tr> ))} </thead> <tbody {...getTableBodyProps()}> {rows.map(row => { prepareRow(row) // 渲染分组表头行 if (row.isGrouped) { return ( <tr {...row.getRowProps()} style={{ background: '#f5f5f5', fontWeight: 'bold' }}> <td {...row.cells[0].getCellProps()} colSpan={columns.length} style={{ border: '1px solid #ddd', padding: '8px' }}> {row.render('GroupValue')} </td> </tr> ) } // 渲染普通数据行 return ( <tr {...row.getRowProps()}> {row.cells.map(cell => ( <td {...cell.getCellProps()} style={{ border: '1px solid #ddd', padding: '8px' }}> {cell.render('Cell') ?? '-'} </td> ))} </tr> ) })} </tbody> </table> ) }
方案2:不转换数据直接渲染
如果你不想做前端数据转换,也可以直接在react-table的行渲染逻辑里遍历嵌套的pzitems字段,手动插入分组行,逻辑和上面的转换逻辑类似,只是把转换步骤放到渲染阶段即可。
内容的提问来源于stack exchange,提问作者skelaw
相关产品推荐
相关产品推荐

