第 3 章:模型与基本字段

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

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

警告

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

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

对象关系映射

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

注解

目标:在本节结束时,应创建表 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.pycrm/models/__init__.py 中被导入(参见 此处 )。

  3. 文件夹 modelscrm/__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

类型

注解

目标:在本节结束时,应向表 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

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

简单字段的示例包括 BooleanFloatCharTextDateSelection

Exercise

向房地产属性表添加基本字段。

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

字段

类型

名称

字符

描述

文本

邮政编码(postcode)

字符

可用日期(date_availability)

日期

预期价格(expected_price)

Float

销售价格(selling_price)

Float

卧室数量(bedrooms)

整数

生活区域面积(living_area)

整数

立面数量(facades)

整数

车库(garage)

Boolean

花园(garden)

Boolean

花园面积(garden_area)

整数

花园朝向(garden_orientation)

选择

garden_orientation 字段必须有 4 个可能的值:’北’、’南’、’东’ 和 ‘西’。选择列表定义为元组列表,请参阅 此处 的示例。

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

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

连接到 psql 并检查表 estate_property 的结构。您会注意到表中还添加了一些额外的字段。我们将在稍后重新讨论它们。

通用属性

注解

目标:在本节结束时,表 estate_property 中的列 nameexpected_price 应设置为不可为空:

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, default: field’s name)

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

requiredbool ,默认值: False

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

helpstr ,默认值: ''

为用户界面中的用户提供长格式的帮助提示。

indexbool ,默认值: False

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

Exercise

为现有字段设置属性。

添加以下属性:

字段

属性

名称

依赖

预期价格(expected_price)

依赖

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

自动字段

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

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

idId

模型记录的唯一标识符。

create_dateDatetime

记录的创建日期。

create_uidMany2one

创建记录的用户。

write_dateDatetime

记录的最后修改日期。

write_uidMany2one

最后修改记录的用户。

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

1

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

2

编写原始SQL查询是可行的,但需要谨慎,因为这会绕过Odoo的所有身份验证和安全机制(authentication and security mechanisms)。