Odoo 18 导出Excel功能知识点与流程总结

Odoo 18 导出Excel功能知识点与流程总结

一、功能概述

在Odoo 18中实现列表数据导出Excel功能,支持选中记录或全部记录导出,生成格式化的Excel文件。

二、技术架构

2.1 核心组件

┌─────────────────────────────────────────────────────────┐
│                    用户界面层                            │
│  (列表视图 + ir.actions.server 按钮)                    │
└──────────────────┬──────────────────────────────────────┘
                   │
                   ▼
┌─────────────────────────────────────────────────────────┐
│                    模型层 (Model)                        │
│  action_export_excel() → 生成URL                         │
│  get_excel_file() → 生成Excel二进制数据                 │
└──────────────────┬──────────────────────────────────────┘
                   │
                   ▼
┌─────────────────────────────────────────────────────────┐
│                    控制器层 (Controller)                 │
│  /cs_quality/export_excel → HTTP路由处理                │
│  接收请求 → 获取记录 → 调用模型方法 → 返回文件           │
└──────────────────┬──────────────────────────────────────┘
                   │
                   ▼
┌─────────────────────────────────────────────────────────┐
│                    文件生成层                            │
│  xlsxwriter库 → 创建Excel工作簿 → 写入数据 → 返回二进制 │
└─────────────────────────────────────────────────────────┘

2.2 文件结构

hx_cs_quality_control/
├── models/
│   └── measurement_data.py          # 模型方法:生成Excel数据
├── controllers/
│   └── controllers.py               # HTTP路由:处理文件下载
└── views/
    └── measurement_data_views.xml   # 视图配置:导出按钮

三、核心知识点

3.1 Odoo Server Action (ir.actions.server)

作用:在列表视图中添加自定义动作按钮

关键字段

  • model_id: 执行动作的模型(Many2one到ir.model)

  • binding_model_id: 绑定到哪个模型的视图(Many2one到ir.model)

  • binding_view_types: 绑定到哪些视图类型(如:list, form)

  • state: 动作类型(code表示执行Python代码)

  • code: 执行的Python代码

示例配置

<record id="action_export_excel" model="ir.actions.server">
    <field name="name">导出Excel</field>
    <field name="model_id" ref="model_cs_quality_measurement_data"/>
    <field name="binding_model_id" ref="model_cs_quality_measurement_data"/>
    <field name="binding_view_types">list</field>
    <field name="state">code</field>
    <field name="code">
action = records.action_export_excel()
    </field>
</record>

注意事项

  • code中,records变量自动包含选中的记录集

  • 返回的action必须是有效的动作字典

3.2 ir.actions.act_url

作用:返回URL动作,用于跳转到指定URL

关键字段

  • type: 固定为'ir.actions.act_url'

  • url: 要跳转的URL

  • target: 打开方式('self’表示当前窗口,'new’表示新窗口)

示例

return {
    'type': 'ir.actions.act_url',
    'url': f'/cs_quality/export_excel?ids={ids_str}',
    'target': 'self',
}

3.3 HTTP Controller路由

路由装饰器

@http.route('/cs_quality/export_excel', type='http', auth='user', methods=['GET'], csrf=False)

参数说明

  • type='http': HTTP类型路由(返回文件)

  • auth='user': 需要用户认证

  • methods=['GET']: 允许的HTTP方法

  • csrf=False: 禁用CSRF保护(文件下载通常不需要)

文件响应

return request.make_response(
    excel_data,  # 二进制数据
    headers=[
        ('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'),
        ('Content-Disposition', content_disposition(filename)),
    ]
)

关键点

  • Content-Type: 指定MIME类型为Excel文件

  • Content-Disposition: 指定文件名和下载方式

  • content_disposition(): Odoo工具函数,处理文件名编码

3.4 xlsxwriter库使用

创建工作簿

import xlsxwriter
import io
​
output = io.BytesIO()  # 内存中的字节流
workbook = xlsxwriter.Workbook(output, {'in_memory': True})
worksheet = workbook.add_worksheet('测量数据')

定义格式

# 表头格式
header_format = workbook.add_format({
    'bold': True,
    'bg_color': '#366092',
    'font_color': '#FFFFFF',
    'align': 'center',
    'valign': 'vcenter',
    'border': 1,
    'font_size': 11
})
​
# 数字格式
number_format = workbook.add_format({
    'num_format': '0.000000',  # 保留6位小数
    'align': 'center',
    'border': 1
})
​
# 日期格式
date_format = workbook.add_format({
    'num_format': 'yyyy-mm-dd hh:mm:ss',
    'align': 'center',
    'border': 1
})

写入数据

# 写入表头
worksheet.write(0, col, header, header_format)
​
# 写入数字
worksheet.write_number(row, col, value, number_format)
​
# 写入日期时间
worksheet.write_datetime(row, col, datetime_value, date_format)
​
# 写入普通文本
worksheet.write(row, col, text, data_format)

