Django:如何遍历两个一对多数据表关联关系?
实现单公司展示关联职位与项目的网页方案
嘿,我懂你要做的事儿——一个能按公司维度展示关联职位和项目的网页,每个公司只出现一次,还能切换查看不同公司的内容对吧?结合你提到的Company和Position、Project的一对多关联,我给你整理了一套落地的思路和代码示例,应该能解决你现在的问题:
第一步:先把数据理顺
不管是后端返回还是前端模拟数据,核心是要把重复的公司数据聚合起来,确保每个公司对象里直接包含对应的职位和项目数组,避免渲染时重复显示公司。
理想的数据结构(后端返回或前端处理后)
// 模拟聚合好的公司数据 const companiesData = [ { id: 1, name: "Alpha科技", description: "专注AI领域的科技公司", positions: [ { id: 101, title: "前端开发工程师", department: "技术部" }, { id: 102, title: "产品经理", department: "产品部" } ], projects: [ { id: 201, name: "智能客服系统", status: "已完成" }, { id: 202, name: "AI图像识别平台", status: "进行中" } ] }, { id: 2, name: "Beta制造", description: "高端制造业企业", positions: [ { id: 103, title: "机械工程师", department: "研发部" }, { id: 104, title: "质量检测员", department: "质检部" } ], projects: [ { id: 203, name: "自动化生产线升级", status: "已完成" }, { id: 204, name: "新能源零件研发", status: "已完成" } ] } ];
如果后端返回平铺数据,前端可以这样聚合
要是后端给你的是公司+职位/项目的平铺记录,你可以用这个函数把数据整理成上面的结构:
// 处理平铺数据为聚合结构的工具函数 function aggregateCompanies(rawData) { const companyMap = {}; rawData.forEach(item => { // 先初始化公司信息(避免重复) if (!companyMap[item.companyId]) { companyMap[item.companyId] = { id: item.companyId, name: item.companyName, description: item.companyDesc, positions: [], projects: [] }; } // 添加关联职位(去重) if (item.positionId && !companyMap[item.companyId].positions.some(p => p.id === item.positionId)) { companyMap[item.companyId].positions.push({ id: item.positionId, title: item.positionTitle, department: item.department }); } // 添加已完成项目(按需求过滤+去重) if (item.projectId && item.projectStatus === "已完成" && !companyMap[item.companyId].projects.some(p => p.id === item.projectId)) { companyMap[item.companyId].projects.push({ id: item.projectId, name: item.projectName, status: item.projectStatus }); } }); return Object.values(companyMap); }
第二步:前端页面实现(带切换功能)
下面是完整的HTML+JS代码,实现公司信息展示、关联职位/项目渲染,以及前后切换公司的功能:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>公司信息展示</title> <style> .company-card { border: 1px solid #eee; padding: 20px; margin: 20px auto; width: 80%; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } .section-title { color: #2c3e50; margin-top: 20px; margin-bottom: 10px; border-bottom: 1px solid #eee; padding-bottom: 5px; } .nav-buttons { text-align: center; margin: 20px 0; } .nav-btn { padding: 8px 18px; margin: 0 5px; cursor: pointer; border: none; border-radius: 4px; background-color: #3498db; color: white; } .nav-btn:disabled { background-color: #bdc3c7; cursor: not-allowed; } ul { list-style-type: square; padding-left: 20px; } </style> </head> <body> <div class="nav-buttons"> <button class="nav-btn" id="prevBtn">上一家公司</button> <button class="nav-btn" id="nextBtn">下一家公司</button> </div> <div class="company-card" id="companyContainer"> <!-- 动态渲染的公司信息会放在这里 --> </div> <script> // 假设已经有了聚合后的companiesData const companiesData = [ { id: 1, name: "Alpha科技", description: "专注AI领域的科技公司", positions: [ { id: 101, title: "前端开发工程师", department: "技术部" }, { id: 102, title: "产品经理", department: "产品部" } ], projects: [ { id: 201, name: "智能客服系统", status: "已完成" }, { id: 202, name: "AI图像识别平台", status: "进行中" } ] }, { id: 2, name: "Beta制造", description: "高端制造业企业", positions: [ { id: 103, title: "机械工程师", department: "研发部" }, { id: 104, title: "质量检测员", department: "质检部" } ], projects: [ { id: 203, name: "自动化生产线升级", status: "已完成" }, { id: 204, name: "新能源零件研发", status: "已完成" } ] } ]; let currentIndex = 0; // 渲染当前公司信息的函数 function renderCurrentCompany() { const company = companiesData[currentIndex]; const container = document.getElementById('companyContainer'); container.innerHTML = ` <h2>${company.name}</h2> <p>${company.description}</p> <h3 class="section-title">关联职位</h3> <ul> ${company.positions.map(pos => `<li>${pos.title} - ${pos.department}</li>`).join('')} </ul> <h3 class="section-title">已完成项目</h3> <ul> ${company.projects.map(proj => `<li>${proj.name}</li>`).join('')} </ul> `; // 处理切换按钮的禁用状态(边界判断) document.getElementById('prevBtn').disabled = currentIndex === 0; document.getElementById('nextBtn').disabled = currentIndex === companiesData.length - 1; } // 绑定切换按钮的事件 document.getElementById('prevBtn').addEventListener('click', () => { if (currentIndex > 0) { currentIndex--; renderCurrentCompany(); } }); document.getElementById('nextBtn').addEventListener('click', () => { if (currentIndex < companiesData.length - 1) { currentIndex++; renderCurrentCompany(); } }); // 页面加载时渲染第一个公司 renderCurrentCompany(); </script> </body> </html>
几个关键注意点
- 数据聚合是核心:不管后端还是前端处理,一定要确保每个公司只存在一条记录,关联的职位和项目都放在对应数组里,这是避免公司重复显示的关键。
- 切换边界处理:记得在切换到第一家或最后一家公司时禁用对应按钮,防止用户无效操作。
- 项目筛选:如果需求是只展示已完成项目,上面的代码已经在聚合和渲染时做了过滤,你可以根据实际需求调整过滤条件。
内容的提问来源于stack exchange,提问作者Hildy
相关产品推荐
相关产品推荐

