第四章 模型和基本的字段
在前一章的末尾,我们能够创建一个Odoo模块,不过,到现在为止它依然是一个空壳不能存储任何数据。在我们的房地产模块中,我们想在数据库里存储跟房屋有关的数据,比如名字,描述,价格,使用面积,Odoo 框架提供了便于跟数据交互的工具。
在继续学习之前,确认estate模块已经被安装,也就是说它必须出现在已经安装的应用列表中。
Warning
Do not use mutable global variables.
不要使用可变的全局变量。
单个Odoo实例可以在同一个python进程中并行运行多个数据库。这些数据库可能会安装不同的模块,因此我们不能依赖将根据安装的模块更新的全局变量。(没看懂)
Object-Relational Mapping
参考: 关于这个主题的文档可用在这里找到 Models API.
目标: 这一章节的最后,estate_property表应该被创建
$ psql -d rd-demo
rd-demo=# SELECT COUNT(*) FROM estate_property;
count
-------
0
(1 row)
Odoo一个关键的组件就是 ORM 层,该层避免人工写大量的sql并且提供了扩展和安全服务。
商业目标被声明为继承Model类的python类,会被自动的集成到持久化系统。
模型可用在他们的定义中通过设置属性进行配置,最重要的属性是_name, 它是必填项,在Odoo系统中定义了模型的名字,这里是一个关于模型最简单的定义。
from odoo import models
class TestModel(models.Model):
_name = "test.model"
这个定义已经足够让ORM生成一个名字叫test_model的数据表。 _name中的“.”会被ORM自动的转换成“—”,通常来说所有的模型都在一个模型目录中被定义,而且每个模型都定义在自己的python文件中。
现在看看crm_recurring_plan是怎么被定义的,并且相关文件是怎么被引入的?
1、文件定义
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields, models
class RecurringPlan(models.Model):
_name = "crm.recurring.plan"
_description = "CRM Recurring revenue plans"
_order = "sequence"
name = fields.Char('Plan Name', required=True, translate=True)
number_of_months = fields.Integer('# Months', required=True)
active = fields.Boolean('Active', default=True)
sequence = fields.Integer('Sequence', default=10)
_sql_constraints = [
('check_number_of_months', 'CHECK(number_of_months >= 0)', 'The number of month can\'t be negative.'),
]
2 引入上面这个文件
#crm/models/__init__.py
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import res_users
from . import calendar
from . import crm_lead
from . import crm_lost_reason
from . import crm_stage
from . import crm_team
from . import res_config_settings
from . import res_partner
from . import digest
from . import crm_lead_scoring_frequency
from . import utm
from . import crm_recurring_plan #注意这里
3 引入模型文件目录
# crm/__init__.py
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import controllers
from . import models # 注意这里
from . import report
from . import wizard
from odoo import api, SUPERUSER_ID
练习:
定义estate.property
model.
对python文件的修改都需要重启Odoo服务,当我们重启服务的时候,可用使用-u 选项
-u estate 意思是我们想升级estate模块,也就是说ORM将数据模型的变化同步到数据库中,-d 指定数据库,
-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来检测表有没有被创建。
练习:
Add a description.
Add a _description
避免其中一条报警。
模型字段
参考: 关于这个主题的文档在这里 Fields API.
字段被用来定义模型能够存储什么和存储在什么位置,字段被定义为model类的属性。
from odoo import fields, models
class TestModel(models.Model):
_name = "test.model"
_description = "Test Model"
name = fields.Char()
name字段是一个字符串,它将被表现为python的unicode字符串和数据库里的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
有两大类字段:
简单字段,作为原子类型的值直接存储在模型的数据表中
关系字段:关联到自己或者其他模型的记录上。
简单字段类型有 Boolean
, Float
, Char
, Text
, Date
and Selection
.
练习
将下面的字段加到Real Estate Property table.
Field | Type |
---|---|
name | Char |
description | Text |
postcode | Char |
date_availability | Date |
expected_price | Float |
selling_price | Float |
bedrooms | Integer |
living_area | Integer |
facades | Integer |
garage | Boolean |
garden | Boolean |
garden_area | Integer |
garden_orientation | Selection |
花园朝向有四个可选的值:‘North’, ‘South’, ‘East’ and ‘West’. 这里有个例子,注意元组中的两个值必须都是字符串。
lead_type = fields.Selection(
string='Type',
selection=[('lead', 'Lead'), ('opportunity', 'Opportunity')],
help="Type is used to separate Leads and Opportunities")
修改完后重启odoo服务,别忘了-u选项
连接到psql并且检查estate_property表结构,你会发现多了一些额外的字段也被添加进了表中,我们将在后面介绍他们。
通用属性
目标: 在这一章节的末尾,name和expected_price 将被设置成不能为null
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)UI中的字段标签.
-
required
(bool
, default:False
)如果为真,该字段不能为空,它必须有一个默认值或者当创建一条记录的时候永远给一个值。
-
help
(str
, default:''
)在UI中提供一个长格式的帮助说明
-
index
(bool
, default:False
)请求Odoo为该字段生成索引
练习
Field | Attribute |
---|---|
name | required |
expected_price | required |
重启服务后,就能看到效果。
Automatic Fields
参考: 关注这个主题的文档在这里 Automatic fields.
你应该注意到了,你的模型多了几个没定义的字段。odoo在所有的模型中都会创建几个字段,这些字段被系统管理并且不能被修改,但是如果需要的话可以读取。
-
id
(Id
)模型中一条记录的唯一标识。
-
create_date
(Datetime
)记录的创建时间
-
create_uid
(Many2one
)记录的创建者
-
write_date
(Datetime
)最近一次修改记录的时间
-
write_uid
(Many2one
)最后一次修改记录人
现在我们创建了我们的第一个模型,让我们增加一些安全机制。
直接写sql查询是可行的,不过这将绕过Odoo的身份认真和安全机制。
作业
在模块目录中新建model目录:
在模块的init 文件中 引入
from . import model
在model下建立init文件
同时引入
from . import estate_property
这样,数据就关联起来了。
estate_property.py
from odoo import models,fields
class EstateProperty(models.Model):
"""
"""
_name = "estate.property"
_description = "房屋信息数据"
name = fields.Char(string="名称",required=True)
description = fields.Text(string="描述")
postcode = fields.Char(string="邮编")
date_availability = fields.Date(string="到期时间")
expected_price = fields.Float(string="期望价格",required=True)
selling_price = fields.Float(string="销售价格")
bedrooms = fields.Integer(string="房间数量")
living_area = fields.Text(string="居住面积")
facades = fields.Integer(string="朝向")
garage = fields.Boolean(string="是否带车库")
garden = fields.Text(string="是否带花园")
garden_area = fields.Integer(string="花园面积")
garden_orientation = fields.Selection(string="花园朝向",selection=("东","南","西","北"))