设置列宽

worksheet.set_column(col, col, width)  # 设置单列宽度

关闭和获取数据

workbook.close()
output.seek(0)  # 重置指针到开头
return output.getvalue()  # 获取二进制数据

3.5 模型外部ID引用

问题:在XML中引用模型时,需要模型的外部ID

解决方案:显式定义ir.model记录

<record id="model_cs_quality_measurement_data" model="ir.model">
    <field name="name">测量数据明细</field>
    <field name="model">cs.quality.measurement.data</field>
</record>

引用方式

<field name="model_id" ref="model_cs_quality_measurement_data"/>

四、完整流程

4.1 用户操作流程

1. 用户打开"测量数据"列表视图
   ↓
2. 用户选择要导出的记录(可选,不选则导出全部)
   ↓
3. 用户点击"动作"菜单 → "导出Excel"
   ↓
4. 系统执行server action代码
   ↓
5. 跳转到导出URL
   ↓
6. 浏览器下载Excel文件

4.2 代码执行流程

┌─────────────────────────────────────────────────────────┐
│ 步骤1: 用户点击按钮                                      │
│ 触发: ir.actions.server (code)                          │
└──────────────────┬──────────────────────────────────────┘
                   │
                   ▼
┌─────────────────────────────────────────────────────────┐
│ 步骤2: 执行模型方法                                      │
│ measurement_data.action_export_excel()                  │
│ - 获取记录ID列表                                         │
│ - 构建URL: /cs_quality/export_excel?ids=1,2,3           │
│ - 返回ir.actions.act_url动作                             │
└──────────────────┬──────────────────────────────────────┘
                   │
                   ▼
┌─────────────────────────────────────────────────────────┐
│ 步骤3: 浏览器跳转到URL                                   │
│ GET /cs_quality/export_excel?ids=1,2,3                 │
└──────────────────┬──────────────────────────────────────┘
                   │
                   ▼
┌─────────────────────────────────────────────────────────┐
│ 步骤4: Controller处理请求                                │
│ controllers.export_excel()                              │
│ - 解析URL参数(ids)                                     │
│ - 获取记录集: env['cs.quality.measurement.data'].browse()│
│ - 调用模型方法生成Excel                                  │
└──────────────────┬──────────────────────────────────────┘
                   │
                   ▼
┌─────────────────────────────────────────────────────────┐
│ 步骤5: 生成Excel文件                                     │
│ measurement_data.get_excel_file()                       │
│ - 创建xlsxwriter工作簿                                   │
│ - 定义格式(表头、数据、数字、日期等)                   │
│ - 写入表头                                               │
│ - 循环写入数据行                                         │
│ - 设置列宽                                               │
│ - 关闭工作簿,返回二进制数据                             │
└──────────────────┬──────────────────────────────────────┘
                   │
                   ▼
┌─────────────────────────────────────────────────────────┐
│ 步骤6: 返回文件响应                                      │
│ request.make_response()                                 │
│ - 设置Content-Type                                      │
│ - 设置Content-Disposition(文件名)                     │
│ - 返回二进制数据                                         │
└──────────────────┬──────────────────────────────────────┘
                   │
                   ▼
┌─────────────────────────────────────────────────────────┐
│ 步骤7: 浏览器下载文件                                    │
│ 文件名: 测量数据导出_YYYYMMDD_HHMMSS.xlsx               │
└─────────────────────────────────────────────────────────┘

4.3 数据流转图

用户选择记录
    │
    ▼
[records对象] (Odoo记录集)
    │
    ├─→ action_export_excel()
    │   └─→ 生成URL字符串
    │
    └─→ get_excel_file()
        │
        ├─→ 遍历records
        │   ├─→ 读取字段值
        │   ├─→ 数据转换(状态映射、日期格式化等)
        │   └─→ 写入Excel单元格
        │
        └─→ 返回bytes二进制数据
            │
            └─→ HTTP响应
                └─→ 浏览器下载

五、关键代码片段

5.1 模型方法 - 生成URL动作

def action_export_excel(self):
    """导出测量数据到Excel - 返回URL动作"""
    # 获取当前记录集(在server action中,records已经包含了选中的记录)
    ids = self.ids if self else []
    
    # 构建URL
    ids_str = ','.join(str(id) for id in ids) if ids else ''
    url = f'/cs_quality/export_excel?ids={ids_str}'
    
    return {
        'type': 'ir.actions.act_url',
        'url': url,
        'target': 'self',
    }

5.2 模型方法 - 生成Excel数据

def get_excel_file(self):
    """生成Excel文件并返回二进制数据"""
    records = self
    
    # 创建Excel文件
    output = io.BytesIO()
    workbook = xlsxwriter.Workbook(output, {'in_memory': True})
    worksheet = workbook.add_worksheet('测量数据')
    
    # 定义格式...
    # 写入表头...
    # 写入数据...
    
    workbook.close()
    output.seek(0)
    return output.getvalue()

