ORM(Object Relational Mapping)并不只是在odoo中有使用,在其它的开发框架中,与数据库的交互,基本上都是通过这样一种模式来进行处理。市面上也有很多专门用于ORM的框架,在odoo中,它是通过自己定义的一系列接口来实现了与PG数据库的映射关系。使用ORM的好处是开发人员可以不用关心数据库端的技术,与数据库的交互直接通过python对象就能完成,用面向对象的模式来对数据进行增删改查,而不需要熟悉SQL语法。当然也会有一定的局限性,通过ORM不能真正发挥数据库的优势,接口没有定义的功能就难以实现,比如PG数据库的分区表、复合索引、地理位置存储类型、触发器、存储过程等。
如果你有一定的数据库知识,就可以更好的理解odoo中的ORM模型定义,我们先来看一下最基础的ORM模型定义:
from odoo import models, fieldsclass AModel(models.Model):_name = 'a.model.name'field1 = fields.Char()
上面的field1是一个数据表字段名,在这里表示为模型的一个属性,这是odoo8以后采用的新的语法规则。模型继承odoo.models.Model(还有另外几种,后面会提到),注意字段名field1,这个名称在模型中是唯一的,后面不能有同名的属性或方法,名称的命名规范也要符合PG数据库的规范。
如果要给字段加一个说明,可以在Char()中增加string参数,如:
field1 = fields.Char(string="Field Label")
在odoo中,odoo.fields包含了所以可以使用的字段类型定义,不同的类型使用不同的方法调用,上面的Char,表示这是一个字符串类型的字段,后面我们会详细介绍一些常用的类型定义方法。
odoo.models.BaseModel是数据模型定义的基类,根据实际业务的使用情况,由此派生了三个不同的子类供框架使用:
Model:继承此对象用于定义常规的持久化数据表;
TransientModel:继承此对象用于定义常规数据表,但数据会定时清理;
AbstractModel:继承此对象用于定义一个抽象的模型,不会生成数据表,如odoo框架中的'ir.http'定义。
系统在安装APP模块的时候,会检查模块中继承以上三类对象的类,并根据设置的属性值,获取每个字段的类型/参数定义,用来对当前数据库进行实例化。
学过python的童鞋都知道在py方法中,都会有self这个变量,而odoo的数据模型定义类中,方法中的self,表示一个记录集,就是说self是表示一笔或多笔数据记录,这些记录可能是通过search、browse或其它自定义方法获取的。
为了保持业务的灵活度,模型接口也提供了丰富的参数属性,来应对复杂的业务需求,我们一起来看一下:
_auto:是否应该创建数据库表(默认值:True)。如果设置为False,则不会创建数据库表。
_table:当_auto为True时,创建的数据库表名,如果不指定,则默认是将_name属性的'.'替换为'_',如"res.partner"对应的表名是"res_partner"。
_sequence:ID字段的序列生成器,不指定时会自动生成。
_sql_constraints:生成数据库层级的约束,比如某列的值只能唯一,某个列的值必须大于0等。
_name:数据模型名称,一般用'.'来作命名空间层次分隔。
_description:数据模型的描述信息。
_inherit:指定当前数据模型的继承模型。
_inherits:指定当前数据模型的组合继承模型。
_rec_name:用于标记记录的字段,默认是name。
_order:指定记录结果集的排序字段,多个字段用','号分隔。
_check_company_auto:多公司环境下,是否调用_check_company方法检查公司的一致性。
_parent_name:用作父字段的many2one字段,这个应用于在SQL数据库中高效组织层次数据的查询解决方案。
_parent_store:是否自动计算_parent_path字段。
_fold_name:确定看板视图中折叠组的字段。
以上这些属性的定义是数据模型层面的,属性的改变是影响数据模型与数据库中数据表的定义,另外还有一部分是数据表字段的参数,通过这些参数可以定义不同业务需求的栏位,我们来看一下主要的参数说明:
string:字段的标签,如果不设置,则使用字段名称大写;
help:用户鼠标移到字段时,显示的提示;
readonly:字段在界面显示是否为只读;
required:字段是否为必填;
index:是否在数据库建立该字段的索引;
default:字段的默认值,可以是固定值,也可以是一个函数的返回值;
states:状态值与UI界面风格的映射关系,可用的界面风格属性有readonly、required、invisible;
groups:限制字段可访问的群组ID(群组定义的XML ID);
company_dependent:该字段是虚拟字段,根据当前公司不同,从ir.property中存取记录;
copy:复制记录时,该字段是否复制内容;
store:计算字段的结果是否存储在数据表中;
group_operator:在进行分组操作时,该字段使用什么分析方法,可选的值有array_agg、count、count_distinct、bool_and、bool_or、max、min、avg、sum;
group_expand:在当前字段分组时,扩展read_group的结果;
compute:字段为计算字段,指明计算用的方法名称;
inverse:计算型虚拟字段保存时的方法名称;
search:计算型虚拟字段搜索时的方法名称;
related:该字段是通过另一个many2one字段,来关联另一个数据模型的其它字段。
以上这些是一些字段定义的公用参数,另外要是具体到每个不同类型的字段,还有一些特定的参数,我们再来看一下一些特定的参数说明。
fields.Char,字符类型的字段,特定的参数有:
size:该字段存储的最大字符长度;
translate:该字段的值允许翻译;
fields.Float,浮点数类型的字段,特定的参数有:
digits:该浮点数的精度;
fields.Binary,二进制类型的字段,特定的参数有:
attachment:文档数据存储为附件(ir_attachment),还是存储在表的列中;
fields.Html,HTML语法字串的字段,特定的参数有:
sanitize:是否清理优化字段内容;
sanitize_tags:是否清理优化内容的标签元素;
sanitize_attributes:是否清理优化内容的元素属性;
sanitize_style:是否清理优化内容的元素风格;
fields.Image,图片二进制类型的字段,这个类是继承自fields.Binary,并增加了几个特定的参数:
max_width:图片的最大宽度;
max_height:图片的最大高度;
verify_resolution:是否应该验证图像分辨率以确保不超过最大图像分辨率;
fields.Selection:下拉选择字段,在数据表中,该字段是一般的字符类型,在UI界面录入时是一个下拉选择项,其特定的参数有:
selection:一个列表,列表中每个元素是一个元组或列表;
selection_add:继承其它的Selection字段类型时,可以扩展列表可选择项;
fields.Many2one:该字段在数据库表中是一个数字型的字段,该字段通过外键关联到另一个数据表的ID,其特定的参数有:
comodel_name:指定另一个数据模型的名称;
domain:指定客户端操作时,从comodel_name中关联记录的条件,比如我们需要关联业务伙伴数据表时,可能只想关联类型为公司的客户;
ondelete:当关联的comodel_name中记录被删除的时候,当前字段的处理方法,系统指定了三种,分别是'set null'(赋值为NULL)、'restrict'(提示错误)、 'cascade'(同时删除当前记录);
delegate:在使用_inherits继承时,将其设置为True以使目标模型的字段可从当前模型访问;
check_company:该字段需要使用 _check_company()验证公司;
fields.One2many,该字段为虚拟字段,不会生成实际的数据表内容,其特定的参数有:
comodel_name:关联的另一个数据模型名称;
inverse_name:在comodel_name模型号,与当前模型关联的many2one字段名;
domain:对数据模型comodel_name的过滤条件;
fields.Many2many,该字段类型定义,会在数据库中生成一张新的表,其特定的参数有:
comodel_name:关联的另一个数据模型名称;
relation:数据库新生成的表格名称;
column1:新生成的表格中,用于关联当前数据模型的字段名;
column2:新生成的表格中,用于关联comodel_name数据模型的字段名;
以上,除了通过各种属性和参数来提高ORM的灵活度以外,odoo还提供了一些用于ORM的装饰器,来提高开发的效率。我们再一起来围观一下:
api.depends,该装饰器一般用于计算字段的计算方法,指定哪些内容有变化时, 需要重新进行计算;
api.constrains,该装饰器用于增加一个py层级的约束控制,跟前面我们讲的数据库层级的约束不一样,这里的方法能提供更精细、更灵活的约束条件;
api.onchange,该装饰器用于增加一个栏位值变化的联动机制,某个字段值发生变化时,可以自动更新其它字段的值,也可以返回一个domain或警告信息;
api.model,该装饰器指明当前方法中的self不是一个结果集,而只是当前对象的一个普通方法;
Odoo在使用ORM与数据库进行交互时,会有一个环境变量,用来存取当前的上下文,主要包括数据库的游标、当前用户以及其它的自定义上下文。在数据模型的方法中,我们可以通过self.env来访问环境变量的内容,其主要的属性和方法有:
env.ref(),这是一个方法,可以根据一个外部的xml_id来获取记录的ORM模型;
env.lang,这是一个属性,返回当前作业环境的语言;
env.user,这个是返回当前作业用户;
env.company,这个是返回当前作业公司;
env.cr,这个是返回当前数据库的游标;
env.context,这个是返回其它自定义的上下文;
有些童鞋可能就会有疑问了,这些环境都是自动生成的,如果我在业务中,需要随时改变上下文参数,怎么办呢?这个不用着急,odoo也给我们提供了几个方法,来动态变更上下文。比如,我们在执行某个方法的时候,想用超级管理员身份来执行,就可以通过如下的方式来调用:
records.sudo().get_xml_data()
如果想增加自定义的上下文参数,就可以用如下的方式:
records.with_context({'key1':1}).get_xml_data()
最后,我们再来介绍一下一个不建议使用的功能。前面说了ORM就是让开发人员脱离数据库知识,来更方便的使用数据库功能。但有些时候执行效率和业务的复杂度还是没办法与直接的SQL语法相比。为了适用这部分人的需求,odoo也提供了直接操作数据库的接口,我们来看下面的例子:
self.env.cr.execute("some_sql", param1, param2, param3)
在游标对象中,提供了execute函数,可以直接来写SQL语句执行。如果是select语法,可以再通过self.env.cr.fetchall()来获取所有查询出来的结果集。但是这种方法需要开发人员熟悉数据库的操作,能正确理解ORM与数据库的对应关系,最重要的一点是通过这种方式的语法,已经绕开了Odoo的权限管控,需要谨慎使用。




