-----------------
更新时间
11:17 2016-09-18 星期日
11:00 2016-03-13 星期日
09:10 2016-03-03 星期四
11:46 2016-02-25 星期四
10:06 2016-02-24 星期三
14:51 2016-02-23 星期二
18:07 2016-02-19 星期五
17:44 2016-02-17 星期三
-----------------
*模型
模型是业务对象的呈现
* 系统已定义的模型查看
设置->技术->数据结构->模型
现在定义的有700多个
* 版本演变
from openerp.osv import fields, osv
->
from openerp import models, fields
-------------
osv.osv ---> orm.Model ---> models.Model
osv.TransientModel ---> orm.TransientModel ---> models.TransientModel
------------
class MyModel(osv.osv):
pass...
-------------
class MyModel(osv.Model):
pass...
------------
class MyModel(models.Model):
pass...
-------------
上面这些在后台8.0版本都可以运行,在后面的版本不敢确定了
确切的查看
openerp/models.py 文件
openerp/osv/osv.py 这个文件内容纯是兼容性写法
except_osv = except_orm
osv = Model
osv_memory = TransientModel
osv_abstract = AbstractModel
左边是早期写法,很多技术文档,包含里面的官方模块也还有很多老的api
新的,尽量用新的api来写模块
openerp/osv/orm.py
把openerp/models.py综合进来了
系统里大部分还是老的写法,不过自己尽量用新的写法,会简洁一些
差别最大是搜索:
v7 search() 得到是ids 要得到recordset, 还得用 browse() 或 read()
V8 search() 得到是recordset
-----------
def _get_language(self, cr, uid, context):
lang_model = self.pool.get('res.lang')
lang_ids = lang_model.search(cr, uid, [('translatable', '=', True)], context=context)
lang_data = lang_model.read(cr, uid, lang_ids, ['code', 'name'], context=context)
return [(d['code'], d['name']) for d in lang_data]
-----------
def _get_language(self):
lang_model = self.env.get('res.lang')
或
# lang_model = self.env['res.lang']
lang_data = lang_model.search_read([('translatable', '=', True)],['code', 'name'])
return [(d['code'], d['name']) for d in lang_data]
-----------
@ v7 版
* 常用模型 models.model, models.TransientModel, models.AbstractModel
model:是基本模型,继续了它,模型就具有官方的ORM功能了
TransientModel:常用于向导模型,数据是存入数据库,但不是永久的
AbstractModel:常用于混合继承
* 创建模型:
from openerp import models, fields
class Stage(models.Model):
_name = 'todo.task.stage'
_order = 'sequence,name'
_rec_name = 'name'
_table = 'todo_task_stage'
field1 = fields.Char()
_columns = {
'name': fields.char('State', size=64, required=True, translate=True),
'sequence': fields.integer('Sequence', help="Used to order states."),
'state_color': fields.selection(STATE_COLOR_SELECTION, 'State Color'),
'team': fields.selection(STATE_SCOPE_TEAM, 'Scope Team'),
}
_defaults = {
'sequence': 1,
}
哪些关键词可以定义,可查看 /openerp/models.py 中的class BaseModel(object): 下面的定义
# _name 模型的标识符,用于引用,全局唯一,一般格式 "ModuleName.ClassName"
# _rec_name 覆盖默认的name字段,重新定义,一般会加其它合成方法生成名称,
# _table 映射到数据库的表名,可自定义,默认是模型标识符把"."换成"_" 连启来
# _columns 设置字段名 字典{字段名->字段声明}
# _defaults 设置字段的默认值
# _auto 是否创建对象对应的table 缺省为True
# _inherit 继承对象当不再定义 _name时,就是用父对象,若再定义_name 就生成了新的对象
# _inherits 继承对象,且创建新的对象,不拥有父对象的字段,但可以操作父对象的字段
# _constraints 对象约束
def _check_date(self,cr,uid,ids)
for rec in self.browse(cr,uid,ids):
search_ids =self.search(cr,uid,[('date_from','<=', rec.date_to),('date_to','>=',rec.date_from),
('employee_id','=', rec.employee_id.id),('id','<>',rec.id)])
if search_ids:
return False
return True
_constraints=[_check_date,u'你在相同的时间段不允许创建多张请假条!',[u'起始日期',u'结束日期']]
# _sql_constraints 数据库约束,最底层级别的约束
(标识,条件,显示字符)
_sql_constraints =[
('date_check',"CHECK(date_from <= date_to)",u"开始日期必须小于结束日期"),
('days_check',"CHECK(days>0)",u"请假天数必须大于0")
]
# _log_access 是否自动在对应的数据表中增加 create_uid,create_date,write_uid,write_date 四个字段
可用于对象的方法(perm_read)读取,权限控制
缺省是True
# _order 定义search()和read()方法是排序,缺省是 id 升序
# _parent_store
# _parent_name
# _parent_order
得到模型
self.env['todo.task.stage'] 或 self.env.get('todo.task.stage')
旧api 是 self.pool['todo.task.stage']或self.pool.get('todo.task.stage')
Transient 和 Abstract
查看系统已注册的模型
技术->数据结构->模型
* 三种常用继承 (在model中操作)
_inherit 没重定义_name 这样数据库不会新建对象表,用被继承的表
_inherit 重定义_name 这样数据库会创建新的对象表存储
_inherits 是复合继承,有模型对象,有字段对象
示例:
class MyModelExtended(Model):
_inherit = 'a.model' # direct heritage
_inherit = ['a.model, 'a.other.model'] # direct heritage
_inherits = {'a.model': 'field_name'} # polymorphic heritage
* 基本字段类型:
具有哪些字段类型可以查看 openerp/fields.py 中定义的字段类
例子:
name = fields.Char('Name',40,translate=True)
desc = fields.Text('Description')
state = fields.Selection( [('draft','New'), ('open','Started'),('done','Closed')],'State')
docs = fields.Html('Documentation')
sequence = fields.Integer('Sequence')
perc_complete = fields.Float('% Complete',(3,2))
date_effective = fields.Date('Effective Date')
date_changed = fields.Datetime('Last Changed')
fold = fields.Boolean('Folded')
image = fields.Binary('Image')
用方法设置默认值:
a_field = fields.Char(default=compute_default_value)
def compute_default_value(self):
return self.get_value()
注:参数self,现在python2.7.x 只是类中的方法,第一个参数就是self
但在调用这个方法的时候不必为这个参数赋值
例子说明:创建了一个类MyClass,实例化MyClass得到了MyObject这个对象,
然后调用这个对象的方法MyObject.method(arg1,arg2) ,这个过程中,
Python会自动转为Myclass.mehod(MyObject,arg1,arg2)
# Char 字符串
size:字符串长度
translate 字符串可翻译
# Text 文本 ,没有长度限制
translate 字符串可翻译
# Selection 下接选择列表
下接选择列表
例子:
aselection = fields.Selection([('a', 'A')])
aselection = fields.Selection(selection=[('a', 'A')])
aselection = fields.Selection(selection='a_function_name')
class SomeModel(models.Model):
_inherits = 'some.model'
type = fields.Selection(selection_add=[('b', 'B'), ('c', 'C')]) #增加选项
# Html html文本
translate 字符串可翻译
# Integer 整数
# Float 浮点数
digits 设定 整数部分的长度,和小数部分的精度位 afloat = fields.Float(digits=(32, 32))
精度也可以用方法去计算 afloat = fields.Float(digits_compute=fun()) fun是自定义方法,返回
整数部分的长度,和小数部分的精度位 的元组
# Date 日期
context_tody 得到今天的日期
today 得到系统的日期
from_string 从字符串转转成日期对象
to_string 从日期对象转字符串
例子:
>>> from openerp import fields
>>> adate = fields.Date()
>>> fields.Date.today()
'2014-06-15'
>>> fields.Date.context_today(self)
'2014-06-15'
>>> fields.Date.context_today(self, timestamp=datetime.datetime.now())
'2014-06-15'
>>> fields.Date.from_string(fields.Date.today())
datetime.datetime(2014, 6, 15, 19, 32, 17)
>>> fields.Date.to_string(datetime.datetime.today())
'2014-06-15'
日期格式化 DATE_FORMAT='%Y-%m-%d'
字段赋当前日期 fields.date.context_today, fields.date.context_today(self,cr,uid,context=context),
fields.date.today()
官方建议写法 fields.date.context_today
字符串转日期 datetime.datetime.strptime(sale.date,DATE_FORMAT)
日期转字符串 datetime.datetime.strftime(datetme.date.today(),DATE_FORMAT)
python获取当前日期 datetime.date.today()
# Datetime 时间
context_timestamp 得到今天的时间
now 得到系统的时间
from_string 从字符串转转成日期对象
to_string 从日期对象转字符串
例子:
>>> fields.Datetime.context_timestamp(self, timestamp=datetime.datetime.now())
datetime.datetime(2014, 6, 15, 21, 26, 1, 248354, tzinfo=<DstTzInfo 'Europe/Brussels' CEST+2:00:00 DST>)
>>> fields.Datetime.now()
'2014-06-15 19:26:13'
>>> fields.Datetime.from_string(fields.Datetime.now())
datetime.datetime(2014, 6, 15, 19, 32, 17)
>>> fields.Datetime.to_string(datetime.datetime.now())
'2014-06-15 19:26:13'
return { 'value': {'shop_accept_datetime': time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(time.time())) }}
时间格式化 DATETIME_FORMAT="%Y-%m-%d %H:%M:%S"
包含毫秒 DATETIME_FORMAT="%Y-%m-%d %H:%M:%S.%f"
字段赋当前时间 fields.datetime.now(), fields.datetime.context_timestamp(self,cr,uid,datetime.now(),context=context)
官方建议写法 fields.datetime.now()
python 获取当前时间 datetime.datetime.now()
--------
'datetime': fields.datetime('Historization Time')
_defaults = {
'datetime': fields.datetime.now, #赋当前时间
}
# Boolean 布尔值(true, false) 若default=True 复选框默认选上
# Bninary 二进制
储存文件采用 base64编码
# function: 函数型,该类型的字段,字段值由函数计算而得,不存储在数据表中
其定义格式为:
fields.function(fnct, arg=None, fnct_inv=None, fnct_inv_arg=None, type='float',
fnct_search=None, obj=None, method=False, store=True)
@ fcnt 是函数或方法,用于计算字段值。如果method = true, 表示fcnt是对象的方法,其格式如下:
def fnct(self, cr, uid, ids, field_name, args, context),否则,其格式如下:
def fnct(cr, table, ids, field_name, args, context)。ids是系统传进来的当前
存取的record id。field_name是本字段名,当一个函数用于多个函数字段类型时,
本参数可区分字段。args是'arg=None'传进来的参数。 返回字典
@ arg 是传给fcnt的实际参数
@ method 为True表示本字段的函数是对象的一个方法,为False表示是全局函数,不是对象的方法。
如果method=True,obj指定method的对象。
@ fcnt_inv 是用于写本字段的函数或方法(没有提供则本字佰只读)。如果method = true, 其格式是:
def fcnt_inv(self, cr, uid, ids, field_name, field_value, args, context),否则格式为:
def fcnt_inv(cr, table, ids, field_name, field_value, args, context)
@ fnct_inv_arg 传给写本字佰方法的实际参数
@ type 是函数返回值的类型。
@ fcnt_search 定义该字段的搜索行为(不提供不返回任何结果)。如果method = true, 其格式为:
def fcnt_search(self, cr, uid, obj, field_name, args),否则格式为:def fcnt_search(cr, uid, obj, field_name, args)
@ store 表示是否希望在数据库中存储本字段值,缺省值为False。true表示存入 若采用增强形式,则会重新计算字段再存入
不过store还有一个增强形式,格式为 store={'object_name':(function_name,['field_name1','field_name2'],priority)} ,
其含义是,如果对象'object_name'的字段['field_name1','field_name2']发生任何改变,系统将调用函数function_name,
函数的返回结果将作为参数(arg)传送给本字段的主函数,即fnct。
# dummy:虚拟型 fields.dummy(string='Pricelist', relation='product.pricelist', type='many2one')
string:显示字符
relation:关联对象
type:关联到对象的类型,这里是many2one
# many2one: 多对一关系,格式为:fields.many2one(关联对象Name, 字段显示名, ... )。可选参数有:
ondelete,可选值为"cascade"和"null",缺省值为"null",表示one端的record被删除后,many端的record是否级联删除。
# one2many: 一对多关系,格式为:fields.one2many(关联对象Name, 关联字段, 字段显示名, ... ),例:
'address': fields.one2many('res.partner.address', 'partner_id', 'Contacts')。
# many2many: 多对多关系。例如: 'category_id':fields.many2many('res.partner.category','res_partner_category_rel',
'partner_id','category_id','Categories'), 表示以多对多关系关联到对象res.partner.category,
关联表为'res_partner_category_rel',关联字段为 'partner_id'和'category_id'。当定义上述字段时,
OpenERP会自动创建关联表为 'res_partner_category_rel',它含有关联字段'partner_id'和'category_id'
# reference: 引用型,
格式为:fields.reference(字段名, selection, size, ... )。
其中selection是:
1)返回tuple列表的函数,或者 2)表征该字段引用哪个对象(or model)的tuples列表。
reference字段在数据库表中的存储形式是(对象名,ID),如(product.product,3)
3)表示引用对象product.product(数据表product_product)中id=3的数据。reference的例子:
def _links_get(self, cr, uid):
cr.execute('select object,name from res_request_link order by priority')
return cr.fetchall()
...
'ref':fields.reference('Document Ref 2', selection=_links_get, size=128),
...
上例表示,字段ref可以引用哪些对象类型的resource,可引用的对象类型从下拉框选择。
下拉框的选项由函数_links_get返回,是(object,name)对的列表,如[("product.product","Product"),
("account.invoice","Invoice"), ("stock.production.lot","Production Lot")] 。
# related: 关联字段,表示本字段引用关联表中的某字段。特别用于一对多,
比如一个移库单包含多个库存移动单,要在一个移库单紧着罗列出关联的所有库存移动单
这时采用 related 定义字段特别有用。
格式为:fields.related(关系字段,引用字段,type, relation, string, ...),
关系字段是本对象的某字段(通常是one2many or many2many),
引用字段是通过关系字段关联的数据表的字段,
type是引用字段的类型,若关联的字段是many2one 或 many2many 类型,那就要明确指定
如果type是many2one or many2many, relation指明关联表。例子如下:
'address': fields.one2many('res.partner.address', 'partner_id', 'Contacts'),
'city':fields.related('address','city',type='char', string='City'),
'country':fields.related('address','country_id',type='many2one', relation='res.country', string='Country'),
这里,city引用address的city字段,country引用address的country对象。在address的关联对象res.partner.address中,
country_id是many2one类型的字段,所以type='many2one', relation='res.country'。
# property: 属性字段,下面以具体例子解说property字段类型。
'property_product_pricelist': fields.property('product.pricelist', type='many2one',
relation='product.pricelist',string="Sale Pricelist", method=True, view_load=True, group_name="Pricelists Properties")
这个例子表示,本对象通过字段'property_product_pricelist'多对一(type='many2one')
关联到对象 product.pricelist(relation='product.pricelist')。和many2one字段类型不同的是,
many2one字段会在本对象中创建数据表字段'property_product_pricelist',property字段类型不会
创建数据表字段'property_product_pricelist'。property字段类型会从数据表ir.property中查找
name='property_product_pricelist'(即字段定义中的'product.pricelist'加上前缀 property,
并将"."替换成"_"作为name)且company_id和本对象相同的记录,从该记录的value字段(value字段
类型为 reference)查得关联记录,如(product.pricelist,1),表示本对象的resource多对一关联
到对象 product.pricelist的id=1的记录。也就是说,property字段类型通过ir.property间接多对一关联到别的对象。
property字段类型基本上和many2one字段类型相同,但是有两种情况优于many2one字段。其一是,例如,
当有多条记录通过 ir.property的name='property_product_pricelist'的记录关联到记录 (product.pricelist,1),
此时,如果希望将所有关联关系都改成关联到记录(product.pricelist,2)。如果是 many2one类型,不写代码,
很难完成此任务,是property字段的话,只要将ir.property中的value值 (product.pricelist,1)
改成(product.pricelist,2),则所有关联关系都变了。修改ir.property的 value值可以在系统管理下的
菜单Configuration --> Properties中修改。其二是,例如,同一业务伙伴,但希望A公司的用户进来看到
的该业务伙伴价格表为pricelistA,B公司的用户进来看到的该业务伙伴价格表为pricelistB,
则many2one类型达不到该效果。property类型通过ir.property中的记录关联时加上了 company_id的条件,
因此可以使得不同公司的员工进来看到不同的关联记录。
由于property类型通过ir.property关联,因此,每个property类型的字段都必须在ir.property中有一条关联记录。
这可以在安装时导入该条记录,参考代码如下:
<record model="ir.property"
% (self._name, self._table))
这个方式也可用来调试代码变量
* 使用数据库操作游标
def my_fun(self):
cursor = self._cr
# or
self.env.cr
* 保用线程
with Environment.manage(): # class function
env = Environment(cr, uid, context)
* 打印时间处理,主要是时区
dt = datetime.datetime.strptime(str, '%Y-%m-%d %H:%M:%S')
dt = dt.replace(tzinfo=pytz.utc).astimezone(pytz.timezone('Asia/Shanghai'))
return dt.strftime(fmt.encode('utf-8')).decode('utf-8')
这个主要的是解决打印时间的显示的和实际时间的差别,