定义模块数据

重要

本教程是 服务器框架入门 教程的延伸。请确保你已完成该教程,并以你构建的 estate 模块作为本教程练习的基础。

数据类型

主数据

主数据通常是模块技术或业务需求的一部分。换言之,这类数据往往是模块正常运行所必需的。安装模块时,这类数据总会被安装。

我们在前面已经接触过技术数据,因为我们已经定义了 视图 和 动作。它们属于主数据的一种。

除技术数据外,还可以定义业务数据,例如国家、货币、计量单位,以及完整的国家本地化(法律报表、税务定义、会计科目表)等等……

演示数据

除了模块正常运行所必需的主数据外,我们还可以提供用于演示目的的数据:

  • 帮助销售人员快速完成演示。

  • 为开发者提供一组可用的数据,用于测试新功能,并查看这些新功能在并非由自己添加的数据下的表现。

  • 测试数据是否能正确加载,而不引发错误。

  • 在创建新数据库时快速配置好大部分要使用的功能。

数据库管理器允许你创建带有演示数据的数据库。通过命令行使用 --with-demo 也能实现同样的效果。

$ ./odoo-bin -h
Usage: odoo-bin [options]

Options:
--version             show program's version number and exit
-h, --help            show this help message and exit

Common options:
  [...]
  --with-demo         install demo data in new databases
[...]

$ ./odoo-bin --addons-path=... -d new-db -i base --with-demo

数据声明

清单文件

参考:与本主题相关的文档可在 模块清单文件 中找到。

数据可以用 CSV 或 XML 声明。每个包含数据的文件都必须添加到清单文件中,才能被加载。

在清单文件中用于添加新数据的键是 data (用于主数据)和 demo (用于演示数据)。这两个值都应是字符串列表,表示声明数据的文件的相对路径。

通常,演示数据放在 demo 文件夹中,视图和动作放在 views 文件夹中,安全相关数据放在 security 文件夹中,其他数据放在 data 文件夹中。

如果你的工作目录树如下所示:

estate
├── data
│   └── master_data.xml
├── demo
│   └── demo_data.xml
├── models
│   ├── *.py
│   └── __init__.py
├── security
│   └── ir.access.csv
├── views
│   └── estate_property_offer_views.xml
├── __init__.py
└── __manifest__.py

你的清单文件应该如下所示:

# -*- coding: utf-8 -*-

{
    "name": "Real Estate",
    "depends": [
        ...
    ],
    "data": [
        "security/ir.access.csv",  # CSV and XML files are loaded at the same place
        "views/estate_property_offer_views.xml",  # Views are data too
        "data/master_data.xml",  # Split the data in multiple files depending on the model
    ],
    "demo": [
        "demo/demo_data.xml",
    ]
    "application": True,
}

CSV

参考:与本主题相关的文档可在 CSV 数据文件 中找到。

声明简单数据最简单的方式是使用 CSV 格式。不过它在功能上有限:可用于简单模型的长列表,其他情况则更推荐 XML。

id,field_a,field_b,related_id:id
id1,valueA1,valueB1,module.relatedid
id2,valueA2,valueB2,module.relatedid

小技巧

你的 IDE 可能已有可为 CSV 文件提供语法高亮的扩展

练习

为 estate 模块添加一些标准的房地产类型:住宅、商业、工业和土地。这些数据应始终安装。

XML

参考:与本主题相关的文档可在 数据文件 中找到。

当要创建的数据较为复杂时,用 XML 来做会很有用,甚至是必需的。

<odoo>
  <record id="id1" model="tutorial.example">
    <field name="field_a">valueA1</field>
    <field name="field_b">valueB1</field>
  </record>

  <record id="id2" model="tutorial.example">
    <field name="field_a">valueA2</field>
    <field name="field_b">valueB2</field>
  </record>
</odoo>

练习

为 estate 模块创建一些演示数据。

字段

值

值

name

大别墅

拖车房屋

state

新建

已取消

description

又大又漂亮的别墅

拖车公园里的住宅

postcode

12345

54321

date_availability

2020-02-02

1970-01-01

expected_price

1,600,000

100,000

selling_price

120,000

bedrooms

6

1

living_area

100

10

facades

4

4

garage

True

False

garden

True

garden_area

100000

garden_orientation

南

数据扩展

在核心培训中,我们在 第 12 章:继承 章节看到可以继承(扩展)已有的视图。这是数据扩展的一个特例:任何数据都可以在模块中扩展。

