暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

odoo13的ORM API

Odoo哥 2020-04-22
508

ORM(Object Relational Mapping)并不只是在odoo中有使用,在其它的开发框架中,与数据库的交互,基本上都是通过这样一种模式来进行处理。市面上也有很多专门用于ORM的框架,在odoo中,它是通过自己定义的一系列接口来实现了与PG数据库的映射关系。使用ORM的好处是开发人员可以不用关心数据库端的技术,与数据库的交互直接通过python对象就能完成,用面向对象的模式来对数据进行增删改查,而不需要熟悉SQL语法。当然也会有一定的局限性,通过ORM不能真正发挥数据库的优势,接口没有定义的功能就难以实现,比如PG数据库的分区表、复合索引、地理位置存储类型、触发器、存储过程等。

如果你有一定的数据库知识,就可以更好的理解odoo中的ORM模型定义,我们先来看一下最基础的ORM模型定义:

from odoo import models, fields
class 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的权限管控,需要谨慎使用。

文章转载自Odoo哥,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论