定义模块数据

Important

本教程是 服务器框架 101 教程的扩展。请确保您已完成该教程,并使用您构建的 estate 模块作为本教程练习的基础。

数据类型

主数据

主数据通常是模块技术或业务需求的一部分。换句话说,这类数据往往是模块正常运行所必需的。安装模块时始终会安装这些数据。

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

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

演示数据

除了模块正常运行所需的主数据之外,我们通常还希望有一些用于演示目的的数据:

  • 帮助销售代表快速进行演示。

  • 为开发人员提供一组可用数据,用于测试新功能,并查看这些新功能在他们自己可能没有添加过的数据下的效果。

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

  • 设置大多数功能,以便在创建新数据库时快速使用。

如果您未明确表示不需要演示数据,启动服务器时会自动加载演示数据。这可以在数据库管理器中或通过命令行完成。

$ ./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:
  [...]
  --without-demo=WITHOUT_DEMO
                      disable loading demo data for modules to be installed
                      (comma-separated, use "all" for all modules). Requires
                      -d and -i. Default is none
[...]

$ ./odoo-bin --addons-path=... -d db -i account --without-demo=all

数据声明

清单

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

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

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

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

如果您的工作树如下所示:

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

您的清单应如下所示:

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

{
    "name": "Real Estate",
    "depends": [
        ...
    ],
    "data": [
        "security/ir.model.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

Tip

您的 IDE 可能有提供 CSV 文件语法高亮显示的扩展:

Exercise

为 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>

Exercise

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

字段

值

值

name

Big Villa

Trailer home

state

New

Cancelled

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

South

数据扩展

在核心培训中,我们在 第 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>

Exercise

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

使用 base 模块中定义的业务伙伴创建报价

业务伙伴

房产

价格

有效期

Azure Interior

Big Villa

10000

14

Azure Interior

Big Villa

1500000

14

Deco Addict

Big Villa

1500001

14

Exercise

请确保您创建的两个演示房产的房源类型(Property Type)均设置为 Residential。

eval

分配给字段的值并不总是简单的字符串,您可能需要进行计算。它还可用于优化关联值的插入,或者因为约束要求您批量添加关联值。参见 :添加 X2many 字段。

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

Exercise

您添加的报价的日期应当始终相对于模块的安装时间。

function

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

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

Exercise

使用“接受报价”(Accept Offer)按钮验证其中一条演示数据报价,并拒绝其余报价。

添加 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>

Exercise

创建一个新的房源(Property),但这次直接在与报价(Offers)关联的 One2many 字段中创建一些报价。

访问数据

Warning

您不应在演示数据声明之外访问演示数据,即使在测试中也不应如此。

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

在 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 存储到 id 为 id1 的 tutorial.example 类型记录的 related_id 字段中。

在 CSV 中,列标题必须以 :id 或 /id 作为后缀。

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

在 SQL 中则更为复杂,参见 高级部分。

Warning

数据随时可能被用户删除。编写代码时要始终以防御性思维考虑到这一点。

高级

什么是 XML id?

因为我们不希望在数据库的每个 SQL 表中都有一列 xml_id,所以需要一种机制来存储它。这是通过 ir.model.data 模型实现的。

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

不更新

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

Note

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

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

以 SQL 方式导入

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

Note

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

参考:Odoo 中的安全