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

如何在Vue.js与Django中关联合并Party与Leads表数据

问题描述

我正在开发一款应用,Party表存储客户基础信息,Leads表存储同一客户的更多信息。两者通过Party_ID关联(Party表的主键,Leads表的外键),但无法在Vue.js前端的ag-grid表格中合并展示这两张表的数据。以下是相关代码:

Party表前端组件

<template>
<div class="tasks_container">
    <div class="tasks_content">
        <h1>Party table</h1>
        <ag-grid-vue
            style="width:1310px; height: 650px"
            class="ag-theme-alpine"
            :columnDefs="columnDefs"
            :rowData="rowData"
        >
        </ag-grid-vue>
    </div>
</div>
</template>
<script>
import axios from 'axios';
import "ag-grid-community/styles/ag-grid.css";
import "ag-grid-community/styles/ag-theme-alpine.css";
import { AgGridVue } from "ag-grid-vue3";
import 'ag-grid-enterprise';

export default {
    components: {
        AgGridVue,
    },
    data() {
        return {
            columnDefs: null,
            rowData: null,
        }
    },
    mounted(){
        this.columnDefs=[
        {field:'FIRST_NAME',headerName: 'First name'},
        {field:'LAST_NAME',headerName: 'Last name'},
        {field:'PARTY_TYPE',headerName: 'Party Type'},
        {field:'PRIMARY_NO',headerName: 'Main number'},
        {field:'WHATSAPP_NO',headerName: 'Whatsapp number'},
        {field:'EMAIL_ID',headerName: 'Email ID'},
        {field:'PREFERRED_CONTACT_METHOD',headerName: 'Preferred contact'},
        ];
        axios.get('http://127.0.0.1:8000/request1')
        .then(response=>this.rowData=response.data)
    },
}
</script>
<style scoped>
.ag-theme-alpine {
--ag-foreground-color: rgb(2, 1, 2);
--ag-background-color: rgb(245, 244, 243);
--ag-header-foreground-color: rgb(204, 245, 172);
--ag-header-background-color: rgb(64, 103, 209);
--ag-odd-row-background-color: rgb(0, 0, 0, 0.03);
--ag-header-column-resize-handle-color: rgb(236, 230, 236);

--ag-font-size: 17px;
--ag-font-family: monospace;
}
</style>

Leads表前端组件

<template>
<div class="tasks_container">
    <div class="tasks_content">
        <h1>Lead table</h1>
        <ag-grid-vue
            style="width: 1330px; height: 850px"
            class="ag-theme-alpine"
            :columnDefs="columnDefs"
            :rowData="rowData"
        >
        </ag-grid-vue>
    </div>
</div>
</template>
<script>
import axios from 'axios';
import "ag-grid-community/styles/ag-grid.css";
import "ag-grid-community/styles/ag-theme-alpine.css";
import { AgGridVue } from "ag-grid-vue3";
import 'ag-grid-enterprise';

export default {
    components: {
        AgGridVue,
    },
    data() {
        return {
            columnDefs: null,
            rowData: null,
        }
    },
    mounted(){
        this.columnDefs=[
        {field:'LEAD_NAME'},
        {field:'DESCRIPTION'},
        {field:'LEAD_STATUS'},
        {field:'LEAD_SOURCE'},
        {field:'PARTY_ID'},
        ];
        axios.get('http://127.0.0.1:8000/request2')
        .then(response=>this.rowData=response.data)
    },
}
</script>

后端代码

views.py

from django.shortcuts import render
from django.views.decorators.csrf import csrf_exempt
from rest_framework.parsers import JSONParser
from django.http.response import JsonResponse

from CRM.models import party,leads,lookup_names,lookup_names_values
from CRM.serializers import partySerializer,leadsSerializer,lookup_namesSerializer,lookup_names_valuesSerializer

@csrf_exempt
def partyAPI(request,id=0):
    if request.method=='GET':
        partys=party.objects.all()
        party_serializers= partySerializer(partys,many=True)
        return JsonResponse(party_serializers.data,safe=False)
    elif request.method=='POST':
        partys_data=JSONParser().parse(request)
        party_serializers=partySerializer(data=partys_data)
        if party_serializers.is_valid():
            party_serializers.save()
            return JsonResponse("20",safe=False)
    elif request.method=='PUT':
        partys_data=JSONParser().parse(request)
        partys=party.objects.get(Party_ID=partys_data['Party_ID'])
        party_serializers=partySerializer(partys,data=partys_data)
        if party_serializers.is_valid():
            party_serializers.save()
            return JsonResponse("19",safe=False)
        return JsonResponse("Failed")
    elif request.method=='DELETE':
        partys=party.objects.get(Party_ID=id)
        partys.delete()
        return JsonResponse("Deleted",safe=False)

