(03)odoo模型/记录集/公用操作

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了(03)odoo模型/记录集/公用操作相关的知识,希望对你有一定的参考价值。

*模型
  模型是业务对象的呈现


* 创建模型: 
  class Stage(models.Model):
    _name = ‘todo.task.stage‘ 
    _order = ‘sequence,name‘
    _rec_name = ‘name‘
    _table = ‘todo_task_stage‘
   
    # _name 模型的标识符,用于引用
    # _order 用于browsed时的记录的排序
    # _rec_name 覆盖默认的name字段
    # _table 映射到数据库的表名,可自定义,默认是模型标识符把"."换成"_" 连启来   
   
    得到模型
     self.envl[‘todo.task.stage‘]  或 self.evn.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
       

* 基本字段类型:
    例子:
    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‘)

   # 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))
   # 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‘   
           
   # 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‘       
       
   # Boolean 布尔值
   # Bninary 二进制
        储存文件采用 base64编码

* 普通字段属性
   # string  是字段的标题,
   # default 设置字段的默认值
   # size  只用于Char 设置可最大接收字符数
   # translate 用于Text Char Html 标识字段标题或内容可翻译
   # help 用于悬挂提示
   # readonly=True 设置字段只读
   # required=True 设置字段不能为空
   # index = True 设置字段创建索引
   # copy=False 设置字段不能被复制
   # groups 设置权限组访问可见
   # states 设置字段状态,接收字典 如:states={‘done‘:[(‘readonly‘,True)]}
   # deprecated=True 当用这个字段,日志会记录警告,主要是版本升级一些过渡字段
   # oldname=‘field‘ 当一个字段在新版本重命名了,指定老的字段,老的字段的数据会自动拷贝到新的字段中

*保留字段名字
   # id 数据库自动生成做为主键
   # create_uid 创建记录时的用户
   # create_date 创建记录时的日期和时间
   # write_uid 修改记录时的最后用户
   # write_date 修改记录时最后日期和时间

*内置专用字段名
   # name  (Char)  记录的默认名,但可以用 _rec_name 去覆盖  
   # active (Boolean) 标识记录的有效性 常用于domain  domain是元组表达式
   # sequence (Integer) 用在列表视图记录排序
   # state (Selection) 显示记录的生命周期
   # parent_id, parent_left, parent_right  用于记录的上下级

* 模型的关系
  例子:
  class TodoTask(models.Model):
    _inherit = ‘todo.task‘
    stage_id = fields.Many2one(‘todo.task.stage‘,‘Stage‘)
    tage_ids =fields.Many2many(‘todo.task.tag‘,string=‘Tages‘)

   model 和 string 所有关系都接受属性,
   # 多对一 Many2one
      接受属性:
              @ ondelete 默认值为set null 表示关联记录删除 本字段设为空
                         restrict 接连删除对应关联的记录
              @ context  domain 是上下文,功能强大
              @ auto_join=True 允许用sql 语句来关联
   # 一对多 One2Many
     接受的属性 和 Many2one 一样
     可以很简单地从one得到many方的对应的所有记录
              class Stage(models.Model):
                 _name = ‘todo.task.stage‘
                 Stage class relation with Tasks:
                 tasks = fields.One2many(
                   ‘todo.task‘, # related model
                   ‘stage_id‘, # field for "this" on related model
                   ‘Tasks in this stage‘)          

   # 多对多 Many2many
             tag_ids = fields.Many2many(
             ‘todo.task.tag‘, # related model
             ‘todo_task_tag_rel‘, # relation table name
             ‘task_id‘, # field for "this" record
             ‘tag_id‘, # field for "other" record
             string=‘Tasks‘)

             也可以用长表单键表示
             tag_ids = fields.Many2many(
             comodel_name=‘todo.task.tag‘, # related model
             relation=‘todo_task_tag_rel‘, # relation table name
             column1=‘task_id‘, # field for "this" record
             column2=‘tag_id‘, # field for "other" record
             string=‘Tasks‘)

   # 父子分层
     可以用Many2one 表示子到父, 用One2many 表示父到子

              class Tags(models.Model):
                _name = ‘todo.task.tag‘
                _parent_store = True
                # _parent_name = ‘parent_id‘
                name = fields.Char(‘Name‘)
                parent_id = fields.Many2one(
                  ‘todo.task.tag‘, ‘Parent Tag‘, ondelete=‘restrict‘)
                parent_left = fields.Integer(‘Parent Left‘, index=True)
                parent_right = fields.Integer(‘Parent Right‘, index=True)
     为了方便还会加一个字段
                child_ids = fields.One2many(‘todo.task.tag‘, ‘parent_id‘, ‘Child Tags‘)
   # One2Many 和 Many2many 的 eval 赋值
     <field name=”tag_ids”
        eval=”[(6,0,
        [ref(’vehicle_tag_leasing’),
        ref(’fleet.vehicle_tag_compact’),
        ref(’fleet.vehicle_tag_senior’)]
        )]” />
       
      (0,_ ,{’field’: value}) 这将创建一个新的记录并连接它
      (1,id,{’field’: value}): 这是更新一个已经连接了的记录的值
      (2,id,_) 这是删除或取消连接某个已经连接了的记录
      (3,id,_) 这是取消连接但不删除一个已经连接了的记录
      (4,id,_) 连接一个已经存在的记录
      (5,_,_) 取消连接但不删除所有已经连接了的记录
      (6,_,[ids]) 用给出的列表替换掉已经连接了的记录
      这里的下划线一般是0或False
     

   # 引用字段动态关系 
       Refers to

              class TodoTask(models.Model):
                 refers_to = fields.Reference(
                   [(‘res.user‘, ‘User‘), (‘res.partner‘, ‘Partner‘)],
                   ‘Refers to‘)

     可用保用任何注册模型
    
     from openerp.addons.base.res import res_request
     def referencable_models(self):
         return res_request.referencable_models(
           self, self.env.cr, self.env.uid, context=self.env.context)
          
    class TodoTask(models.Model):
          refers_to = fields.Reference(
             referencable_models, ‘Refers to‘)    
    这样就不会只局限在用户和合作伙伴两个模型了            
          
