Skip to content

06. 模型和字段 ​

在上章节中,我们已经学习并创建了一个新的 Odoo 模块。但它仍是一个空壳,没有菜单也没有任何操作,也不能存储任何数据。接下来将学习如何在 estate 模块中,实现将房产相关的信息(名称、描述、价格、居住面积等)存储在数据库中。

在继续本小节实践之前,请确保 estate 模块已安装,即在“应用程序”列表中, estate 模块的状态应显示为“已安装”。

03-estate-installed.png

注:请勿使用可变的全局变量。

一个 Odoo 实例可以在同一个 Python 进程中并行运行多个数据库。由于每个数据库可能安装了不同的模块,因此不能依赖那些会根据已安装模块而发生变化的全局变量。

对象关系映射 ​

首先,创建名称为 estate_property 的数据库表:

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

Odoo 的一个关键组件是 ORM 层。ORM 层避免了手动编写大部分 SQL 的需求,并提供了可扩展性和安全服务。业务对象标准的做法是继承自 odoo.models.Model 类,从而将业务对象集成到自动持久化系统中。

可以通过在模型定义中设置属性来配置模型。一个非常重要的属性是 _name,该属性为必填项,用于定义模型在 Odoo 系统中的名称。以下是一个模型的最简定义:

py
from odoo import models

class Property(models.Model):
    _name = "estate_property"
    _description = "The Real Estate Advertisement module."

以上定义让 ORM 自动生成一个名称为 estate_property 的数据库表。按照惯例,所有模型都位于模块目录下的 models 目录中,且每个模型都在各自的 Python 文件中定义。

定义房地产属性模型

下面是 estate_property 表相应的文件和文件夹。

python
from odoo import models

class Property(models.Model):
    _name = "estate.property"
    _description = "房地产模型"

以上定义让 ORM 自动生成一个名称为 estate_property 的数据库表,可以看到数据库表名与模型中 _name 属性的值相关联,Odoo 系统把 estate.property 替换成为 estate_property 来命名数据库中表的名称。按照惯例,所有模型都位于模块目录下的 models 目录中,且每个模型都在各自的 Python 文件中定义。

定义房地产属性模型

实践一: 创建 estate.property 模型相应的文件和文件夹。具体如下所示:

创建模型文件:estate/models/property.py,详细内容如下:

python
from odoo import models

class Property(models.Model):
    _name = "estate.property"

创建 estate/__init.py 文件,并导入文件模型包:

python
from . import models

模型包文件:estate/models/__init.py

python
from . import property

文件创建完成后,使用以下脚本重启服务器:

