第 4 章:模型与基础字段

在 上一章 的结尾,我们成功创建了一个 Odoo 模块。但此时它仍然是一个空壳,无法存储任何数据。在我们的房地产模块中,我们希望将与房产相关的信息(名称、描述、价格、居住面积……)存储在数据库中。Odoo 框架提供了简化数据库交互的工具。

在继续练习之前,请确保 estate 模块已安装,即它必须在应用列表中显示为“已安装”。

Warning

不要使用可变的全局变量。

一个 Odoo 实例可以在同一个 Python 进程中并行运行多个数据库。不同的模块可能安装在这些数据库中的某一个上,因此我们不能依赖于会根据已安装模块而更新的全局变量。

对象关系映射

参考:与此主题相关的文档可在 模型 API 中找到。

Note

目标:在本节结束时,应已创建 estate_property 表:

$ psql -d rd-demo
rd-demo=# SELECT COUNT(*) FROM estate_property;
count
-------
    0
(1 row)

Odoo 的一个关键组件是 ORM 层。该层避免了手动编写大部分 SQL,并提供了可扩展性和安全服务2。

业务对象声明为继承 Model 的 Python 类,这会将它们集成到自动持久化系统中。

可以通过在定义中设置属性来配置模型。最重要的属性是 _name,它是必需的,用于定义模型在 Odoo 系统中的名称。以下是一个模型的最小定义:

from odoo import models

class TestModel(models.Model):
    _name = "test_model"

这样的定义足以让 ORM 生成一个名为 test_model 的数据库表。按照惯例,所有模型都位于 models 目录中,并且每个模型都在自己的 Python 文件中定义。

看一看 crm_recurring_plan 表是如何定义的,以及相应的 Python 文件是如何导入的:

  1. 该模型定义在文件 crm/models/crm_recurring_plan.py 中(见 此处)

  2. 文件 crm_recurring_plan.py 导入于 crm/models/__init__.py 见 此处)

  3. 文件夹 models 导入于 crm/__init__.py 见 此处)

Exercise

定义房地产物业模型。

根据 CRM 模块中给出的示例,为 estate_property 表创建相应的文件和文件夹。

文件创建好后,为 estate.property 模型添加一个最小定义。

对 Python 文件的任何修改都需要重启 Odoo 服务器。当我们重启服务器时,我们将添加 -d 和 -u 参数:

$ ./odoo-bin --addons-path=addons,../enterprise/,../tutorials/ -d rd-demo -u estate

-u estate 表示我们想要升级 estate 模块,即 ORM 将应用数据库架构更改。在本例中,它会创建一个新表。-d rd-demo 表示应在 rd-demo 数据库上执行升级。-u 应始终与 -d 结合使用。

启动期间,您应该会看到以下警告:

...
WARNING rd-demo odoo.models: The model estate.property has no _description
...
WARNING rd-demo odoo.modules.loading: The model estate.property has no access rules, consider adding one...
...

如果是这样,那就没问题了!为确保无误,请按照 目标 中的演示用 psql 再次检查。

Exercise

添加描述。

为您的模型添加 _description,以消除其中一条警告。

模型字段

参考:与此主题相关的文档可在 字段 API 中找到。

字段用于定义模型可以存储什么以及存储在哪里。字段定义为模型类中的属性:

from odoo import fields, models

class TestModel(models.Model):
    _name = "test_model"
    _description = "Test Model"

    name = fields.Char()

name 字段是一个 Char,它将表示为 Python unicode str 和 SQL VARCHAR。

类型

Note

目标:在本节结束时,应已向 estate_property 表添加几个基本字段:

$ psql -d rd-demo

rd-demo=# \d estate_property;
                                            Table "public.estate_property"
    Column       |            Type             | Collation | Nullable |                   Default
