作者 主题: OpenERP对象定义详解(连载中...)  (阅读 5107 次)

NewZN

  • 布道达人
  • ****
  • 帖子: 256
  • 人气: 2
    • OSCG
OpenERP对象定义详解(连载中...)
« 于: 九月 11, 2010, 05:30:17 下午 »
本连载准备详细解密OpenERP的对象定义,内容包括深入理解OpenERP的对象,对象详细定义、字段详细定义、对象的预定义方法、OpenERP的Services等部分,力求每一部分都详细说明。
要完全理解OpenERP的开发,需要理解这么几个部分:OpenERP的对象,OpenERP的视图及Action,OpenERP的Workflow,OpenERP的Report。理解了这几部分就可以比较彻底的理解OpenERP开发平台了,本连载力求解说第一部分。
本连载将会是OpenERP对象开发的“史上最牛”手册,本连载的内容综合了OpenERP的官方手册,以及大量的“民间野史”,再加以大量的代码研究和亲自实验,终熬制成本手册。
本连载可算做OpenERP开发的提高篇,希望读者对象先学习完《OpenERP应用和开发基础》,参见http://shine-it.net/index.php/topic,882.0.html
第一部分:深入理解OpenERP的对象
由于写作顺序的缘故,放到后面去了,参见后面跟帖。

第二部分:OpenERP对象定义的属性详细解说
    OpenERP的对象定义的一般形式如下。
程序代码: [选择]
class name_of_the_object(osv.osv):
    _name = 'xxx'
    ......
name_of_the_object()

#Sample:
class qingjd(osv.osv):
    _name = 'qingjia.qingjd'
    _description = '请假单'
    _columns = {
        'shenqr': fields.many2one('hr.employee', '申请人', required=True),
    }
qingjd()

对象定义的完整属性如下:
必须属性
       _name     
       _columns 
可选属性
       _table 
       _description
       _defaults
       _order
       _rec_name 
       _auto 
       _constraints
       _sql_constraints
       _inherit
       _inherits

下面详细解说各个属性。
_auto: 是否自动创建对象对应的Table,缺省值为: True。当安装或升级模块时,OpenERP会自动在数据库中为模块中定义的每个对象创建相应的Table。当这个属性设为False时,OpenERP不会自动创建Table,这通常表示数据库表已经存在。例如,当对象是从数据库视图(View)中读取数据时,通常设为False。
_columns: 定义对象的字段,系统会字段为这里定义的每个字段在数据库表中创建相应的字段。关于字段(Fields)的定义,参见后文。
_constraints: 定义于对象上的约束(constraints),通常是定义一个检查函数,关于约束的详细说明,参见后文。
_defaults: 定义字段的缺省值。当创建一条新记录(record or resource)时,记录中各字段的缺省值在此定义。
_description: 对象说明性文字,任意文字。
_log_access: 是否自动在对应的数据表中增加create_uid, create_date, write_uid, write_date四个字段,缺省值为True,即字段增加。这四个字段分布记录record的创建人,创建日期,修改人,修改日期。这四个字段值可以用对象的方法(perm_read)读取。
_name: 对象的唯一标识符,必须是全局唯一。这个标识符用于存取对象,其格式通常是"ModuleName.ClassName",对应的,系统会字段创建数据库表"ModuleName_ClassName"。
_order: 定义search()和read()方法的结果记录的排序规则,和SQL语句中的order 类似,缺省值是id,即按id升序排序。详细说明参见后文。
_rec_name: 标识record name的字段。缺省情况(name_get没被重载的话)方法name_get()返回本字段值。_rec_name通常用于记录的显示,例如,销售订单中包含业务伙伴,当在销售订单上显示业务伙伴时,系统缺省的是显示业务伙伴记录的_rec_name。
_sequence: 数据库表的id字段的序列采集器,缺省值为: None。OpenERP创建数据库表时,会自动增加id字段作为主键,并自动为该表创建一个序列(名字通常是“表名_id_seq”)作为id字段值的采集器。如果想使用数据库中已有的序列器,则在此处定义序列器名。
_sql: _auto为True时,可以在这里定义创建数据库表的SQL语句。不过5.0以后好像不支持了,不建议使用。
_sql_constraints: 定义于对象上的约束(constraints),和SQL文中的约束类似,关于约束的详细说明,参见后文。
_table: 待创建的数据库表名,缺省值是和_name一样,只是将"."替换成"_"。
_inherits:
_inherit: _inherits和_inherit都用于对象的继承,详细说明参见后文。