* 字段操作
   # 计算
     class TodoTask(models.Model):
       stage_fold = fields.Boolean(
         ‘Stage Folded?‘,
         compute=‘_compute_stage_fold‘)
        
       @api.one
       @api.depends(‘stage_id.fold‘)
       def _compute_stage_fold(self):
         self.stage_fold = self.stage_id.fold
        
   # 关联字段
      participant_nick = fields.Char(string=‘Nick name‘,
                               related=‘partner_id.name‘)
      合作伙伴名字改,该对象的昵称跟着变化
     
        
   # 搜索和写入
     class TodoTask(models.Model):
       stage_fold = fields.Boolean(
         ‘Stage Folded?‘,
         compute=‘_compute_stage_fold‘,
         # store=False) # the default
         search=‘_search_stage_fold‘,
         inverse=‘_write_stage_fold‘)
        
       @api.one
       @api.depends(‘stage_id.fold‘)
       def _compute_stage_fold(self):
         self.stage_fold = self.stage_id.fold 

       def _search_stage_fold(self, operator, value):
         return [(‘stage_id.fold‘, operator, value)]

       def _write_stage_fold(self):
         self.stage_id.fold = self.stage_fold   
   # 为了搜索加字段
       stage_state = fields.Selection(
       related=‘stage_id.state‘,
       string=‘Stage State‘)  
      
   # 模型约束(SQL 和 Python)
     #一个用户在一时间只有一个活动的任务,加复合主键一样
     class TodoTask(models.Model):
       _sql_constraints = [
         (‘todo_task_name_uniq‘,
         UNIQUE (name, user_id, active)‘,
         ‘Task title must be unique!‘)]
     
     #检查名称少于5个字符     
     class TodoTask(models.Model):
       @api.one
       @api.constrains(‘name‘)
       def _check_name_size(self):
          if len(self.name) < 5:
            raise ValidationError(‘Must have 5 chars!‘)
           
* 记录集
   一个模型的实例同时也是一个记录集的实例
   一个记录集是同一个模型的一组记录
   class AModel(Model):
    # ...
    def a_fun(self):
        self.do_something() # self 在这里就是一个记录集,会包含很多记录
        record_set = self
        record_set.do_something()

    def do_something(self):
        for record in self:
           print record
     如果用了@api.one 来修饰,self 就会当前一条记录,不是记录集了