--------------------+-----------------------------+-----------+----------+---------------------------------------------
id                 | integer                     |           | not null | nextval('estate_property_id_seq'::regclass)
create_uid         | integer                     |           |          |
create_date        | timestamp without time zone |           |          |
write_uid          | integer                     |           |          |
write_date         | timestamp without time zone |           |          |
name               | character varying           |           |          |
description        | text                        |           |          |
postcode           | character varying           |           |          |
date_availability  | date                        |           |          |
expected_price     | double precision            |           |          |
selling_price      | double precision            |           |          |
bedrooms           | integer                     |           |          |
living_area        | integer                     |           |          |
facades            | integer                     |           |          |
garage             | boolean                     |           |          |
garden             | boolean                     |           |          |
garden_area        | integer                     |           |          |
garden_orientation | character varying           |           |          |
Indexes:
    "estate_property_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
    "estate_property_create_uid_fkey" FOREIGN KEY (create_uid) REFERENCES res_users(id) ON DELETE SET NULL
    "estate_property_write_uid_fkey" FOREIGN KEY (write_uid) REFERENCES res_users(id) ON DELETE SET NULL

字段有两大类别:“简单”字段,即直接存储在模型表中的原子值;“关系”字段,即链接记录(相同或不同模型的记录)的字段。

简单字段的例子有 Boolean、Float、Char、Text、Date 和 Selection。

Exercise

向房地产物业表添加基本字段。

在表中添加以下基本字段:

字段

类型

name

字符型

description

文本

postcode

字符型

date_availability

日期

expected_price

浮动

selling_price

浮动

bedrooms

整数

living_area

整数

facades

整数

garage

布尔值

garden

布尔值

garden_area

整数

garden_orientation

选择

garden_orientation 字段必须有 4 个可能的值:’North’、’South’、’East’ 和 ‘West’。选择列表定义为元组列表,示例见 此处。

将字段添加到模型后,使用 -u estate 重启服务器

$ ./odoo-bin --addons-path=addons,../enterprise/,../tutorials/ -d rd-demo -u estate

连接到 psql 并检查 estate_property 表的结构。您会注意到表中还添加了几个额外的字段。我们稍后会重新审视它们。

通用属性

Note

目标:在本节结束时,estate_property 表中的 name 和 expected_price 列应为非空(not nullable):

rd-demo=# \d estate_property;
                                            Table "public.estate_property"
    Column       |            Type             | Collation | Nullable |                   Default
--------------------+-----------------------------+-----------+----------+---------------------------------------------
...
name               | character varying           |           | not null |
...
expected_price     | double precision            |           | not null |
...

与模型本身一样,字段也可以通过传递配置属性作为参数来进行配置:

name = fields.Char(required=True)

有些属性可用于所有字段,以下是最常用的几种:

string (str,默认值:字段名)

字段在界面中的标签(用户可见)。

required (bool,默认值:False)

如果为 True,则该字段不能为空。它必须有默认值,或者在创建记录时始终被赋予一个值。

help (str,默认值:'')

在界面中为用户提供详细的帮助提示信息。

index (bool,默认值:False)

请求 Odoo 在该列上创建 数据库索引 。

Exercise

为现有字段设置属性。

添加以下属性:

字段

属性

name

required

expected_price

required

重启服务器后,这两个字段应为非空。

自动字段

参考:与此主题相关的文档可在 自动字段 中找到。

您可能已经注意到,您的模型中有一些您从未定义的字段。Odoo 会在所有模型中创建一些字段1。这些字段由系统管理,不能写入,但在有用或必要时可以读取:

记录标识:id (Id)

模型中某条记录的唯一标识符。

创建日期:create_date (Datetime)

记录的创建日期。

创建用户:create_uid (Many2one)

创建该记录的用户。

修改日期:write_date (Datetime)

记录的最后修改日期。

修改用户:write_uid (Many2one)

最后修改该记录的用户。

现在我们已经创建了第一个模型,让我们 添加一些安全设置 吧!

1

可以 禁用某些字段的自动创建

2

可以直接编写原生 SQL 查询,但需要谨慎,因为这会绕过所有 Odoo 身份验证和安全机制。