_constraints
    _constraints可以灵活定义OpenERP对象的约束条件,当创建或更新记录时,会触发该条件,如果条件不符合,则弹出错误信息,拒绝修改。
    _constraints的定义格式:
    [(method, 'error message', list_of_field_names), ...]

· method: 是对象的方法,该方法的格式为:def _name_of_the_method(self, cr, uid, ids): −> True|False
· error message: 不符合检查条件(method返回False)时的错误信息。
· list_of_field_names: 字段名列表,这些字段的值会出现在error message中。通常列出能帮助用户理解错误的字段。

    _constraints的例子:
程序代码: [选择]
def _constraint_sum(self, cr, uid, ids):
    cr.execute('SELECT a.currency_id
        FROM account_move m, account_move_line l, account_account a
        WHERE m.id=l.move_id AND l.account_id=a.id AND m.id IN ('+','.join(map(str, ids))+')
        GROUP BY a.currency_id')
    if len(cr.fetchall()) >= 2:
        return True
    cr.execute('SELECT abs(SUM(l.amount))
        FROM account_move m LEFT JOIN account_move_line l ON (m.id=l.move_id)
        WHERE m.id IN ('+','.join(map(str, ids))+')')
    res = cr.fetchone()[0]
    return res < 0.01

_constraints = [
    (_constraint_sum, 'Error: the sum of all amounts should be zero.', ['name'])
    ]

_sql_constraints 和 _order
    _sql_constraints定义数据表的约束条件,其格式如下例所示。
    _sql_constraints = [
        ('code_company_uniq', 'unique (code,company_id)', 'The code of the account must be unique per company !')
    ]
    本例的_sql_constraints会在数据表中增加下述约束:
    CONSTRAINT ObjectName_code_company_uniq UNIQUE(code, company_id)

    _order在对象的search或read方法中的select语句上加上"Order"子句,如 _order = 'name desc, account_id',对应SQL文的Order子句:order by name desc, account_id。

_defaults
    _defaults属性用于定义字段的缺省值,其格式为:
_defaults:
    {
        'name_of_the_field':function, ...
    }

function的返回值作为'name_of_the_field'字段的缺省值。function格式是:function(obj, cr, uid, context),返回值必须是简单类型,如boolean, integer, string 等。下面是_defaults的例子。
_defaults = {
    'date_order': lambda *a: time.strftime('%Y−%m−%d'),
    'state': lambda *a: 'draft',
    'user_id': lambda obj, cr, uid, context: uid
}

lambda是Python的行函数,"lambda obj, cr, uid, context: uid"等同于下述函数:
def func(obj, cr, uid, context):
    return uid


_inherit和_inherits
    _inherit继承有两种情况,1)如果子类中不定义_name属性,则相当于在父类中增加一些字段和方法,并不创建新对象。2)如果子类中定义_name属性,则创建一个新对象,新对象拥有老对象的所有字段和方法,老对象不受任何影响。两种情况的示例及继承关系的图示见下面。

   class res_partner_add_langs(osv.osv):
       _inherit = 'res.partner'
       _columns = {
           'lang_ids' : fields.many2many('res.lang', 'res_lang_partner_rel', 'partner_id', 'lang_id', 'Languages'),
       }
   res_partner_add_langs()


   class formateur(osv.osv):
       _name = 'formateur'
       _inherit = 'res.partner'
       _columns = {
           'lang_ids' : fields.many2many('res.lang', 'res_lang_partner_rel', 'partner_id', 'lang_id', 'Languages'),
       }
   formateur()

    _inherits相当于多重继承。子类通过_inherits中定义的字段和各个父类关联,子类不拥有父类的字段,但可以直接操作父类的所有字段和方法。_inherits的示例及图示见下图。

   class cursus_category(osv.osv):
       _name = 'cursus.category'
       _inherits = {'account.analytic.caccount':'analytic_caccount_id'}
       _columns = {
           'analytic_caccount_id' : fields.many2one('account.analytic.caccount', 'ID'),
       }
   cursus_category()
« 最后编辑时间: 九月 13, 2010, 01:37:49 下午 作者 NewZN »
上海 - 老肖:OpenERP咨询专家 QQ:1417063315  Mail: sales@oscg.com.hk  Web: http://www.oscg.com.hk   电邮给我     了解我们 -

Joshua

  • 布道达人
  • ****
  • 帖子: 275
  • 人气: 4
    • 上海先安科技
