-----------------
更新时间
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')
    这个主要的是解决打印时间的显示的和实际时间的差别,

相关文章: