如何在Odoo 16中为one2many字段实现批量导入记录功能
Odoo 16 批量上传数据至One2many字段解决方案
核心逻辑
One2many字段本质是关联子模型数据,批量上传的核心是:读取上传文件(以Excel为例)的行数据,创建/关联子模型记录,再绑定到主模型的One2many字段。以下是具体实现步骤:
步骤1:定义模型结构(示例场景)
假设主模型为main.order,One2many字段关联子模型main.order.line,子模型包含产品、数量、单价字段:
# 主订单模型 class MainOrder(models.Model): _name = 'main.order' _description = '主订单' name = fields.Char('订单编号') order_line_ids = fields.One2many('main.order.line', 'order_id', '订单明细') # 订单明细子模型 class MainOrderLine(models.Model): _name = 'main.order.line' _description = '订单明细' order_id = fields.Many2one('main.order', '主订单', required=True) product_id = fields.Many2one('product.product', '产品', required=True) quantity = fields.Float('数量', required=True) price = fields.Float('单价', required=True)
步骤2:添加上传按钮与处理逻辑
表单视图(XML)
在主订单表单中添加批量导入按钮:
<record id="view_main_order_form" model="ir.ui.view"> <field name="name">main.order.form</field> <field name="model">main.order</field> <field name="arch" type="xml"> <form> <header> <button name="action_upload_order_lines" string="批量导入明细" type="object" class="btn-primary"/> </header> <sheet> <group> <field name="name"/> <field name="order_line_ids" widget="one2many_list"> <tree editable="bottom"> <field name="product_id"/> <field name="quantity"/> <field name="price"/> </tree> </field> </group> </sheet> </form> </field> </record>
上传逻辑(Python)
使用openpyxl读取Excel,需先在requirements.txt添加openpyxl并安装依赖:
from openpyxl import load_workbook from io import BytesIO import base64 from odoo.exceptions import UserError class MainOrder(models.Model): _inherit = 'main.order' def action_upload_order_lines(self): # 弹出文件选择向导 return { 'name': '选择Excel文件', 'type': 'ir.actions.act_window', 'res_model': 'file.upload.wizard', 'view_mode': 'form', 'target': 'new', 'context': {'default_order_id': self.id} } # 文件上传向导模型 class FileUploadWizard(models.TransientModel): _name = 'file.upload.wizard' _description = '批量导入向导' order_id = fields.Many2one('main.order', '主订单', required=True) file = fields.Binary('Excel文件', required=True) file_name = fields.Char('文件名') def action_import(self): # 校验文件格式 if not self.file_name.endswith('.xlsx'): raise UserError('仅支持.xlsx格式文件') # 读取二进制文件 wb = load_workbook(filename=BytesIO(base64.b64decode(self.file))) ws = wb.active line_data = [] # 跳过表头(假设第一行为表头) for row_num, row in enumerate(ws.iter_rows(min_row=2, values_only=True), start=2): product_name, quantity, price = row # 校验必填字段 if not all([product_name, quantity, price]): raise UserError(f'第{row_num}行数据缺失,请检查') # 匹配产品(建议用产品编码替代名称,避免重复) product = self.env['product.product'].search([('name', '=', product_name)], limit=1) if not product: raise UserError(f'第{row_num}行产品【{product_name}】不存在') line_data.append({ 'order_id': self.order_id.id, 'product_id': product.id, 'quantity': quantity, 'price': price }) # 批量创建明细记录 self.env['main.order.line'].create(line_data) # 刷新视图 return { 'type': 'ir.actions.client', 'tag': 'reload' }
向导视图(XML)
<record id="view_file_upload_wizard_form" model="ir.ui.view"> <field name="name">file.upload.wizard.form</field> <field name="model">file.upload.wizard</field> <field name="arch" type="xml"> <form> <sheet> <group> <field name="order_id" readonly="1"/> <field name="file" filename="file_name"/> </group> </sheet> <footer> <button name="action_import" string="导入" type="object" class="btn-primary"/> <button string="取消" class="btn-secondary" special="cancel"/> </footer> </form> </field> </record>
关键注意事项
- 文件格式:Excel表头需与代码读取的字段顺序匹配,确保数量、单价为数字格式。
- 数据匹配:关联记录(如产品)建议用唯一标识(如内部编码
default_code)匹配,避免名称重复导致错误。 - 性能优化:大数据量导入时,先收集所有数据到列表再批量
create,减少数据库交互次数。 - 错误处理:添加字段校验、异常捕获,用
UserError明确提示用户问题所在。
测试流程
- 打开主订单表单,点击「批量导入明细」按钮。
- 选择准备好的Excel文件,点击「导入」。
- 订单明细的One2many字段将自动填充导入数据。
内容的提问来源于stack exchange,提问作者Nisarga Cm
相关产品推荐
相关产品推荐

