• odoo-orm


    添加自定义模型

    https://www.odoogo.com/manual/odoo-dev-doc/4d400c71

    1.在models文件夹新建文件
    2.在文件里面编写内容
    class classroom(models.Model):
        _name = 'classroom.zxmodles'
    
        name = fields.Char()
    3.在__init__.py声明
    

    模型编写

    模型属性
    _name odoo模型的标识,必须唯一
    
    _description 展示给用户看的标题
    
    _order 默认的排序字段规则
    
    _rec_name 可以指定一个字段作为该记录的描述,由于展示和搜索
    
    _table 指定后台数据库的表名
    

    https://alanhou.org/odoo12-structuring-data/

    段与字段属性
    
    name = fields.Char()
    
    required=True 必填
    
    string 界面label的值
    
    help 界面tooltip
    
    index 是否创建数据库索引
    
    简单字段类型Boolean, Integer,Float,Monetary, Char,Text,Html,Date,Datetime,Binary(二进制),Selection(选择框),Reference 不可再分 保存单一数据
    
    保留字段 id, create_date, create_uid, write_date, write_uid 会随模型一起在表中建立(请勿重复定义)
    
    注意
    1.__name__不能乱改,实在要改的话,就卸载再改,一般名称为模块名.表名
    2.模块里面新建模型需要重启服务器进行更新
    3.一开始不设置required=true,如果表中有数据了在设置,可能会出现数据的冲突
    

    临时模型

    临时模型继承models.TransientModel类,用于向导式的用户交互。这类数据会存储在数据库中,但仅是临时性的。会定时运行清空 job 来清除这些表中的老数据。比如Settings > Translations菜单下的Load a Language对话窗口,就使用了临时模型来存储用户选择并实现向导逻辑
    

    抽象模型

    抽象模型继承models.AbstractModel类,它不带有数据存储。抽象模型用作可复用的功能集,与使用 Odoo 继承功能的其它模型配合使用。
    

    继承

    https://www.jianshu.com/p/365696a5b823

    _inherit:单一继承。值为所继承父类_name标识。如子类不定义_name属性,则在父类中增加该子类下的字段或方法,不创建新对象;如子类定义_name属性,则创建新对象,新对象拥有父类所有的字段或方法,父类不受影响。
    
    _inherits:多重继承。子类通过关联字段与父类关联,子类不拥有父类的字段或方法,但是可以直接操作父类的字段或方法。
    

    计算字段和视图装饰器

    https://www.odoogo.com/manual/odoo-dev-doc/701ffd98

    https://segmentfault.com/a/1190000016052104

    # models.py
    is_expired = fields.Boolean(u'已过期', compute='_compute_is_expired')
    
    @api.depends('deadline')
    @api.multi
    def _compute_is_expired(self):
    	#self相当于所以的记录,用for来遍历记录
        for record in self:
            if record.deadline:
                record.is_expired = record.deadline < fields.Datetime.now()
            else:
                record.is_expired = False
    

    一对多

    https://ruterly.com/2018/08/19/Odoo-basic-tutorial-04/

    https://www.odoogo.com/manual/odoo-dev-doc/ee9a55e8

    #odoo的一对多比较特殊,可以在两个modles里面都设置相互关联的字段,这样其实就是为了查数据更方便
    一对多 外键多方
    
    多方
    category_id = fields.Many2one('todo.category', string=u'分类')
    Many2one 有一个必填的属性 comodel_name 表示要关联的模型的 _name,这个字段的值可能是 0 个或 1 个所关联对象的记录集,我们可以通过这个字段直接获取到所关联的数据对象,而不需要自己去查找对应的实例。
    
    一方
    需要以Many2one作为依靠,可以在视图显示多方的数据
    task_ids = fields.One2many('todo.task', 'category_id', string=u'待办事项')
    One2many 同样有必填的属性 comodel_name,同时还有一个 inverse_name 属性,表示的是与当前模型所关联的模型(comodel_name 所指的模型)的 Many2one 字段的字段名,在此例中即 category_id,通过 One2many 字段我们可以直接获取到所有关联了当前记录的数据集。在这个例子中,假设我们有一个分类是「工作」,也就是说我们可以通过工作这个分类的 task_ids 这个字段获取到所有待办事项中 category_id 所关联的分类是「工作」的所有待办事项。
    

    多对多

    attendee_ids = fields.Many2many('res.partner', string="学生")
    

    层级关联

    class BookCategory(models.Model):
        _name = 'library.book.category'
        _description = 'Book Category'
        _parent_store = True
    
        name = fields.Char(translate=True, required=True)
        # Hierarchy fields
        parent_id = fields.Many2one(
            'library.book.category',
            'Parent Category',
            ondelete='restrict')
        parent_path = fields.Char(index=True)
    
        # Optional but good to have:
        highlighted_id = fields.Reference(
            [('library.book', 'Book'), ('res.partner', 'Author')],
            'Category Highlight'
        )
        child_ids = fields.One2many(
            'library.book.category',
            'parent_id',
            'Subcategories')
    

    模型案例

    # models.py
    class TodoTask(models.Model):
        _name = 'todo.task'
        _description = '待办事项'
    
        name = fields.Char('描述', required=True)
        is_done = fields.Boolean('已完成?')
        priority = fields.Selection([
            ('todo', '待办'),
            ('normal', '普通'),
            ('urgency', '紧急')
        ], default='todo', string='紧急程度')
    

    模型继承

    详细

    https://www.odoogo.com/manual/odoo-dev-doc/10068063

    传统继承
    允许子类修改父类的方法,字段,也允许新增
    
    委派
    保留父类的字段和方法
    

    直接继承

    class Book(models.Model):
        _inherit = 'library.book'
        is_available = fields.Boolean('Is Available?')
        isbn = fields.Char(help="Use a valid ISBN-13 or ISBN-10.")
        publisher_id = fields.Many2one(index=True)
    
    可以增加和修改原模型的字段_name没有使用的情况下
    如果使用了,就会新建一个独立的模型
    

    模型约束

    sql约束
    SQL约束加在数据表定义中,并由PostgreSQL直接执行。它由_sql_constraints类属性来定义。这是一个元组组成的列表,并且每个元组的格式为(name, code, error):
    
    name是约束标识名
    code是约束的PostgreSQL语法
    error是在约束验证未通过时向用户显示的错误消息
    我们将向图书模型添加两个SQL约束。一条是唯一性约束,用于通过标题和出版日期是否相同来确保没有重复的图书;另一条是检查出版日期是否为未出版:
    class Book(models.Model):
    ...
        _sql_constraints = [
            ('library_book_name_date_uq', # 约束唯一标识符
            'UNIQUE (name, date_published)', # 约束 SQL 语法
            'Book title and publication date must be unique'), # 消息
            ('library_book_check_date',
            'CHECK (date_published <= current_date)',
            'Publication date must not be in the future.'),
    
    python约束
    from odoo.exceptions import ValidationError
     
    class Book(models.Model):
    ...
        @api.constrains('isbn')
        def _constrain_isbn_valid(self):
            for book in self:
                if book.isbn and not book._check_isbn():
                    raise ValidationError('%s is an invalid ISBN' % book.isbn)
    

    进入ORM环境

    https://alanhou.org/odoo12-recordsets/

    python odoo-bin shell -d odoo12(数据库名字)
    
    self.env 环境属性
    env.cr是正在使用的数据库游标(cursor)
    env.user是当前用户的记录
    env.uid是会话用户 id,与env.user.id相同
    env.context是会话上下文的不可变字典
    
    通过外部标识寻找数据
    self.env.ref('base.module_bug_manage')
    

    事物

    self.env.cr.savepoint() 设置事物保存点
    self.env.cr.execute()   执行原生sql
    self.env.cr.commit()    提交事物
    self.env.cr.rollback()  回滚事物
    

    domain

    domain想当于过滤数据
    每个条件都是一个(‘字段名’, ‘运算符’, ‘值’)组成的元组,例如,[(‘is_done’,’=’,False)]是仅带有一个条件的有效域表达式
    作为search中的搜索条件
    
    #使用波兰表示法
    +1, 1
    
    & (AND)  接2个操作数
    | (OR) 接2个操作数
    ! (NOT) 接1个操作数
    
    ['|',('product_type', '=', 'service'),'!', '&',('unit_price', '>=', 1000),('unit_price', '<', 2000)]
    
    看待顺序
    '&',('unit_price', '>=', 1000),('unit_price', '<', 2000)
    !
    |
    从后开始
    
    下拉列表过滤数据
    #下拉列表过滤是老师的人
    instructor_id = fields.Many2one('res.partner', string="Instructor",domain=[('instructor', '=', True)])
    
    

    search-browse

    #通过条件找数据
    self.env['res.partner'].search([('name','like','Ag')])
    
    #通过id找数据
    self.env['res.partner'].browse([9,31])
    
    快速入门

    https://www.jianshu.com/p/3bea01aa8a17

    #快速指定模型表
    self.env['res.partner']
    

    ORM-常用api装饰器

    计算字段和视图装饰器

    https://www.odoogo.com/manual/odoo-dev-doc/701ffd98

    https://www.odoogo.com/manual/odoo-dev-doc/a0fbb9e7

    https://segmentfault.com/a/1190000016052104

    # models.py
    is_expired = fields.Boolean(u'已过期', compute='_compute_is_expired')
    
    @api.depends('deadline')
    @api.multi
    def _compute_is_expired(self):
    	#self相当于所以的记录,用for来遍历记录
        for record in self:
            if record.deadline:
                record.is_expired = record.deadline < fields.Datetime.now()
            else:
                record.is_expired = False
    
    计算字段
    class Book(models.Model):
    ...
        publisher_country_id = fields.Many2one(
            'res.country', string='Publisher Country',
            # store = False, # 默认不在数据库中存储
            compute='_compute_publisher_country'
        )
    
        @api.depends('publisher_id.country_id')
        def _compute_publisher_country(self):
            for book in self:
                book.publisher_country_id = book.publisher_id.country_id
    
    出版社的国家就是书籍的国家信息
    
    #注意
    #计算字段默认是不写入数据库的
    # store = False, # 默认不在数据库中存储
    
    写入计算字段
    #注意:前提是当前用户有权限
    def _inverse_publisher_country(self):
            for book in self:
                book.publisher_id.country_id = book.publisher_country_id
    
    搜索字段
    #可以被拿来作为搜索的字段
        def _search_publisher_country(self, opearator, value):
            return [('publisher_id.country_id', operator, value)]
    
    关联字段
    #本质上关联字段仅仅是快捷实现 search 和 inverse 方法的计算字段。也就是说可以直接对其进行搜索和写入,而无需书写额外的代码。默认关联字段是只读的,因inverse写操作不可用,可通过readonly=False字段属性来开启写操作。
    publisher_country_id = fields.Many2one(
            'res.country', string='Publisher Country',
            related='publisher_id.country_id',
        )
    
    multi
        name = fields.Char(compute='_compute_name')
    
    #multi代表数据集,每次展示数据都会执行一次
        @api.multi
        def _compute_name(self):
            for record in self:
                record.name = str(random.randint(1, 1e6))
    
    当每次调用rec.name时,都会调用compute方法来计算字段的值
    
    depends
    class Book(models.Model):
    ...
        publisher_country_id = fields.Many2one(
            'res.country', string='Publisher Country',
            # store = False, # 默认不在数据库中存储
            compute='_compute_publisher_country'
        )
    
        @api.depends('publisher_id.country_id')
        def _compute_publisher_country(self):
            for book in self:
                book.publisher_country_id = book.publisher_id.country_id
    
    出版社的国家就是书籍的国家信息
    计算字段默认是不写入数据库的
    # store = False, # 默认不在数据库中存储
    
    onchange
    当记录的字段发生变化,则会触发函数,一般来说onchange可以非常方便的根据一些字段变化对其他字段进行进一步的赋值
    # onchange handler
    @api.onchange('amount', 'unit_price')
    def _onchange_price(self):
        # set auto-changing field
        self.price = self.amount * self.unit_price
        # Can optionally return a warning and domains
        return {
            'warning': {
                'title': "Something bad happened",
                'message': "It was very bad indeed",
            }
        }
    
    这个方法有几点需要注意
    1 self是单条记录 在一个form里展示  不能用在list view
    2 方法里改变self里的字段 会更新到未保存的form里
    3 通过onchange里参数字段 触发更新与上一节depands类似
    4 onchange方法不针对某个特定字段
    
    可以直接抛出异常,动态关联的数据就会回退
    raise UserError('不生效')
    

    ORM内置方法

    create
    user=self.env['res.partner']
    new_one=user.create({'name':'zx'})
    
    重写创建
        @api.model
        def create(self, vals):
            # Code before create: should use the `vals` dict
            if 'stage_id' in vals:
                Stage = self.env['library.checkout.stage']
                new_state = Stage.browse(vals['stage_id']).state
                if new_state == 'open':
                    vals['checkout_date'] = fields.Date.today()
            new_record = super().create(vals)
            # Code after create: can use the `new_record` created
            if new_record.state == 'done':
                raise exceptions.UserError(
                    'Not allowed to create a checkout in the done state.')
            return new_record
    
    write
    Partner = self.env['res.partner']
    recs = Partner.search( [('name', 'ilike', 'Azure')] )
    recs.write({'comment': 'Hello!'})
    
    #重写修改 
        @api.multi
        def write(self, vals):
            # Code before write: can use `self`, with the old values
            if 'stage_id' in vals:
                Stage = self.env['library.checkout.stage']
                new_state = Stage.browse(vals['stage_id']).state
                if new_state == 'open' and self.state != 'open':
                    vals['checkout_date'] = fields.Date.today()
                if new_state == 'done' and self.state != 'done':
                    vals['closed_date'] = fields.Date.today()
            super().write(vals)
            # Code after write: can use `self`, with the updated values
            return True
    
    Partner = self.env['res.partner']
    recs = Partner.search( [('name', 'ilike', 'Azure')] )
    recs.unlink()
    
    案例
    >>> self.env['res.partner'].search([('name', 'like', 'Ad')])
    res.partner(10, 35, 3)
    上例中返回的res.partner模型记录集包含三条记录,id 分别为10, 35和3。记录集并没有按 id 排序,因为使用了相应模型的默认排序。就 partner 模型而言,默认的_order为display_name。
    
    >>> self.env['res.partner'].search([('name', 'like', 'Pac')])
    res.partner(42, 62)
    >>> self.env['res.partner'].browse([42, 62])
    res.partner(42, 62)
    
  • 相关阅读:
    计算机基础理论 基本功
    mac 下使用gcc 编译c函数
    技术书籍 读书方法有感
    IntentFilter的相关问题解析
    IOS 学习笔记 2015-04-03 OC-API-文件读写
    (转)iOS中3种正则表达式的使用与比较
    (转)IOS学习笔记-2015-03-29 int、long、long long取值范围
    mysql 游标
    (转) 生活感悟
    IOS 学习笔记 2015-03-27 我理解的OC-代理模式
  • 原文地址:https://www.cnblogs.com/zx125/p/13153932.html
Copyright © 2020-2023  润新知