定义模块数据¶
Important
本教程是 入门 教程的扩展。请确保您已完成该教程,并以您构建的 estate 模块为基础完成本教程的练习。如果想从干净的起点开始,请从 technical-training-solutions 仓库拉取 16.0-core 分支。
数据类型¶
主数据¶
主数据通常是模块技术或业务需求的一部分。换句话说,这类数据往往是模块正常运行所必需的。安装模块时始终会安装这些数据。
我们之前已经接触过技术数据,因为我们定义过 视图 和 动作。这些是主数据的一种。
除技术数据外,还可以定义业务数据,例如国家、货币、计量单位,以及完整的国家本地化(法定报表、税定义、会计科目表)等等……
演示数据¶
除了模块正常运行所需的主数据之外,我们通常还希望有一些用于演示目的的数据:
帮助销售代表快速进行演示。
为开发人员提供一组可用数据,用于测试新功能,并查看这些新功能在他们自己可能没有添加过的数据下的效果。
测试数据是否正确加载且不引发错误。
设置大多数功能,以便在创建新数据库时快速使用。
如果您未明确表示不需要演示数据,启动服务器时会自动加载演示数据。这可以在数据库管理器中或通过命令行完成。
$ ./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
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 |
大别墅 |
拖车房屋 |
state |
新建 |
Canceled |
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 |
南 |
数据扩展¶
在核心培训中,我们在 第 13 章:继承 一章中看到,可以继承(扩展)现有视图。这是数据扩展的一个特例:任何数据都可以在模块中被扩展。
在新模块中为现有模型添加新字段时,您可能希望为所依赖模块中创建的记录填充这些字段的值。这可以通过提供要扩展的记录的 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 |
大别墅 |
10000 |
14 |
Azure Interior |
大别墅 |
1500000 |
14 |
Deco Addict |
大别墅 |
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
您添加的报价的日期应当始终相对于模块的安装时间。
search¶
有时,您需要调用 ORM 来执行 search。这在 CSV 格式中是无法实现的。
<odoo>
<record id="id1" model="account.move.line">
<field name="account_id" search="[
('user_type_id', '=', ref('account.data_account_type_direct_costs')),
('company_id', '=', obj().env.company.id)]
"/>
</record>
</odoo>
在这段代码片段中之所以需要这样做,是因为主数据取决于所安装的本地化设置。
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 约束。