zoukankan      html  css  js  c++  java
  • odoo 之 结构化应用数据

    odoo 之 结构化应用数据

    一.模型深入了解

    ​ 模型是 Odoo 框架的核心,它们描述应用的数据结构,是应用服务和数据库存储之间的桥梁

    ​ 模型类型:普通(regular)、临时(transient)和抽象(abstract)类型。

    模型属性

    ### 常用属性
    _name    #  是我们创建的 Odoo 模型的内部标识符,在创建新模型时为必填。
    _description #是对用户友好的模块记录标题,在用户界面中查看模型时显示。可选但推荐添加。
    _order   # 设置浏览模型记录时或列表视图的默认排序。其值为 SQL 语句中 order by 使用的字符串,所以可以传入符合 SQL 语法的任意值,它有智能模式并支持可翻译及many-to-one字段名。
    
    
    如:
        class Book(models.Model):
        _name = 'library.book'
        _description = 'Book'
        _order = 'name, date_published desc'
        
        
    ### 高级属性
    _rec_name   # 在从关联字段(如many-to-one关联)中引用时作为记录描述。默认使用模型中常用的 name字段,但可以指定任意其它字段。
    
    _table   # 是模型对应的数据表名。默认表名由 ORM 通过替换模块名中的点为下划线来自动定义,但是可通过该属性指定表名。
    
    _log_access=False   #  用于设置不自动创建审计追踪字段:create_uid, create_date, write_uid和write_date。
    
    _auto=False  #  用于设置不自动创建模型对应的数据表。如有需要,可通过重载init()方法来创建数据库对象:数据表或视图。
    

    模型和 Python 类

    ​ Odoo的模型保存在中央注册表(central registry)中,可通过 env 环境对象(老 API 中称为 pool)获取

    它是一个数据库保存所有可用模型类引用的字典,其中的词条可通过模型名引用 。

    ​ 具体来说,模型方法中的代码可使用self.env[‘library.book’]来获取表示 library.book模型的模型类

    # 模型名非常重要,因为它是访问该注册表的关键。模型名的规则是以点号连接的小写单词 . 如library.book或library.book.category
    
    # 由于历史原因,有些内核模型没有遵循这一规则,如res.users。
    
    # 1. 模型名必须全局唯一,因此第一个单词应使用模块关联的主应用对应.以图书应用来说,模型的前缀名使用library. 其它内核模块如 project,crm和sale
    
    # 2. 另一方面 Python 类仅为所声明文件本地内容,名称仅需在代码文件中唯一即可。因为类名不会与其它模块中的类产生冲突,也就不需为其添加主应用相关的前缀
    
    # 3. 类的命名遵守驼峰原则CamelCase,与pep8规范一致
    

    临时(Transient)模型和抽象模型

    # 临时模型继承models.TransientModel类 
    	1. 作用: 用于向导式的用户交互 , 数据会临时性的存储
    	2. 清理: 定期运行清空job来清楚这些表中的老数据.
            
            
    # 抽象模型继承models.AbstractModel类
    	1. 不带有数据存储, 抽象模型可用于功能集,配合odoo继承功能的其它模型使用
        2. 比如:mail.thread是Discuss应用种的一个抽象模型,用于为其它模型添加消息和follower功能
    

    检查已有模型

    ​ python类创建的模型和字段,在用户界面中有自己的元标签

    ​ Settings > Technical > Database Structure > Models,这里有数据库中的所有模型。点击列表中的模型会打开详情表单

    # 上图中在右上角 In Apps字段中可以看到library.book模型的定义来自library_app和library_member两个模块。
    
    
    # 下方区域中还有几个包含附加信息的标签
        Fields可快速查看模型字段
        Access Rights是授予不同权限组的访问控制规则
        Views显示模型所带的视图列表
        
        
    # 开发者可通过 View Metadata选项查看模型的外部标识符。  
    	如: library.book模型的外部标识符为model_library_book
    	在定义安全访问控制列表经常在 CSV 文件中使用到这些XML ID。
    

    二.模型字段深入了解

    基本字段类型

    #####
    Char(string)  # 是一个单行文本,唯一位置参数是string字段标签。
    
    Text(string)  # 是一个多行文本,唯一位置参数是string字段标签。
    
    Selection(selection, string)  # 是一个下拉选择列表。选项位置参数是一个[(‘value’, ‘Title’),]元组列表。元组第一个元素是存储在数据库中的值,第二个元素是展示在用户界面中的描述。该列表可由其它模块使用selection_add关键字参数扩展。
    
    Html(string)  # 存储为文本字段,但有针对用户界面 HTML 内容展示的特殊处理。出于安全考虑,该字段会被清洗,但清洗行为可被重载。
    
    Integer(string)  # 仅需字段标题字符串参数。
    
    Float(string, digits)  # 带有第二个可选参数digits,该字段是一个指定字段精度的(x,y)元组,x 是数字总长,y 是小数位。
    
    Monetary(string, currency_field)  # 与浮点字段类似,但带有货币的特殊处理。第二个参数currency_field用于存储所使用货币,默认应传入currency_id字段。
    
    Date(string)和Datetime(string)  # 字段只需一个字符串文本位置参数。
    
    Boolean(string)  # 的值为True 或False,可传入一个字符串文本位置参数。
    
    Binary(string)  # 存储文件类二进制文件,只需一个字符串文本位置参数。它可由Python使用 base64编码字符串进行处理。
    
    ##### 文本字符串 Char Text Html的特有属性
    	size  #  (Char)设置最大允许尺寸。无特殊原因建议不要使用,例如可用于带有最大允许长度的社保账号。
    	
        translate  #  使用得字段内容可翻译,带有针对不同语言的不同值。
    
        trim默认值为 True,  #  启动在网络客户端中自动去除周围的空格。可通过设置trim=false来取消。
    

    常用字段属性

    tring  #  是字段的默认标签,在用户界面中使用。除Selection和关联字段外,它都是第一个位置参数,所以大多数情况下它用作关键字参数。如未传入,将由字段名自动生成。
    
    default   #   设置字段默认值。可以是具体值(如 active字段中的default=True),或是可调用引用,有名函数或匿名函数均可。
    
    help  #  提供 UI 中鼠标悬停字段向用户显示的提示文本。
    
    readonly=True  #  
    会使用户界面中的字段默认不可编辑。在 API 层面并没有强制,模型方法中的代码仍然可以向其写入。仅针对用户界面设置。
    
    required=True  #  使得用户界面中字段默认必填。这通过在数据库层面为列添加NOT NULL 约束来实现。
    
    index=True  #  为字段添加数据库索引,让搜索更快速,但同时也会部分降低写操作速度。
    
    copy=False  #  让字段在使用 ORM copy()方法复制字段时忽略该字段。除 to-many 关联字段外,其它字段值默认会被复制。
    
    groups  #  可限制字段仅对一些组可访问并可见。值为逗号分隔的安全组XML ID列表,如groups=’base.group_user,base.group_system’。
    
    states  #  传入依赖 state字段值的 UI 属性的字典映射值。可用属性有readonly, required和invisible,例如states={‘done’:[(‘readonly’,True)]}。
    
    
    ##注意
    	states 字段等价于视图中的 attrs 属性。同时注意视图也支持 states 属性,但用途不同,传入逗号分隔的状态列表来控制元素什么时候可见。
        
        
        
    # 例子
    name = fields.Char(
            'Title',
            default=None,
            index=True,
            help='Book cover title',
            readonly=False,
            required=True,
            translate=False,
        )
    
    # default属性 是固定值, 或者引用函数来计算默认值. 对于简单的运算,使用lambda即可. 如下:
    
    last_borrow_date = fields.Datetime(
            'Last Borrowed On',
            default=lambda self: fields.Datetime.now(),
        )
    
    # 默认值也可以是一个函数引用,或者待定义函数名字符串
        last_borrow_date = fields.Datetime(
            'Last Borrowed On',
            default='_default_last_borrow_date',
        )
    
        def _default_last_borrow_date(self):
            return fields.Datetime.now()
        
        
    ### 当模块数据结构在不同版本种,下列两个属性非常重要
    	deprecated=True  # 在字段被使用时记录一条 warning 日志
        oldname=’field’  # 是在新版本中重命名字段时使用,可在升级模块时将老字段中的数据自动拷贝到新字段中
    

    特殊字段名

    ​ 出于特殊目的作为 ORM 保留字,也是默认字段

    # 只要模型中没有设置  _log_access=False , 都会在新模型中自动创建
    
    create_uid为创建记录的用户
    create_date是记录创建的日期和时间
    write_uid是最后写入记录的用户
    write_date是最后修改记录的日期和时间
    
    ### 一些内置 API 功能默认需要一些指定字段名。避免在不必要的场合使用这些字段名会让开发更轻松。其中有些字段名被保留并且不能在其它地方使用
    
    name #  (通常为 Char)默认作为记录的显示名称。通过是一个 Char,但也可以是 Text 或Many2one字段类型。用作显示名的字段可修改为_rec_name模型属性。
    
    active   #  (Boolean型)允许我们关闭记录。带有active=False的记录会自动从查询中排除掉。可在当前上下文中添加{‘active_test’: False} 来关闭这一自动过滤。可用作记录存档或假删除(soft delete)。
    
    state    #   (Selection类型) 表示记录生命周期的基本状态。它允许使用states字段属性来根据记录状态以具备不同的 UI 行为。动态修改视图:字段可在特定记录状态下变为readonly, required或invisible。
    
    parent_id和parent_path Integer和Char型)  #  对于父子层级关系具有特殊意义。本文后续会进行讨论。
    

    三.模型关系深入了解

    ​ 具体的用例就是层级关联,即一个模型中的记录与同模型中的其它记录关联。

    ​ Odoo 框架还支持弹性关系,即一个字段可指向其它表中的字段,这称为引用字段。

    Many-to-one关联

    ​ 多对一

    # 格式:
    publisher_id = fields.Many2one(
            'res.partner', string='Publisher')
    
    
    
    # 参数:
        ondelete #  定义关联记录删除时执行的操作:
        set null  # (默认值): 关联字段删除时会置为空值
        restricted #  :抛出错误阻止删除
        cascade:#   在关联记录删除时同时删除当前记录
        context  # 是一个数据字典,可在浏览关联时为网页客户端传递信息,比如设置默认值
        domain  # 是一个域表达式:使用一个元组列表过滤记录来作为关联记录选项
        auto_join=True  #  允许ORM在使用关联进行搜索时使用SQL连接。使用时会跳过访问安全规则,用户可以访问安全规则不允许其访问的关联记录,但这样 SQL 的查询会更有效率且更快。
    	delegate=True   # 创建一个关联记录的代理继承。使用时必须设置required=True和ondelete=’cascade’。
    

    One-to-many反向关联

    ​ one-to-many关联是many-to-one的反向关联

    ​ 在图书模型中,publisher_id和parnter是 many-to-one. 同样说明 在partner模型中是有一个one-to-many的反向模型

    # 在partner 模型中添加 library_app/models/res_partner.py下代码:
    from odoo import fields, models
    
    class Partner(models.Model):
        _inherit = 'res.partner'
        published_book_ids = fields.One2many(
            'library.book', # related model
            'publisher_id', # fields for "this" on related model
            string='Published Books')
        
    # One2many字段接收三个位置参数说明
    	关联模型 (comodel_name关键字参数) # 关联的模型
    	引用该记录的模型字段 (inverse_name关键字参数) # 引用关联模型的字段
    	字段标签 (string关键字参数) # 提示
        
    # 其它可用的关键字参数与many-to-one字段相同:context, domain和ondelete 
    

    Many-to-many关联

    ​ 书和作者之间是many-to-many关联:一本书可以有多个作者,一个作者可以有多本书

    # 图书
    class Book(models.Model):
        _name = 'library.book'
    ...
        author_ids = fields.Many2many(
            'res.partner', string='Authors')
            
    # 作者端
    class Partner(models.Model):
        _inherit = 'res.partner'
        book_ids = fields.Many2many(
            'library.book', string='Authored Books')
        
        
    # 格式:
    author_ids = fields.Many2many(
        'res.partner', # 关联模型(尾款)
        'library_book_res_partner_rel', # 要使用的关联表名
        'a_id', # 本记录关联表字段
        'p_id', # 关联记录关联表字段
        'Authors') # string标签文本
    
    # many2many 默认自动生成第三张表
    	# 1.模型名称过程,psql默认是63个字符,需要手动指定关联名
        # 2.手动创建时,避免冲突
    
    # 参数 和one-to-many类似,还可以使用context, domain和auto_join这些关键字参数
    
    # 在创建抽象模型时,many-to-many中不要使用column1和column2属性。在 ORM 设计中对抽象模型有一个限制,如果指定关联表列名,就无法再被正常继承。    
    

    层级关联

    ​ 父子树状关联使用同一模型中many-to-one关联表示,来将每条记录引用其父级。反向的one-to-many关联对应记录的子级。Odoo 通过域表达式附加的child_of和parent_of操作符改良了对这些层级数据结构的支持。只要这些模型有parent_id字段(或_parent_name有效模型定义)就可以使用这些操作符。

    ​ 通过设置_parent_store=True和添加parent_path帮助字段可加快层级树的查询速度。该字段存储用于加速查询速度的层级树结构信息。

    from odoo import api, fields, models
    
    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:
        child_ids = fields.One2many(
            'library.book.category',
            'parent_id',
            'Subcategories')
        
        
    ### 这里定义了一个基本模型,包含引用父级记录的parent_id字段。为启用层级索引来加快树级搜索,添加了一个_parent_store=True 模型属性。使用该属性必须要添加且必须要索引parent_path字段。引用父级的字段名应为parent_id,但如果声明了可选的_parent_name模型属性,则可以使用任意其它字段名。    
    

    引用字段的弹性关联

    ​ 普通关联字段指定固定的引用co-model模型,但是Reference字段类型不受这一点限制,支持弹性关联

    ​ 因此相同字段不用限制只指向相同的目标模型。

    # 图书分类模型来添加引用重点图书或者作者,因此该字段可引用图书或 partner:
    
    class BookCategory(models.Model):
    ...   
        highlighted_id = fields.Reference(
            [('library.book', 'Book'), ('res.partner', 'Author')],
            'Category Highlight'
        )
    
    ###该字段定义与 selection 字段相似,但这里选择项为该字段中可以使用的模型。在用户界面中,用户会先选择列表中的模型,然后选择模型中的指定记录。
    
    # 技术细节:
    	# 1. 引用字段在数据库中以model,id字符串形式存储
    	# 2. read()方法供外部应用使用,以格式化的(‘model_name’, id)元组返回,而不是常用的many-to-one字段的(id, ‘display_name’)形式
    

    四.计算字段

    ​ 计算字段声明和普通字段类似, 有一个额外的compute参数来定义计算函数

    例如: 添加出版商的国别 依赖 出版商的 country_id

    class Book(models.Model):
    ...
        publisher_country_id = fields.Many2one(
            'res.country', string='Publisher Country',
            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
                
                
    ### 注意 
    	计算字段必须分配值,如果没有分配值则会报错.
    

    五.模型约束

    ​ 模型约束的目的是: 通过验证保证数据完整性和正确性.

    ​ psql支持多可用验证: 避免重复,检查条件是否符合等. 如若要求更复杂的逻辑,还是用python代码实现约束

    SQL模型约束

    ​ 由PostgreSQL直接执行, 由_sql_constraints类属性来定义.

    ​ 是由元组组成的列表,格式(name,code,error)

    # name 是约束标识名
    # code 是约束的PostgreSQL语法
    # error 是在约束验证未通过时向用户显示的错误消息
    _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模型约束

    ​ 自定义代码来检查条件,@api.constrains装饰器

    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)
    

    搜索和写入计算字段

    ​ 刚计算的字段可读取,但不可以搜索和写入. 默认情况下是实时计算,而不是存储在数据库种的.

    # 可以通过特殊方法来开启搜索和写入操作
    # 计算字段可与compute方法一起设置,实现搜索逻辑的search方法,以及实现写入逻辑的inverse方法
    
    class Book(models.Model):
    ...
        publisher_country_id = fields.Many2one(
            'res.country', string='Publisher Country',
            compute='_compute_publisher_country',
            # store = False, # 默认不在数据库中存储
            inverse='_inverse_publisher_country',
            search='_search_publisher_country',
        )
    

    ​ 计算字段的写入是计算的反向逻辑. 因此处理写入的方法成为inverse

    # 计算将book.publisher_id.country_id 的值复制给book.publisher_country_id,反向操作是将写入book.publisher_country_id的值拷贝给book.publisher_id.country_id field字段
    
    def _inverse_publisher_country(self):
            for book in self:
                book.publisher_id.country_id = book.publisher_country_id
                
                
    # 注意 
    	# 1.由于要修改出版商partner记录数据,因此也会修改相同出版商图书的字段.
        # 2.仅对partner模型有写入权限的当前用户才能成功执行此操作
    

    ​ 为计算字段开启搜索操作,需要实现search方法. 要将计算字段的搜索转换为使用常规存储字段的搜索域.

    def _search_publisher_country(self, opearator, value):
            return [('publisher_id.country_id', operator, value)]
    

    存储计算字段

    ​ 字段参数添加 store = True

    ​ 将计算字段值保存到数据库中,在任意依赖变更时值就会重新计算.可以像普通字段一样被搜索,不需要使用search方法了

    关联字段

    ​ 依赖 related参数

    publisher_country_id = fields.Many2one(
            'res.country', string='Publisher Country',
            related='publisher_id.country_id',
        )
    

    六.base模型

    ​ odoo内置了base插件模块,包含

    ​ 信息仓库(Information Repository) ir.*模型

    ​ 资源(Resources) res.*模型

    # 信息仓库用于存储 odoo 所需数据, 如:菜单,视图,模型,aciton等
        ir.actions.act_window用于窗口操作
        ir.ui.menu用于菜单项
        ir.ui.view用于视图
        ir.model用于模型
        ir.model.fields用于模型字段
        ir.model.data用于XML ID
    
    # 资源包含基本数据,基本上用于应用
        res.partner用于业务伙伴,如客户、供应商和地址等等
        res.company用于公司数据
        res.currency用于货币
        res.country用于国家
        res.users用于应用用户
        res.groups用于应用安全组
    
  • 相关阅读:
    【Android
    【数据结构】之链表(C语言描述)
    【数据结构】之顺序表(C语言描述)
    【Android
    【Android
    【Android
    【Android
    【Android
    在线HTTP速度测试(响应时间测试)及浏览器兼容测试
    阿里云 腾讯云 服务器挂载数据盘
  • 原文地址:https://www.cnblogs.com/dengz/p/12965038.html
Copyright © 2011-2022 走看看