定义模块数据

重要

本教程是 开始 教程的扩展。确保您已经完成了该教程,并使用您构建的 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

小技巧

你的 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 模块创建一些演示数据。

字段

名称

大别墅

拖车房屋

状态

新建

已取消

描述

一座漂亮而宽敞的别墅

拖车公园里的家

邮政编码

12345

54321

可用日期

2020年02月02日

1970年01月01日

期望价格

160万

10万

销售价格

120,000

卧室数

6

1

生活区域

100

10

门面

4

4

车库

花园

花园面积

十万

花园方向

数据扩展

在核心培训中,我们在 第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 中定义的合作伙伴创建报价

合作伙伴

房地产

价格

有效期

蓝色内饰

大别墅

10000

14

蓝色内饰

大别墅

一百五十万

14

装饰迷

大别墅

1500001

14

Exercise

请确保您的两个演示属性的属性类型均设置为住宅。

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

使用“接受报价”按钮验证其中一个演示数据报价。拒绝其他报价。

添加 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字段中直接创建一些报价。

访问数据

警告

永远不要在演示数据声明之外访问演示数据,即使在测试中也不行。

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

在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 will bypass this setting and always load the data. But normally one shouldn’t do this on a production database.

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

导入为SQL

在某些情况下,直接在SQL中进行导入是有意义的。但是,这是不鼓励的,因为它绕过了ORM的所有功能,包括计算字段(包括元数据)和Python约束。

注解

通常使用原始 SQL 也会绕过 ACL 并增加注入风险。

参考: Odoo中的安全性