5.3 Controller路由 - 处理文件下载

@http.route('/cs_quality/export_excel', type='http', auth='user', methods=['GET'], csrf=False)
def export_excel(self, ids=None, **kwargs):
    """导出测量数据到Excel"""
    # 解析参数
    if ids:
        ids_list = [int(id.strip()) for id in ids.split(',') if id.strip()]
        records = request.env['cs.quality.measurement.data'].browse(ids_list)
    else:
        records = request.env['cs.quality.measurement.data'].search([])
    
    # 生成Excel
    excel_data = records.get_excel_file()
    
    # 生成文件名
    filename = f'测量数据导出_{datetime.now().strftime("%Y%m%d_%H%M%S")}.xlsx'
    
    # 返回文件
    return request.make_response(
        excel_data,
        headers=[
            ('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'),
            ('Content-Disposition', content_disposition(filename)),
        ]
    )

六、常见问题与解决方案

6.1 模型外部ID找不到

问题ValueError: External ID not found in the system: base.model_xxx

解决:显式定义ir.model记录

<record id="model_cs_quality_measurement_data" model="ir.model">
    <field name="name">测量数据明细</field>
    <field name="model">cs.quality.measurement.data</field>
</record>

6.2 中文文件名乱码

解决:使用content_disposition()函数处理文件名编码

from odoo.http import content_disposition
​
headers=[
    ('Content-Disposition', content_disposition(filename)),
]

6.3 日期格式问题

解决:使用write_datetime()方法并指定格式

date_format = workbook.add_format({
    'num_format': 'yyyy-mm-dd hh:mm:ss'
})
worksheet.write_datetime(row, col, datetime_value, date_format)

6.4 数字精度问题

解决:使用write_number()方法并指定数字格式

number_format = workbook.add_format({
    'num_format': '0.000000'  # 保留6位小数
})
worksheet.write_number(row, col, value, number_format)

七、最佳实践

7.1 代码组织

  1. 模型方法职责分离

    • action_export_excel(): 负责生成URL动作

    • get_excel_file(): 负责生成Excel二进制数据

  2. Controller职责

    • 参数解析和验证

    • 调用模型方法

    • 返回HTTP响应

7.2 性能优化

  1. 内存管理

    • 使用io.BytesIO()在内存中操作

    • 使用{'in_memory': True}选项

    • 及时关闭工作簿释放资源

  2. 大数据量处理

    • 考虑分页导出

    • 使用流式写入

    • 添加进度提示

7.3 错误处理

try:
    # Excel生成逻辑
    excel_data = records.get_excel_file()
    return request.make_response(excel_data, headers=...)
except Exception as e:
    _logger.error('导出Excel错误: %s', str(e))
    return request.not_found()

7.4 用户体验

  1. 文件名规范:包含时间戳,避免覆盖

    filename = f'测量数据导出_{datetime.now().strftime("%Y%m%d_%H%M%S")}.xlsx'
    
  2. 格式美化

    • 表头使用醒目的颜色和字体

    • 数据使用合适的对齐方式

    • 重要信息使用颜色标识(如合格/不合格)

  3. 列宽自适应

    column_widths = [15, 18, 18, 12, ...]
    for col, width in enumerate(column_widths):
        worksheet.set_column(col, col, width)
    

八、扩展功能建议

8.1 导出选项

  • 支持选择导出字段

  • 支持导出格式选择(Excel/CSV)

  • 支持导出模板自定义

8.2 批量导出

  • 支持异步导出

  • 支持导出任务队列

  • 支持导出历史记录

8.3 数据过滤

  • 支持按条件过滤导出

  • 支持按时间范围导出

  • 支持按状态筛选导出

九、总结

9.1 核心技术栈

  • Odoo框架:ir.actions.server, ir.actions.act_url

  • HTTP路由:Controller处理文件下载

  • Excel生成:xlsxwriter库

  • 数据流转:模型 → Controller → 文件响应

9.2 关键要点

  1. Server Action绑定到列表视图,提供用户入口

  2. URL动作跳转到Controller路由

  3. Controller解析参数,调用模型方法生成Excel

  4. 使用xlsxwriter生成格式化的Excel文件

  5. 通过HTTP响应返回文件供用户下载

9.3 学习价值

  • 理解Odoo动作系统(Actions)

  • 掌握HTTP Controller的使用

  • 学习Excel文件生成技术

  • 了解Odoo数据流转机制

适用版本:Odoo 18.0
效果展示


6 个赞

蹲!!!!!!!!!!!!!!!!

1 个赞

导出来的是条件数据,当前数据 还是 当前表单的全部数据?

感谢分享!~~~~~