powershell
.\venv\Scripts\python.exe odoo-bin.py `
--db_host 127.0.0.1 --db_port=5432 -r userodoo `
-w 123456  -d odoodb --addons-path=addons -u estate `
--limit-time-real=120000

或者( bat 脚本):

bat
.\venv\Scripts\python.exe odoo-bin.py ^
--db_host 127.0.0.1 --db_port=5432 -r userodoo ^
-w 123456  -d odoodb --addons-path=addons -u estate ^
--limit-time-real=120000

-u estate 表示要升级 estate 模块,即 ORM 将应用数据库模式变更,它会创建模型中对应的新表。-d odoodb 表示在 odoodb 这个数据库上执行升级。-u 必须始终与 -d 配合使用。

注:对 Python 文件进行任何修改都需要重启 Odoo 服务器,才能生效。

启动过程中,可以会看到类似以下警告内容:

shell
2026-08-03 08:03:15,217 8956 INFO doodb odoo.modules.loading: Loading module estate (2/15)
2026-08-03 08:03:15,278 8956 WARNING doodb odoo.registry: The model estate.property has no _description
2026-08-03 08:03:15,323 8956 INFO doodb odoo.registry: module estate: creating or updating database tables
2026-08-03 08:03:15,777 8956 INFO doodb odoo.addons.base.models.ir_module: module estate: no translation for language zh_CN
2026-08-03 08:03:15,811 8956 WARNING doodb odoo.modules.loading: The models ['estate.property'] have no access rules in module estate, consider adding some, like:
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
estate.access_estate_property,access_estate_property,estate.model_estate_property,base.group_user,1,0,0,0
2026-08-03 08:03:15,857 8956 INFO doodb odoo.modules.loading: Module estate loaded in 0.64s, 37 queries (+37 other)
2026-08-03 08:03:15,864 8956 WARNING doodb odoo.registry: The model estate.property has no _description
2026-08-03 08:03:15,937 8956 WARNING doodb odoo.registry: The model estate.property has no _description
2026-08-03 08:03:16,242 8956 WARNING doodb odoo.registry: The model estate.property has no _description
2026-08-03 08:03:16,256 8956 INFO doodb odoo.modules.loading: 15 modules loaded in 1.04s, 37 queries (+37 extra)

从上面输出结果中可以看到,已经正常启动!为了保险起见可使用 psql 查看创建的表信息再次确认。数据库表中缺少了一个字段,需要在模型中添加一个 _description 字段,即可消除其中一条警告。

到了这个阶段,数据库中已经创建了 estate_property,表的结构如下:

sql
CREATE TABLE "public"."estate_property" (
  "id" int4 NOT NULL DEFAULT nextval('estate_property_id_seq'::regclass),
  "create_uid" int4,
  "write_uid" int4,
  "create_date" timestamp(6),
  "write_date" timestamp(6),
  CONSTRAINT "estate_property_pkey" PRIMARY KEY ("id"),
  CONSTRAINT "estate_property_create_uid_fkey" FOREIGN KEY ("create_uid") REFERENCES "public"."res_users" ("id") ON DELETE SET NULL ON UPDATE NO ACTION,
  CONSTRAINT "estate_property_write_uid_fkey" FOREIGN KEY ("write_uid") REFERENCES "public"."res_users" ("id") ON DELETE SET NULL ON UPDATE NO ACTION
)
;

ALTER TABLE "public"."estate_property"
  OWNER TO "userdoo";

COMMENT ON COLUMN "public"."estate_property"."create_uid" IS 'Created by';

COMMENT ON COLUMN "public"."estate_property"."write_uid" IS 'Last Updated by';

COMMENT ON COLUMN "public"."estate_property"."create_date" IS 'Created on';

COMMENT ON COLUMN "public"."estate_property"."write_date" IS 'Last Updated on';

COMMENT ON TABLE "public"."estate_property" IS 'estate.property';

模型属性 ​

属性(也叫字段,一般 Python 类中叫属性,数据表中叫字段)用于定义模型可以存储哪些数据以及这些数据存储的位置。在模型类中可以定义各种属性。如下一个简单的示例:

python
from odoo import fields, models

class Property(models.Model):
    _name = "estate_property"
    _description = "房地产模型"

    name = fields.Char()

name 字段是 Property 类的一个字符型属性,系统表示为 Python 的 Unicode 字符串,在数据库中存储为 SQL 的 VARCHAR 类型。

实践二: 在 estate.property 模型中添加一些属性,这些属性最终会被创建并存储在数据库表中。属性大致可分为两类:

  • “简单”属性,即直接存储在模型表中的原子值;
  • “关系”属性,用于链接(同一模型或不同模型的)记录。

在 estate.property 模型中添加基本属性。在对应的 estate_property 表中添加以下基本字段:

字段类型备注
nameChar名称
descriptionText描述
postcodeChar邮编
date_availabilityDate有效期
expected_priceFloat期望价
selling_priceFloat实际售价
bedroomsInteger房间数
living_areaInteger居住面积
facadesBoolean立面/外表
has_garageBoolean是否有车库
has_gardenBoolean是否有庭院
garden_areaInteger庭院面积
garden_orientationSelection朝向

garden_orientation 字段包含 4 个可能的取值:“朝北”、“朝南”、“朝东”和“朝西”。下拉列表定义为元组列表,示例请参见 - 此处 。

继续打开文件:models/property.py,在 Property 类中添加以下属性:

python
from odoo import models, fields, api, exceptions
from odoo.tools.float_utils import float_compare, float_is_zero
from odoo import models
from datetime import datetime

class Property(models.Model):
    _name = "estate.property"
    _description = "房地产模型"    

    name = fields.Char("标题/名称", required=True, translate=True)
    description = fields.Text("描述", )
    postcode = fields.Char("邮编")
    date_availability = fields.Date(string="有效期", required=True, default=datetime.now(), copy=False)
    expected_price = fields.Float("期望价格", required=True)
    selling_price = fields.Float("实际售价", readonly=True, copy=False)
    bedrooms = fields.Integer("房间数量", default=2)
    living_area = fields.Integer("室内面积(M²)")
    has_garage = fields.Boolean("是否有车库")
    has_garden = fields.Boolean("是否有花园")
    garden_area = fields.Integer("花园面积(M²)", copy=False)
    garden_orientation = fields.Selection(
        string='朝向',
        selection=[('north', '朝北'), ('south', '朝南'), ('east', '朝东'), ('west', '朝西')],
        help="“朝向”可用于区分潜在客户和商机")

Odoo 会根据上面模型的定义自动创建相应的数据库表:estate_property。数据库表字段的数据类型有:布尔型、浮点型、字符型、文本型、日期型和选择型。

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

shell
$ ./odoo-bin --addons-path=addons,dev-addons/ -d odoodb -u estate

连接到 psql 并查看 estate_property 表的结构,就会发现新增了几个字段。

shell
$ psql -d ododb

ododb=# \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                     |           |          |
has_garage         | boolean                     |           |          |
has_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

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

简单字段的示例包括:布尔型、浮点型、字符型、文本型、日期型和选择型。

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

字段类型备注
nameChar
descriptionText
postcodeChar
date_availabilityDate
expected_priceFloat
selling_priceFloat
bedroomsInteger
living_areaInteger
facadesBoolean
garageBoolean
garden_areaInteger
garden_orientationSelection

garden_orientation字段必须包含 4 个可能的取值:“North”、“South”、“East”和“West”。下拉列表定义为元组列表,示例请参见此处。

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

shell
$ ./odoo-bin --addons-path=addons,../dev-addons/ -d dev-demo -u estate

连接到 psql 并查看 estate_property 表的结构,就会发现该表还新增了几个字段。我们稍后会再讨论这些字段。

常见属性 ​

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

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

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

py
name = fields.Char(required=True)

某些属性适用于所有字段,以下是最常见的属性:

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

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

  • required (bool, 默认值:False)

若为 True,则该字段不能为空。它必须具有默认值,或者在创建记录时必须始终提供值。

  • help (str, 默认值: '')

在 UI 中为用户提供长篇帮助提示。

  • index (bool, 默认值: False)

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

为现有字段设置属性。添加以下属性(required=True):

字段属性备注
namerequired
expected_pricerequired

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

自动字段 ​

参考:有关此主题的文档请参见“自动字段”。

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

  • id (Id) - 该模型记录的唯一标识符。

  • create_date (Datetime) - 记录的创建日期时间。

  • create_uid (Many2one) - 创建该记录的用户。

  • write_date (Datetime) - 记录的最后修改日期时间。

  • write_uid (Many2one) - 最后修改该记录的用户。

虽然可以编写原始 SQL 查询,但需谨慎操作,因为这会绕过 Odoo 的所有身份验证和安全机制。

最近更新