Re: OpenERP对象定义详解(连载中...)
« 回复 #1 于: 九月 11, 2010, 05:46:57 下午 »
好贴,占位
上海先安科技 (http://www.openerp.cn)
joshua AT openerp.cn

NewZN

  • 布道达人
  • ****
  • 帖子: 256
  • 人气: 2
    • OSCG
Re: OpenERP对象定义详解(连载中...)
« 回复 #2 于: 九月 11, 2010, 08:04:35 下午 »
OpenERP对象的字段定义详解吐血完成,总算充分理解了最难以理解的 fields.property 字段类型。
再完善哈,即将发布。。。
上海 - 老肖:OpenERP咨询专家 QQ:1417063315  Mail: sales@oscg.com.hk  Web: http://www.oscg.com.hk   电邮给我     了解我们 -

zhaohuaw

  • 新手上路
  • *
  • 帖子: 9
  • 人气: 0
Re: OpenERP对象定义详解(连载中...)
« 回复 #3 于: 九月 11, 2010, 08:12:22 下午 »
我来坐板凳,刚接触OPENERP。谢谢您的好资料!

coldfire

  • 新手上路
  • *
  • 帖子: 44
  • 人气: 0
Re: OpenERP对象定义详解(连载中...)
« 回复 #4 于: 九月 11, 2010, 08:43:21 下午 »
广告位出租

linyaohui

  • 小试牛刀
  • **
  • 帖子: 93
  • 人气: 3
Re: OpenERP对象定义详解(连载中...)
« 回复 #5 于: 九月 11, 2010, 09:00:34 下午 »
顶住啊

mrshelly

  • 版主
  • 唐僧初段
  • *****
  • 帖子: 944
  • 人气: 2
  • OpenERP python,php,js 谢谢
Re: OpenERP对象定义详解(连载中...)
« 回复 #6 于: 九月 12, 2010, 01:15:14 下午 »
一定要详啊....

NewZN

  • 布道达人
  • ****
  • 帖子: 256
  • 人气: 2
    • OSCG
深入理解OpenERP的对象
« 回复 #7 于: 九月 13, 2010, 01:50:11 下午 »
一切都是对象
    OpenERP的所有资源(Resource)都是对象,如 menus, actions, reports, invoices, partners 等等。换言之,在OpenERP中,一个菜单项,一个弹出窗口,其实都是一条数据库记录。OpenERP运行时,从数据库读出“菜单项”记录,根据该记录的信息,在屏幕上显示菜单项及其子菜单项。因此,理论上,可以不写代码,而是直接修改OpenERP的数据库而编写功能菜单、查询窗口、动作按钮等实现业务功能开发。实际开发中,通常是编写XML文件,导入菜单、窗口、动作等编程元素,实现功能开发。XML文件比直接修改数据库或编写SQL语句更容易使用一些。
    OpenERP通过自身实现的对象关系映射(ORM,object relational mapping of a database)访问数据库。OpenERP的对象名是层次结构的,就是说可以使用"."访问树状对象,如:
· account.invoice : 表示财务凭证对象。
· account.invoice.line : 表示财务凭证对象中的一个明细行对象。

    通常,对象名中,第一级是模块名,如: account, stock, sale 等。比之直接用SQL访问数据库,OpenERP的对象的优势有,1)直接使用对象的方法增、删、改数据库记录。因为OpenERP在基类对象中实现了常规的增、删、改方法,因而,普通对象中不需要写任何方法和代码就具备增、删、改数据库记录的功能。2)对于复杂对象,只需操作一个对象即可访问多张数据表。如partner对象,它的信息实际上存储在多张数据表中(partner address, categories, events 等等),但只要通过"."操作即可访问所有关联表(如,partner.address.city),简化了数据库访问。
    注意,在其他编程语言或开发平台(如Java or JavaEE)中,一个对象(Object)通常和数据库中一条记录(Record)相对应。但是,OpenERP的对象其实是一个Class,它和一个数据表(Table)对应,而不是和一个记录(Record)对应。在OpenERP中,数据库记录(Record)通常叫资源(Resource)。因为Object操作的是数据表,OpenERP的对象的方法(Method)中,几乎每个方法都带有参数ids,该参数是资源(Resource or Record)的ID(在OpenERP中ID是主键)列表,通过该ids就可以操作具体的Record了。

访问OpenERP对象
    OpenERP提供了三种方式执行对象的方法(Method),每种方式都是先取得对象,然后调用对象的方法。三种方式是,1)直接使用对象,2)通过netservice使用对象,3)通过xmlrpc使用对象。
直接使用对象:这种方式最简单,这种方式只能在OpenERP Server端使用,编写OpenERP的模块时候,通常使用这种方式。这个方式的内部实现原理是,OpenERP加载模块(不是安装,是启动时加载已安装模块)时,会将创建模块中的对象实例,对象实例以对象名为关键字,存储在对象池(pool)中。此方式是,从pool中取得对象,而后调用对象的方法。
这个方式的一般调用形式是:
    obj=self.pool.get('name_of_the_object')
    obj.name_of_the_method(parameters_for_that_method)