*记录集支持的操作   
    rs1 | rs2  求并集 是指两个集合的所有元素构成的集合
    rs1 +    rs2   求两个集直接连起来,不管重复
    rs1 & rs2   求交集 是指两个集合元素相同的部分构成的集合
    rs1 - rs2   求差集 是指其中一个集合中除去另一个集合相同元素以后剩余的元素构成的集合
    rs1.copy()  浅拷贝
   
    #其它记录集操作
    record in recordset   检测一个记录是否在一个记录集中
    record not in recordset      检测一个记录是否不在一个记录集中 

    recordset.ids 记录ID集
    recordset.ensure_one() 检测是一个记录
    recordset.exists() 若存在返回一个备份
    recordset.filtered(func) 过滤记录集
    recordset.mapped(func)
    recordset.sorted(func) 排序后
   
        # filtered()
         recset.filtered(lambda record: record.company_id == user.company_id)
         recset.filtered("product_id.can_be_sold")
    
        # sorted()  
          recset.sorted(key=lambda r: r.name)
         
         from operator import attrgetter
         recset.sorted(key=attrgetter(‘partner_id‘, ‘name‘))
       
        # mapped()
         recset.mapped(lambda record: record.price_unit - record.cost_price)

         # 从记录集抽出 name 这一列再组成记录集
         recset.mapped(‘name‘)

         # 返回合作伙伴记录集
          recset.mapped(‘invoice_id.partner_id‘)

* 特殊记录集ids属性

* 一些记录的操作
     # 得到记录的显示名称
        name_get() 方法   对应的字段是 display_name
       
* 环境操作
   # self.env 得到环境
        def afun(self):
         self.env
         # or
         model.env
        
    #修改环境
      self.env[‘res.partner‘].with_context(tz=x).create(vals)
      不能用这个函数来修改当前的环境
     
    # 切换用户
        self.sudo(user.id)
        self.sudo()   # 会切换到超级用户
        # or
        self.env[‘res.partner‘].sudo().create(vals)
       
    # 得到当前用户
        self.env.user
       
    # 得到xml的记录
        self.evn.ref(‘base.main_company‘)
       
    # 清理环境缓存
        self.env.invalidate_all()
     
* 公用操作
    # 搜索
        >>> self.search([(‘is_company‘, ‘=‘, True)])
        res.partner(7, 6, 18, 12, 14, 17, 19, 8,...)
        >>> self.search([(‘is_company‘, ‘=‘, True)])[0].name
        ‘Camptocamp‘
       
    # 搜索读到字典中
        >>> self.search_read([], [‘name‘])
        [{‘id‘: 3, ‘name‘: u‘Administrator‘},
         {‘id‘: 7, ‘name‘: u‘Agrolait‘},
         {‘id‘: 43, ‘name‘: u‘Michel Fletcher‘},
         ...]   
    # 从id搜索
        >>> self.browse([1, 2, 3])
        res.partner(1, 2, 3)
       
    # 活动记录写
        @api.one
        def any_write(self):
          self.x = 1
          self.name = ‘a‘       
         
    # 记录写
        @api.one
        ...
        self.write({‘key‘: value })
        # or
        record.write({‘key‘: value})   
       
    # 记录集写
        @api.multi
        ...
        self.write({‘key‘: value })
        # It will write on all record.
        self.line_ids.write({‘key‘: value })   
       
    # Many2many One2Many的行为
        self.line_ids.create({‘name‘: ‘Tho‘})   #
        self.line_ids.create({‘name‘: ‘Tho‘, ‘order_id‘: self.id})  #
        self.line_ids.write({‘name‘: ‘Tho‘})    # 保存所有的相关记录
       
    # 记录拷贝
        >>> @api.one
        >>> ...
        >>>     self.copy()
        broken
       
    # 记录集拷贝
        >>> @api.multi
        >>> ...
        >>>     self.copy()
        broken
       
    # 创建记录
        self.create({‘name‘: ‘New name‘})
       
* 使用数据库操作句柄
    def my_fun(self):
    cursor = self._cr
    # or
    self.env.cr
   
* 保用线程
    with Environment.manage():  # class function
        env = Environment(cr, uid, context)

以上是关于(03)odoo模型/记录集/公用操作的主要内容,如果未能解决你的问题,请参考以下文章

Odoo开发之记录集 – 使用模型数据

Odoo开发之记录集 – 使用模型数据

pycharm添加Odoo代码片段

odoo14开发侧权限配置

odoo10 ORM操作

odoo 二次开发的方法