@csrf_exempt
def leadsAPI(request,id=0):
    if request.method=='GET':
        leadss=leads.objects.all()
        leads_serializers= leadsSerializer(leadss,many=True)
        return JsonResponse(leads_serializers.data,safe=False)
    elif request.method=='POST':
        leads_data=JSONParser().parse(request)
        leads_serializers=leadsSerializer(data=leads_data)
        if leads_serializers.is_valid():
            leads_serializers.save()
            return JsonResponse("20",safe=False)
    elif request.method=='PUT':
        leads_data=JSONParser().parse(request)
        leadss=leads.objects.get(LEAD_ID=leads_data['LEAD_ID'])
        leads_serializers=leadsSerializer(leadss,data=leads_data)
        if leads_serializers.is_valid():
            leads_serializers.save()
            return JsonResponse("Added",safe=False)
        return JsonResponse("Failed")
    elif request.method =='DELETE':
        leadss=leads.objects.get(LEAD_ID=id)
        leadss.delete()
        return JsonResponse("deleted",safe=False)

Models.py

from django.db import models
import datetime

class party(models.Model):
    Party_ID = models.AutoField(primary_key=True)
    FIRST_NAME = models.CharField(max_length=100)
    LAST_NAME = models.CharField(max_length=100)
    PARTY_TYPE = models.CharField(max_length=2000)
    PRIMARY_NO = models.IntegerField()
    WHATSAPP_NO = models.IntegerField()
    EMAIL_ID = models.CharField(max_length=100)
    PREFERRED_CONTACT_METHOD = models.CharField(max_length=100)
    CREATED_BY = models.IntegerField(default=-1)
    CREATION_DATE = models.DateField(default=datetime.date.today)
    LAST_UPDATE_DATE = models.DateField(default=datetime.date.today)
    TENANT_MAP_ID = models.IntegerField(default=1)

class leads(models.Model):
    LEAD_ID = models.AutoField(primary_key=True)
    LEAD_NAME = models.CharField(max_length=100)
    DESCRIPTION = models.CharField(max_length=2000)
    LEAD_STATUS = models.CharField(max_length=100)
    LEAD_SOURCE = models.CharField(max_length=500)
    CREATED_BY = models.IntegerField(default=-1)
    CREATION_DATE = models.DateField(default=datetime.date.today)
    LAST_UPDATE_DATE = models.DateField(default=datetime.date.today)
    PARTY_ID = models.ForeignKey(party, on_delete=models.CASCADE)
    TENANT_MAP_ID = models.IntegerField(default=1)

希望找到合并两张表数据展示的方法,要求功能正常、界面美观。


解决方案

有两种可行方案,推荐优先使用后端联表查询,减少前端数据处理逻辑,同时保证数据一致性。

方案一:后端联表查询返回合并数据

步骤1:修改Serializer,关联Leads数据

在partySerializer中嵌套leadsSerializer,自动带出关联的Leads信息:

# CRM/serializers.py
from rest_framework import serializers
from .models import party, leads

class leadsSerializer(serializers.ModelSerializer):
    class Meta:
        model = leads
        fields = ('LEAD_NAME', 'DESCRIPTION', 'LEAD_STATUS', 'LEAD_SOURCE')

class partySerializer(serializers.ModelSerializer):
    leads_set = leadsSerializer(many=True, read_only=True)  # Django自动生成反向关联名

    class Meta:
        model = party
        fields = ('Party_ID', 'FIRST_NAME', 'LAST_NAME', 'PARTY_TYPE', 'PRIMARY_NO', 'WHATSAPP_NO', 'EMAIL_ID', 'PREFERRED_CONTACT_METHOD', 'leads_set')

步骤2:前端修改为单组件展示合并数据

创建合并后的表格组件,将关联的Leads数据转为平级行展示:

<template>
<div class="tasks_container">
    <div class="tasks_content">
        <h1>客户与线索合并表</h1>
        <ag-grid-vue
            style="width: 1600px; height: 700px"
            class="ag-theme-alpine"
            :columnDefs="columnDefs"
            :rowData="rowData"
        >
        </ag-grid-vue>
    </div>