第一行代码从对象池中取得对象实例,第二行代码调用对象的方法。

Netservice方式:这个方式和直接使用对象的方式是类似的,只是不以对象的方式呈现,而是以“服务”(Service)的方式呈现。这种方式也只能在OpenERP Server端使用,即调用程序和OpenERP Server程序在同一个Python虚拟机上运行。这个方式的内部实现原理是,类似对象池,OpenERP有一个全局变量的服务池:SERVICES,该变量位于bin\netsvc.py。有一些对象,它在创建时(__init__方法中)将自己提供的服务登记在服务池中,并暴露自己的服务方法(即该服务可供调用的method)。和对象池不同的是,服务可以有选择性的暴露自己的方法。OpenERP的工作流(Workflow)、报表(Report)都以服务的形式暴露自己的方法,关于OpenERP可供使用的服务有哪些,将在以后介绍。这个方式调用形式如下:
    service = netsvc.LocalService("object_proxy")
    result = service.execute(user_id, object_name, method_name, parameters)
第一行指定服务名取得服务,"object_proxy"是osv.osv对象初始化时注册的一个服务,这个服务可用于调用OpenERP的几乎所有对象(准确的说是所有从osv.osv派生的对象)。"object_proxy"服务用于调用对象的方法。第二行是其调用格式,execute是该服务暴露的一个服务方法,该方法的参数说明如下:
user_id: 用户id,以用户名、密码登录后取得的id。
object_name: 对象名,欲访问的对象的名称,如"res.patner"等。
method_name: 方法名,欲调用的方法的名称,如"create"等。
parameters: 方法的参数。

XML−RPC方式:这个方式相当灵活,它以HTTP协议远程访问对象,因此,能在本机、局域网、广域网范围调用OpenERP的对象的方法。该方式的调用形式是:
sock = xmlrpclib.ServerProxy('http://server_address:port_number/xmlrpc/object')
result = sock.execute(user_id, password, object_name, method_name, parameters)
参数说明如下:
server_address: 运行OpenERP Server的机器的IP或域名。
port_number: OpenERP Server的xmlrpc调用端口,缺省情况是8069。
execute的参数和Netservice方式相同,只是多了个password参数,该参数即用户的登录密码。

XML-RPC方式参考例子。这个例子以xmlrpc方式调用OpenERP的对象res.partner,在数据库中插入一条业务伙伴及其联系地址记录。因为含有中文,测试时注意代码文件保存成utf-8格式:
程序代码: [选择]
# -*- encoding: utf-8 -*-
import xmlrpclib  #导入xmlrpc库,这个库是python的标准库。
username ='admin' #用户登录名
pwd = '123' #用户的登录密码,测试时请换成自己的密码
dbname = 'case1' #数据库帐套名,测试时请换成自己的帐套名
# 第一步,取得uid
sock_common = xmlrpclib.ServerProxy ('http://localhost:8069/xmlrpc/common')
uid = sock_common.login(dbname, username, pwd)
#replace localhost with the address of the server
sock = xmlrpclib.ServerProxy('http://localhost:8069/xmlrpc/object')

# 调用res.partner对象的create方法在数据库中插入一个业务伙伴
partner = {
    'name': 'shine-it',
    'lang': 'zh_CN',
}
partner_id = sock.execute(dbname, uid, pwd, 'res.partner', 'create', partner)

# 下面再创建业务伙伴的联系地址记录
address = {
    'partner_id': partner_id,
    'type' : 'default',
    'street': '浦东大道400号',
    'zip': '200000',
    'city': '上海市',
    'phone': '021-88888888',
}
address_id = sock.execute(dbname, uid, pwd, 'res.partner.address', 'create', address)

再议OpenERP的对象
在《OpenERP应用和开发基础》的第一章中提过OpenERP的架构如下图所示:


这个架构图中,Server端的Business Object就是这里重点解说的内容。通过上面的解说,可以这样通俗的理解OpenERP的对象:
每个对象就是一个代码块,包含了数据表操作(增删改查)的代码。OpenERP Server好比是一个代码池,里面装满了代码块。通过对象池、服务池、xmlrpc等方式,可以取得代码块位置(或者专业一点,叫指针),然后调用代码块的方法,操作数据库。对象的代码什么时候装入“代码池”呢?每个对象定义的后面都有一行:name_of_the_object(),这一行实际上是创建对象实例,实例创建好以后就装到了代码池,这个装入的过程在对象的基类(osv.osv)中完成。对象的基类(osv.osv)已实现了增删改查等常规数据表操作方法,因此,只要定义好对象的字段,即使不写任何代码,该对象已经具备增删改查数据表的能力。
上海 - 老肖:OpenERP咨询专家 QQ:1417063315  Mail: sales@oscg.com.hk  Web: http://www.oscg.com.hk   电邮给我     了解我们 -