当你在新模块中为已有模型添加新字段时,可能希望在你所依赖的模块创建的记录上填充这些字段。这可以通过给出要扩展的记录的 xml_id 来实现。它不会替换该记录,在这种情况下,我们会为两条记录都把 field_c 设为给定的值。

<odoo>
  <record id="id1" model="tutorial.example">
    <field name="field_c">valueC1</field>
  </record>

  <record id="id2" model="tutorial.example">
    <field name="field_c">valueC2</field>
  </record>
</odoo>

ref

关联字段可以用 ref 键来设置。该键的值是你要链接到的记录的 xml_id。请记住,xml_id 由首次声明该数据的模块名,后跟一个点,再后跟记录的 id 组成(如果你就在声明该数据的模块中,只写 id 也可以)。

<odoo>
  <record id="id1" model="tutorial.example">
    <field name="related_id" ref="module.relatedid"/>
  </record>
</odoo>

练习

为你创建的房产创建一些演示数据报价。

使用 base 中定义的合作伙伴创建报价

往来账

房产

价格

有效期

Azure Interior

大别墅

10000

14

Azure Interior

大别墅

1500000

14

Deco Addict

大别墅

1500001

14

练习

确保你的两个演示房产都以「住宅」作为房产类型创建。

eval

赋给字段的值并不总是简单的字符串,你可能需要计算它。它也可用于优化关联值的插入,或者是因为某个约束迫使你批量添加关联值。请参阅 :添加 X2many 字段。

<odoo>
  <record id="id1" model="tutorial.example">
    <field name="year" eval="datetime.now().year+1"/>
  </record>
</odoo>

练习

你添加的报价日期应始终相对于模块的安装日期。

function

在加载数据时,你可能还需要执行 Python 代码。

<function model="tutorial.example" name="action_validate">
    <value eval="[ref('demo_invoice_1')]"/>
</function>

练习

使用「接受报价」按钮验证其中一条演示数据报价。拒绝其余的。

添加 X2many 字段

参考:与本主题相关的文档可在 Command 中找到。

如果你需要在 One2many 或 Many2many 字段中添加关联数据,可以使用 Command 方法来实现。

<odoo>
  <record id="id1" model="tutorial.example">
    <field name="related_ids" eval="[
        Command.create({
            'name': 'My name',
        }),
        Command.create({
            'name': 'Your name',
        }),
        Command.link(ref('model.xml_id')),
    ]"/>
  </record>
</odoo>

练习

创建一个新的房产,但这次在链接到报价的 One2many 字段中直接创建一些报价。

访问数据

警告

你绝不应在演示数据声明之外访问演示数据,即使在测试中也不行。

访问主数据/演示数据有多种方式。

在 Python 代码中,你可以使用 env.ref(self, xml_id, raise_if_not_found=True) 方法。它返回与你指定的 xml_id 关联的记录集。

在 XML 中,你可以像这样使用 ref 键

<odoo>
  <record id="id1" model="tutorial.example">
    <field name="related_id" ref="module.relatedid"/>
  </record>
</odoo>

它会调用 ref 方法,并把返回记录的 id 存储到类型为 tutorial.example、id 为 id1 的记录的 related_id 字段上。

在 CSV 中,列标题必须加上 :id 或 /id 后缀。

id,parent_id:id,name
"child1","module.parent","Name1"
"child2","module.parent","Name2"
"child3","module.parent","Name3"

在 SQL 中则更复杂,请参阅 高级部分。

警告

数据随时可能被用户删除。编写代码时务必考虑这一点,做好防御性编程。

高级

什么是 XML id?

因为我们不希望在数据库的每一张 SQL 表中都加一列 xml_id,所以需要一种机制来存储它。这由 ir.model.data 模型完成。

它包含记录的名称(即 xml_id),以及定义它的模块、定义它的模型和该记录的 id。

不更新

用 noupdate 标志创建的记录在升级创建它们的模块时不会被更新,但如果记录尚不存在,则会被创建。

注解

odoo-bin -i module 会绕过此设置并始终加载数据。但通常不应在生产数据库上这样做。

<odoo noupdate="1">
  <record id="id1" model="model">
    <field name="fieldA" eval="True"/>
  </record>
</odoo>

以 SQL 导入

在某些情况下,直接用 SQL 导入是有意义的。不过并不推荐这样做,因为它会绕过 ORM 的所有特性、计算字段(包括元数据)以及 Python 约束。

注解

一般来说,使用原生 SQL 还会绕过 ACL,并增加注入的风险。

参考:Odoo 中的安全性