</div>
</template>
<script>
import axios from 'axios';
import "ag-grid-community/styles/ag-grid.css";
import "ag-grid-community/styles/ag-theme-alpine.css";
import { AgGridVue } from "ag-grid-vue3";
import 'ag-grid-enterprise';

export default {
    components: {
        AgGridVue,
    },
    data() {
        return {
            columnDefs: [
                {field:'FIRST_NAME',headerName: '姓名'},
                {field:'LAST_NAME',headerName: '姓氏'},
                {field:'PARTY_TYPE',headerName: '客户类型'},
                {field:'PRIMARY_NO',headerName: '主号码'},
                {field:'WHATSAPP_NO',headerName: 'WhatsApp号码'},
                {field:'EMAIL_ID',headerName: '邮箱'},
                {field:'PREFERRED_CONTACT_METHOD',headerName: '首选联系方式'},
                {field:'LEAD_NAME',headerName: '线索名称'},
                {field:'DESCRIPTION',headerName: '线索描述'},
                {field:'LEAD_STATUS',headerName: '线索状态'},
                {field:'LEAD_SOURCE',headerName: '线索来源'}
            ],
            rowData: null
        }
    },
    async mounted(){
        const response = await axios.get('http://127.0.0.1:8000/request1');
        // 处理一对多关联:每个线索生成一行客户数据
        const mergedData = [];
        response.data.forEach(party => {
            if (party.leads_set?.length) {
                party.leads_set.forEach(lead => {
                    mergedData.push({
                        ...party,
                        ...lead,
                        leads_set: undefined
                    });
                });
            } else {
                mergedData.push({
                    ...party,
                    LEAD_NAME: '-',
                    DESCRIPTION: '-',
                    LEAD_STATUS: '-',
                    LEAD_SOURCE: '-',
                    leads_set: undefined
                });
            }
        });
        this.rowData = mergedData;
    },
}
</script>
<style scoped>
.ag-theme-alpine {
--ag-foreground-color: rgb(2, 1, 2);
--ag-background-color: rgb(245, 244, 243);
--ag-header-foreground-color: rgb(204, 245, 172);
--ag-header-background-color: rgb(64, 103, 209);
--ag-odd-row-background-color: rgb(0, 0, 0, 0.03);
--ag-header-column-resize-handle-color: rgb(236, 230, 236);

--ag-font-size: 17px;
--ag-font-family: monospace;
}
</style>

方案二:前端获取两个数据集后合并

如果不想修改后端,可在前端并行获取两个数据集后,通过Party_ID关联合并:

<template>
<!-- 同方案一模板 -->
</template>
<script>
export default {
    // ...
    async mounted(){
        // 并行拉取两个接口数据
        const [partyRes, leadsRes] = await Promise.all([
            axios.get('http://127.0.0.1:8000/request1'),
            axios.get('http://127.0.0.1:8000/request2')
        ]);
        const partyData = partyRes.data;
        const leadsData = leadsRes.data;

        // 合并数据
        const mergedData = partyData.flatMap(party => {
            const relatedLeads = leadsData.filter(lead => lead.PARTY_ID === party.Party_ID);
            return relatedLeads.length 
                ? relatedLeads.map(lead => ({...party, ...lead}))
                : [{...party, LEAD_NAME: '-', DESCRIPTION: '-', LEAD_STATUS: '-', LEAD_SOURCE: '-'}];
        });

        this.rowData = mergedData;
        this.columnDefs = [
            {field:'FIRST_NAME',headerName: '姓名'},
            {field:'LAST_NAME',headerName: '姓氏'},
            {field:'PARTY_TYPE',headerName: '客户类型'},
            {field:'PRIMARY_NO',headerName: '主号码'},
            {field:'WHATSAPP_NO',headerName: 'WhatsApp号码'},
            {field:'EMAIL_ID',headerName: '邮箱'},
            {field:'PREFERRED_CONTACT_METHOD',headerName: '首选联系方式'},
            {field:'LEAD_NAME',headerName: '线索名称'},
            {field:'DESCRIPTION',headerName: '线索描述'},
            {field:'LEAD_STATUS',headerName: '线索状态'},
            {field:'LEAD_SOURCE',headerName: '线索来源'}
        ];
    }
}
</script>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 09:56:21