xtjie

  • 新手上路
  • *
  • 帖子: 36
  • 人气: 0
Re: OpenERP对象定义详解(连载中...)
« 回复 #8 于: 九月 13, 2010, 02:41:52 下午 »
楼主真是高人啊。内容太棒了.

mrshelly

  • 版主
  • 唐僧初段
  • *****
  • 帖子: 944
  • 人气: 2
  • OpenERP python,php,js 谢谢
Re: OpenERP对象定义详解(连载中...)
« 回复 #9 于: 九月 13, 2010, 02:55:02 下午 »
本着看野史与八卦的心态, 45度观望....

digitalsatori

  • 论坛管理员
  • 唐僧初段
  • *****
  • 帖子: 897
  • 人气: 49
    • 上海先安科技
Re: OpenERP对象定义详解(连载中...)
« 回复 #10 于: 九月 13, 2010, 04:37:19 下午 »
"为人性僻耽佳句,语不惊人死不休"。老肖的文字不是用来惊人的,但是技术文档能写得如此透彻,绝对是花了功夫去专研理解的,向老肖致敬。
  对文档做一些小补充:
 
 
  • _auto   当_auto的值为“False"时,OE不会自动在数据库中创建相应的表,开发者可以在对应类的init()方法中定义表或视图的SQL。这一般应用在  报表所对应的数据对象中,因为报表的数据对象往往是“视图”,所以我们可以在init()方法中创建所需的数据库视图SQL即可。
  • _columns OE并不一定为每个定义项创建数据库字段,比如:selection, reference, one2many, function等字段是不会有对应的数据库字段的。
  • _name 当使用_inherit时可以与被继承的类的_name一致,_name一致表示不创建新的数据库表,而直接在原表上修改
  • _rec_name   缺省的情况下,name_get方法使用表中的‘name'字段,所以当你定义了一个实体类B,并且实体类B中即没有“name“字段也没有为  _rec_name特别指定一个字段,   那么当OE调用name_get方法时(比如实体类A有一个many2one字段b指向实体类B,显示A.b的值时就会调用类B的name_get方法)  就会报“未定义name字段“的错误
  • _inherits   另外还可以这样理解_inherits,_inherits又被称为实例继承,就是新继承的数据对象不但继承被继承对象的属性和方法同时也继承了数据实  例,即表中的记录。比如product类不但继承product_template的属性和方法,而且这两个表的数据也是同步的。
  • _defaults 在V6中字典的值可以不是函数,就比如在V5中我们必须这样来定义:_defaults= { 'state' : lambda *a: 'draft'} 而在 V6中可以这样来:_defaults = {'state' : 'draft'}
 
OpenERP高级实施顾问
上海先安科技 (http://www.openerp.cn)
tony AT openerp.cn
021 50323731

NewZN

  • 布道达人
  • ****
  • 帖子: 256
  • 人气: 2
    • OSCG
Re: OpenERP对象定义详解(连载中...)
« 回复 #11 于: 九月 13, 2010, 05:09:21 下午 »
校长的补充很好!
上海 - 老肖:OpenERP咨询专家 QQ:1417063315  Mail: sales@oscg.com.hk  Web: http://www.oscg.com.hk   电邮给我     了解我们 -

tomp

  • 新手上路
  • *
  • 帖子: 16
  • 人气: 0
Re: OpenERP对象定义详解(连载中...)
« 回复 #12 于: 九月 14, 2010, 09:08:04 上午 »
感谢各位达人!

Joshua

  • 布道达人
  • ****
  • 帖子: 275
  • 人气: 4
    • 上海先安科技
Re: OpenERP对象定义详解(连载中...)
« 回复 #13 于: 九月 14, 2010, 09:53:01 上午 »
顶老肖!!
上海先安科技 (http://www.openerp.cn)
joshua AT openerp.cn

Jeff

  • 还没入门呢
  • 全局版主
  • 唐僧初段
  • *****
  • 帖子: 506
  • 人气: 5
    • 新社区,打造OpenERP在中国的权威机构
Re: OpenERP对象定义详解(连载中...)
« 回复 #14 于: 九月 14, 2010, 05:48:52 下午 »
深入浅出,值得阅读
OpenERP 高级实施顾问
上海先安科技
jeff AT openerp.cn


Tags: