Odoo基于父模型字段生成动态子模型的实现方案问询
基于动态Properties生成子模型的实现方案
完全可以实现需求,下面分后端、前端给出具体落地方案,以Python Django + Vue.js为例(其他技术栈思路通用):
一、核心思路
因为properties字段是动态定义的,子模型的列结构无法提前固定,所以采用**「动态结构存储+前端动态渲染」**的组合方案,主要有两种存储模式可选,根据查询需求选择:
二、后端实现
1. 定义基础Data模型
首先存储主模型的基础信息,同时记录动态属性的结构(字段名、数据类型):
from django.db import models class Data(models.Model): name = models.CharField(max_length=255) latitude = models.FloatField() longitude = models.FloatField() # 用JSON存储动态属性的结构,比如 {"temperature": "float", "humidity": "string"} properties_schema = models.JSONField(default=dict)
2. 动态子数据存储方案
方案1:JSON字段存储(推荐,适合中小数据量)
用JSON字段直接存储每一行的动态值,结构简单,开发成本低:
class DataRow(models.Model): data = models.ForeignKey(Data, on_delete=models.CASCADE, related_name="rows") # 存储一行的动态属性值,key对应properties_schema中的字段名 values = models.JSONField(default=dict)
- 当用户创建Data对象时,提交的properties结构会被存在
properties_schema中 - 添加数据行时,前端收集的字段值会以JSON格式存在
values里
方案2:EAV模式(适合需对动态列做复杂查询的场景)
如果需要基于动态属性做过滤、排序等操作,可采用实体-属性-值模式:
class DataAttribute(models.Model): data = models.ForeignKey(Data, on_delete=models.CASCADE, related_name="attributes") row_identifier = models.CharField(max_length=255) # 标记同一行的属性 attribute_name = models.CharField(max_length=255) # 动态属性名 # 按数据类型分字段存储,避免类型混淆 value_float = models.FloatField(null=True) value_string = models.CharField(max_length=255, null=True) value_int = models.IntegerField(null=True)
这种方式每个属性单独存一行,查询时可针对性过滤,但代码复杂度更高。
3. 核心接口实现
- 创建Data接口:接收
name、latitude、longitude、properties_schema,保存Data对象 - 添加数据行接口:接收
data_id和values(JSON),创建DataRow关联到对应Data - 获取Data详情接口:返回Data的基础信息、properties_schema及所有关联的DataRow
三、前端实现
核心是根据后端返回的properties_schema动态生成表格的表头和输入框:
1. 动态渲染表格(Vue.js示例)
<template> <div class="dynamic-table"> <table> <thead> <tr> <th v-for="(type, field) in data.properties_schema" :key="field"> {{ field }} </th> <th>操作</th> </tr> </thead> <tbody> <!-- 已有数据行 --> <tr v-for="(row, idx) in data.rows" :key="row.id"> <td v-for="(type, field) in data.properties_schema" :key="field"> {{ row.values[field] }} </td> <td><button @click="deleteRow(row.id)">删除</button></td> </tr> <!-- 新增行输入区 --> <tr> <td v-for="(type, field) in data.properties_schema" :key="field"> <input :type="type === 'float' ? 'number' : 'text'" v-model="newRow[field]" :placeholder="`输入${field}`" > </td> <td><button @click="submitNewRow">添加</button></td> </tr> </tbody> </table> </div> </template> <script> export default { data() { return { data: {}, newRow: {} } }, methods: { async fetchData() { // 从后端获取Data详情(含schema和rows) const res = await this.$axios.get(`/api/data/${this.$route.params.id}/`) this.data = res.data // 初始化新增行的空值 this.newRow = Object.fromEntries(Object.keys(this.data.properties_schema).map(k => [k, ''])) }, async submitNewRow() { // 提交新增行到后端 await this.$axios.post('/api/data-rows/', { data_id: this.data.id, values: this.newRow }) // 刷新数据 this.fetchData() this.newRow = Object.fromEntries(Object.keys(this.data.properties_schema).map(k => [k, ''])) }, async deleteRow(rowId) { await this.$axios.delete(`/api/data-rows/${rowId}/`) this.fetchData() } }, mounted() { this.fetchData() } } </script>
四、关键注意事项
- 数据校验:后端要根据
properties_schema校验传入的values类型是否匹配,比如float类型不能存字符串 - 性能优化:如果用JSON存储,PostgreSQL的
JSONB类型支持索引和部分查询,比普通JSON更适合生产环境 - 兼容性处理:如果允许用户修改
properties_schema,要考虑已有数据的兼容,比如新增字段给默认值,删除字段时保留历史数据
内容的提问来源于stack exchange,提问作者Riajul Kashem
相关产品推荐
相关产品推荐

