ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

最好用的免费ERP系统Odoo 12开发手册 | 第六篇 模型-结构化应用数据

2020-07-30 18:34:55  阅读:25  来源: 互联网

标签:12 ERP fields 模型 关联 字段 book Odoo id


第六章 模型-结构化应用数据

1. 在第三篇文章中,我们创建了第一个Odoo应用,概览了创建Odoo应用所需的所有组件.
2. 本文及接下来的一篇我们将深入到组成应用的每一层: 模型层,视图层,业务逻辑层
3. 本文中我们将深入学习模型层,以及学习如何使用模型来设计应用所需的数据结构. 
      我们会探索模型和字段的各项作用,包括定义模型关系,添加计算字段,创建数据约束.
4. 文本的主要内容有: 
      (1) 学习项目 - 优化图书馆应用
      (2) 创建模型
      (3) 创建字段
      (4) 模型间的关系
      (5) 计算字段
      (6) 模型约束
      (7) 了解Odoo的 base 模型

学习项目-优化图书应用

1. 在第三章中,我们创建了一个 library_app,实现了一个简单的library.book模型用于展示图书目录.
2. 本文中,我们将回到该模块来丰富图书数据.
      我们将添加一个分类层级,添加如下字段用作图书分类: 
      (1) Name: 分类标题
      (2) Parent: 所属父级分类
      (3) Subcategories: 坚持作为父级分类的子分类
      (4) Featured book 或 author: 此分类中所选图书或作者 
3. 图书模型中已有一些基本信息字段,我们会添加一些字段来展示Odoo的数据类型. 
      我们还会为图书模型添加一些约束: 
      (1) 标题和出版日期唯一
      (2) 输入的ISBN有效 

创建模型

1. 模型(Models)是Odoo框架的核心,模型描述应用的数据结构,是应用服务和数据库存储之间的桥梁.
2. 可围绕模型实现业务逻辑来为应用添加功能,用户界面也建立在模型之上(业务逻辑和用户界面都是基于模型的)
3. 下面我们将学习模型的通用属性,用于影响行为,以及几种模型类型: 普通(regular),临时(transient)和抽象(abstract)类型. 

模型属性

1. 模型类可以使用控制其部分行为的额外属性,以下是最常用的属性: 
      (1) _name 是我们创建的Odoo模型的内部标识符,在创建新模型时为必填
      (2) _description 是对用户友好的模块记录标题,在用户界面中查看模型时显示. 可选单推荐添加
      (3) _order 设置浏览模型记录时或列表视图的默认排序. 
            其值时SQL语句中 order by 使用的字符串,所以可以传入符合SQL语法的任意值. 

2. 我们的图书模型中已经使用了 _name 和 _description属性,可以添加一个 _order属性来默认以图书名排序,然后按照出版日期倒序排(新出版在前面)
class Book(models.Model):
    _name = 'library.book'
    _description = 'Book'
    _order = 'name asc, date_published desc'

3. 在高级用例中还会出现如下属性: 
      (1) _rec_name 在从关联字段(如 many-to-one 关联)中引用时作为记录描述. 
            默认使用模型中最常用的 name 字段, 但可以指定任意其他字段.
      (2) _table 是模型对应的数据表名.(一般也是使用默认的)
             默认表明由 ORM 通过替换模块名(_name)中的点为下划线来自动定义,也可以通过该属性指定表名
      (3) _log_access = False 用户设置不自动创建审计追踪字段: create_uid,create_date,write_uid,write_date (一般也不会取消的)
      (4) _auto=False 用于设置不自动创建模型对应的数据表.
            如有需要,可通过重载的 init() 方法来创建数据库对象: 数据表或视图. 

模型和Python类

1. odoo模型以Python类的形式展现.
2. 前面代码中,有一个继承了 models.Model类的Python类: Book, 创建了新Odoo模型: library.book.
3. Odoo的模型保存在中央注册表(central registry)中,可以通过 env 环境对象获取.
4. 它是一个数据库保存所有可用模型类引用的字典,其中的词条可通过模型名引用. 
      具体来说, 模型方法中的代码可使用 self.env['library.book'] 来获取表示 library.book 模型的模型类.
5. 可以看出模型名非常重要,因为它是访问该注册表的关键. 
      (1) 模型名的规则是以点号连接的小写单词, 如 library.book或 library.book.catagory
            内核模块中的其他示例有 project.project, project.task,porject.type
      (2) 模型名应使用单数,如 library.book而非 library.books
            注意: 由于历史原因,有些内核模型没有遵循单数这一规则, 如 res.users
6. 模型名必须全局唯一,因此第一个单词应使用模块关联的主应用对应.
      (1) 以图书app而言,模型名前缀使用 library.
      (2) 其他示例如内核模块的project,crm和sale. 

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

1. 在前述代码中大多数 Odoo模型中的类会继承 models.Model类.
      (1) 这类模型会在数据库中持久化存储
      (2) 会为模型创建数据表并存储记录直至删除.
      (3) 但 Odoo 中还有另外两种模型类型: 临时模型 和 抽象模型

2. 临时模型继承 models.TransientModel类,用于向导式的用户交互. 
      (1) 这类数据会存储在数据库中,但仅是临时性的.
      (2) 会定时运行清空job来清除这些表中的老数据.
      (3) 比如 Settings>Translation 菜单下的 Load a Language 对话窗口,就是用了临时模型来存储用户选择并实现向导逻辑
      (4) 在第八章会有讨论临时模型的示例.

3. 抽象模型继承 models.AbstractModel类,它不带有数据存储.
      (1) 抽象模型用作可复用的功能集,与使用Odoo继承功能的其他模型配合使用.
      (2) 例如 mail.thred 是 Discuss应用中的一个抽象模型,用于为其他模型添加消息和 follower功能.

检查已有模型

1. 通过Python类创建的模型和字段在用户界面中有自己的元标签. 
      (1) 启动开发者工具,访问菜单 Settings > Technical > Database Structure > Models,这里有数据库的所有模型
      (2) 直接使用 navicat 进行数据表的查看
2. 这是一个检查模型结构很好的工具,因为在这里可以看到不同模块所有自定义结果.
      (1) 右上角的In Apps(应用)字段中可以看到 library.book 模型的定义来自 library_app 和 library_member两个模块.
      (2) 下方区域中还有几个包含附加信息的标签: 
            1) Fields 可快速查看模型字段
            2) Access Right 授予不同权限组的访问控制规则
            3) Views 显示模型所带的视图列表
3. 我们可以通过开发者菜单下的 View Metadata选项查看模型的外部标识符.
      (1) 模型的XML ID由 ORM自动生成,但根据规则可以预知,如 library.book模型的XML ID为 model_library_book
      (2) 在定义安全访问控制列表经常在 CSV文件中使用到这些XML ID. 

4. 如在第一章 -- 使用开发者模式快速入门中,模型表单是可编辑的. 
      (1) 通过这里是以可创建并修改模型,字段和视图的.
      (2) 可在此处创建原型然后在插件模块中实现

创建字段

1. 创建新模型后的第一步就是添加字段.
2. Odoo支持我们能想到的所有基本数据类型
      如 文本字符串,整型,浮点型,布尔型,日期,日期时间以及图片或二进制数据.

基本字段类型

1. 我们将为图书模型添加几种可用的字段类型, 编辑 library_app/models/library_book.py文件
class Book(models.Model):
...
    # String fields
    name = fields.Char('Title', required=True)
    isbn = fields.Char('ISBN')
    book_type = fields.Selection(
        [('paper', 'Paperback'),
        ('hard', 'Hardcover'),
        ('electronic', 'Electronic'),
        ('other', 'Other')],
        'Type')
    notes = fields.Text('Internal Notes')
    descr = fields.Html('Description')

    # Numeric fields:
    copies = fields.Integer(default=1)
    avg_rating = fields.Float('Average Rating', (3,2))
    price = fields.Monetary('Price', 'currency_id')
    currency_id = fields.Many2one('res.currency') # price helper

    # Date and time fields
    date_published = fields.Date()
    last_borrow_date = fields.Datetime(
        'Last Borrowed On',
        default=lambda self: fields.Datetime.now())

    # Other fields
    active = fields.Boolean('Active?', default=True)
    image = fields.Binary('Cover')

    # Relational Fields
...

2. 此处是Odoo中所带的非关联字段示例,每个字段都带有所需的位置参数.
      (1) Python中有两类菜单树: 位置参数 和 关键字参数.
      (2) 位置参数需按指定顺序使用,如 f(x,y)应以 f(1,2)方式调用
      (3) 关键字参数通过参数名传递,如 f(x,y)应以 f(x=1,y=2)或 f(1,y=2)或f(1,2)方式进行调用
3. 对于大多数非关联字段,第一个参数是字段标题,与字符串参数相对应.
      (1) 它(传入的第一个字符串字段)用做用户界面标签的默认文本
      (2) 这个参数是可选的,如果没有传入,会根据字段名将下划线替换成空格并将单词首字母大写自动生成

4. 以下为可用的非关联字段类型以及对应的位置参数: 
      (1) Char(string) 是一个单行文本,唯一位置参数是string字段标签
      (2) Text(string) 是一个多行文本,唯一位置参数是string字段标签
      (3) Selection(selection,string) 是一个下拉选择列表.
            1) selection是一个 [('value','title')...]的元组列表.
            2) 元组第一个元素是存储在数据库中的值,第二个元素是展示在用户界面中的描述.
            3) 该列表可由其他模块使用 selection_add 关键字参数扩展
      (4) Html(string) 存储为文本字段,但针对用户界面HTML内容展示的特殊处理
            处于安全考虑,该字段会被清洗,但清洗行为可被重载
      (5) Integer(string) 仅需字段标签字符串参数
      (6) Float(string,digits) 带有第二个可选参数 digits,
            该字段是一个指定字段精度的(x,y)元组, x是数字总长,y是小数位
            avg_rating = fields.Float('Average Rating', (3, 2))
      (7) Monetary(string,currency_field) 与浮点字段类型,但带有货币的特殊处理. 
            第二个参数 currency_field 用于存储所使用的货币,默认应传入 currency_id 字段
            price = fields.Monetary('Price', 'currency_id')
            currency_id = fields.Many2one('res.currency')  # price helper
      (8) Date(string)和Datetime(string)字段只需一个字符串文本位置参数
      (9) Boolean(string)的值为True或False,可传入一个字符串文本位置参数
      (10) Binary(string)存储文本类二进制文件,只需一个字符串文本位置参数.
            它可由 Python使用 base64编码字符串进行处理.
      注意: Odoo12中, Date和Datetime 字段在ORM中作为日期对象处理. 
            此前的版本都是作为文本字符串处理,进行操作时需与Python日期对象 进行转换


5. 文本字符串: Char,Text和Html有一些特有属性: 
      (1) size(Char)设置最大允许尺寸. 无特殊原因建议不要使用,例如可用于带有最大允许长度社保账号
      (2) translate 使用字段内容可翻译,带有针对不同语言的不同值
      (3) trim 默认值为True,启动在网络客户端中自动去除周围的空格,可通过设置 trim=False来取消
            注意: trim字段属性在Odoo 12中引入,此前版本中文本字段保存前后的空格.

常用字段类型

1. 字段还有一些其他属性供我们定义其行为.
2. 以下是常用的属性,通常都作为关键字参数: 
      (1) string 
            1) 是字段的默认标签,在用户界面中使用.
            2) 除 Selection 和 关联字段外,它都是第一个位置参数,所以大多数情况下它用作关键字参数.
            3) 如未传入,将由字段名自动生成
      (2) default 
            1) 设置字段默认值
            2) 可以是具体值(如 active字段中的 default=True)
            3) 或是可调用引用,有名函数或匿名函数均可
      (3) help
            提供UI鼠标悬停字段向用户显示的提示文本.
      (4) readonly=True
            1) 会使用户界面的字段默认不可以编辑.
            2) 在API层面并没有强制,模型方法的代码仍然可以向其写入,仅针对用户界面设置.
      (5) required=True
            1) 使得用户界面字段默认必填.
            2) 这是在数据库层面为列添加 NOT NULL约束来实现的.
      (6) index=True
            为字段添加数据库索引,让搜索更快速,但同时也会部分降低写操作速度.
      (7) copy=False
            1) 让字段在使用 ORM copy() 方法复制字段时忽略该字段
            2) 除 to-many 关联字段外,其他字段值默认会被复制
      (8) groups
            1) 可限制字段仅对一些组可访问并可见.
            2) 值为逗号分隔的安全组XML ID列表,如 groups="base.group_user.group_system"
      (9) states
            1) 传入依赖 state字段值的UI属性的字典映射值.
            2) 可用属性有 readonly,required 和 invisible, 例如 states={'done':[('readonly',True)]}
            注意: states 字段等价于视图中 attrs 属性. 
                  同时注意视图也支持 states 属性,但用途不同,传入逗号分隔的状态列表来控制元素什么时候可见.

3. 以下为字段属性关键字参数的使用示例: 
    name = fields.Char(
        'Title',
        default=None,
        index=True,
        help='Book cover title',
        readonly=False,
        required=True,
        translate=False,
    )

4. 如前所述,default属性可带有固定值,或引用函数来自动计算默认值.
      (1) 对于简单运算,可使用 lambda 函数来避免过重的有名函数或方法的创建.
      (2) 以下是一个计算当前日期和时间的默认值的常用示例:  default = lambda self:fields.Datetime.now()
    last_borrow_date = fields.Datetime(
        'Last Borrowed On',
        default=lambda self: fields.Datetime.now(),
    )
      (3) 默认值也可以是一个函数引用,或带定义的函数名字符串
    last_borrow_date = fields.Datetime(
        'Last Borrowed On',
        default='_default_last_borrow_date',
    )

    def _default_last_borrow_date(self):
        return fields.Datetime.now()      

5. 当模块数据结构在不同版本中变更时以下两个属性非常有用: 
      (1) deprecated=True 在字段中使用时记录一条 warning 日志
      (2) oldname='field' 是在新版本重命名字段时使用,可在升级模块时将老字段中的数据自动拷贝到新字段中.

特殊字段名

1. 一些字段名很特别,可能时因为它们处于特殊目的作为ORM保留字,或者是由于内置功能使用了一些默认字段名.
      id字段保留以用作表示每条记录的自增数字以及数据库主键,每个模型都会自动添加

2. 以下字段只要模型中没有设置 _log_access=False 都会在新模型中自动创建: 
      (1) create_uid 创建记录的用户
      (2) create_date 记录创建的日期和时间
      (3) write_uid 最后写入记录的用户
      (4) write_date 最后修改记录的日期

3. 每条记录的这些字段信息都可以通过开发者菜单下的 View Metadata进行查看. 
      (1) 一些内置API功能默认需要一些指定字段名
      (2) 避免在不必要的场合是以哦那个这些字段名会让开发更轻松.
      (3) 其中有些字段名被保留并且不能在其他地方使用: 
            1) name(通常为Char) 默认作为记录的显示名称.
                  通常是Char,但也可以是 Text或 Many2one字段类型.
                  用作显示名的字段可修改为 _rec_name 模型属性
            2) active(Boolean) 来关闭这一自动过滤. 可用作记录存档或假删除(soft delete)
            3) state(Selection类型) 表示记录生命周期的基本状态.
                  它允许使用 states 字段属性来根据记录状态以具备不同的UI行为.
                  动态修改视图: 字段可在特定记录状态下变为 readonly,required 或 invisible
            4) parent_id 和 parent_path (Integer 和 Char) 对于父子层级关系具有特殊意义,本文后续会进行讨论
            注意: Odoo12 层级关联现在使用 parent_path 字段,它替代了老版本中已淘汰的 parent_left 和 parent_right字段(整型)

模型间的关系

1. 中,大型业务应用有一个结构数据模型,需要关联所涉及到的不同实体间的数据.
      要实现这点,需要使用关联字段.
2. 看看我们的图书应用, 图书模型中有如下关系: 
      (1) 每本书有一个出版商,这是一个 many2one关联(书many,出版商one),在数据库引擎中通过外键实现. 
            反过来则是 one-to-many 关联,表示一个出版商可出版多本书. 
      (2) 每本书可以有多名作者,这是一个 many2many 关联,反过来还是 many2many关联,因为一个作者也可以有多本书.
3. 下面我们就会分别讨论这些关联,具体的用例就是层级关联,即一个模型中的记录与同模型中的其他记录关联.
      (1) 我们将引入一个图书分类模型解释这一情况
      (2) Odoo框架还支持弹性关系,即一个字段可指向其他表中的字段,这称为引用字段.

Many-to-one关联

1. many2one关联是对其他模型中记录的引用
      例如在图书模型中,publisher_id表示图书出版商,是对 partner记录的一个引用
    publisher_id = fields.Many2one(
        'res.partner', string='Publisher')

2. 与所有关联字段一样,many2one字段的第一个位置参数是关联模型(comodel关键字参数).
      第二位置参数是字段标签(string关键字参数),但和其他关联字段不同
      所以推荐像以上代码一样,一直使用 string 关键字参数.

3. many2one模型字段在数据表中创建一个字段,并带有指向关联表的外键,其中为关联记录的数据库ID.
      以下是many2one字段可用的关键字参数
      (1) ondelete 定义关联记录删除时执行的操作: 
            1) set null(默认值) 关联字段删除时会置为空值
            2) restricted 抛出错误阻止删除
            3) cascade 在关联记录删除时同时删除当前记录
      (2) context 是一个数据字典, 可在浏览关联时为网页客户端传递信息,比如设置默认值.(在第八章时会做深入说明)
      (3) domain 是一个域表达式: 使用一个元组列表过滤记录来作为关联记录选项(在第八章时会做深入说明)
      (4) auto_join=True 允许ORM在使用关联进行搜索时SQL连接.
            使用时会跳过安全规则,用户可以访问安全规则不允许其访问的关联记录,但这样SQL的查询会更有效率且更快.
      (5) delegate=True 创建一个关联记录的代理继承.
            使用时必须设置 required=True 和 ondelete='cascade' (代理继承更多知识参考第四章)

One-to-many 反向关联

1. one2many关联是many2one的反向关联,它列出引用该记录的关联模型记录.
2. 比如在图书模型中,publisher_id 与 parnter 模型是一个many2one关联
      这说明 partner 与图书模型可以有一个 one2many 的反向关联,列出每个出版商出版的图书
3. 要让这个关联可用,我们可在 partner 模型中添加它, 在 library_app/models/res_partner.py文件中添加:
      通过继承的方法修改 res.partner 模型
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')

4. one2many字段接受三个位置参数: 
      (1) 关联模型(comodel_name关键字参数)
      (2) 引用该记录的模型字段(inverse_name 关键字参数)
      (3) 字段标签(string 关键字参数)

5. 其他可用的关键字参数与 many2one 字段相同: context,domain,ondelete(此处作用于关联中的many一方)

Many-to-many关联

1. 在两端都存在 to-many 关联时使用 many2many关联.
2. 还是以图书app为例, 书和作者之间是 many2many关联: 一本书可以有多个作者,一个作者可以有多本书

图书端有一个 library.book 模型: 
class Book(models.Model):
    _name = 'library.book'
...
    author_ids = fields.Many2many(
        'res.partner', string='Authors')

在作者端,我们也可以为 res.partner 添加一个反向关联:
class Partner(models.Model):
    _inherit = 'res.partner'
    book_ids = fields.Many2many(
        'library.book', string='Authored Books')

3. many2many最少要包含一个关联模型位置参数(comodel_name关键字参数),推荐为字段标签提供一个string参数.
4. 在数据库层面上, many2many关联不会再已有表总添加任何列.(其他都关联都是在数据表中创建一个字段)
      (1) many2many会自动创建一个关联表来存储记录间的关联,该表仅有两个ID字段,为两张关联表的外键.
      (2) 默认关联表名由两个表名中间加下划线并在最后加上 _rel 来组成. 
            我们图书和作者关联,表明应为 library_book_res_partner_rel
5. 有时我们可能需要重写这种自动生成默认值
      (1) 一种情况是关联模型名合成过长,导致关联表名的长度超出 PostgreSQL数据库63个字符的上限.
            这时候就需要手动选择一个关联表名来符合字符数据要求.
      (2) 另外一种情况是我们需要在相同模型间建立第二章many2many关联表
            这时候也需要手动提供一个关联表名来避免与已存在的第一张表名冲突.
6. 有两种方案来重写关联表名: (1) 位置参数 (2) 关键字参数 (就是使用了一个 relation 参数 自定义了表名)
      (1) 通过位置参数的定义如下
# Book <-> Authors关联(使用位置参数)
author_ids = fields.Many2many(
    'res.partner', # 关联模型(尾款)
    'library_book_res_partner_rel', # 要使用的关联表名
    'a_id', # 本记录关联表字段
    'p_id', # 关联记录关联表字段
    'Authors') # string标签文本
      (2) 要是可读性更强,也可使用关键字参数
# Book <-> Authors关联(使用关键字参数)
author_ids = fields.Many2many(
    comodel_name='res.partner', # 关联模型(必填)
    relation='library_book_res_partner_rel', # 关联表名
    column1='a_id', # 本记录关联表字段
    column2='p_id', # 关联记录关联表字段
    string='Authors') # string标签文本

7. 与 one2many relational字段相似, many2many字段还可以使用 context,domain和auto_join这些关键字参数.

层级关联

1. 层级结构: 
      (1) 父记录与子记录之间是one2many的关系
      (2) 子记录和夫记录之间是many2one的关系
2. 我们可以设置一些参数来达到更快的浏览层级结构子结点数据的目的.
      具体操作起来: 
      (1) 使用模型中的 _parent_store 模型属性
      (2) 使用 parent_left 和 parent_right 两个字段作为辅助
      注意: Odoo12中使用 parent_path,实现了和使用 parent_left 和 parent_right整型字段的相同功能

3. 这些附加操作会带来存储和执行速度的开销,所以最好是用到读的频率大于写的情况下,比如本例中的分类树.
      仅在优化多节点深度层级时才需要使用,对于小层级或浅层级的可能会被误用

4. 为演示层级结构,我们将为图书app添加一个分类树,用于图书分类. 
      在 library_app/models/library_book_category.py 文件中添加
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')

      (1) 这里定义了一个基本模型,包含应用父级记录的 parent_id字段.
      (2) 为启用层级索引来加快树级搜索,添加了一个 _parent_store = True模型属性.
            使用该属性必须要添加且必须要索引 parent_path 字段.     


...
                    

使用引用字段的弹性关联

...

计算字段

1. 字段值除普通的读取数据库中存储值外,还可自动由函数计算.       
      比如一个有一个字段 deadline, 还有一个 is_expried 就可以是一个 计算字段      
2. 计算字段的声明和普通字段相似,但有一个额外的 compute 参数来定义用于计算的函数.
3. 大多数情况下,计算字段包含书写业务逻辑.
      因此要完全使用这一功能,还要学习第八章的知识
      此处我们将解释计算字段用法,但会使用简单的业务逻辑      
4. 图书有出版商,我们的例子是在 图书表单中添加出版商的国别.
      实现该功能,我们会使用基于 publisher_id的计算字段,将从出版商的 country_id 字段中获取值
5. 编辑 library_app/models/library_book.py 文件中的图书模型,代码如下: 
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
      (1) 以上代码添加了一个 publisher_country_id 字段,和一个计算其值的 _compute_publisher_country 方法.
      (2) 方法名作为字符串参数传入字段中,但也可以传递一个可调用引用(方法标识符,不带引号),一般是使用 lambda表达式的情况.
      (3) 计算如果依赖其他字段的话就需要使用 @api.depends装饰器,通常都会依赖其他字段.
            1) 它告诉服务器何时重新计算或缓存值
            2) 参数可接受一个或多个字段名,点号标记可用于了解字段关联. 
            3) 本例中,只要图书 publisher_id的country_id 变更了就会重新进行计算
      (4) 和平常一样,self参数是要操作的字符集对象.
            1) 我们需要对其遍历作用于每条记录.
            2) 计算值通过常用(写)操作来设置,本例中计算相当简单,我们为其分配当前图书的 publisher_id.country_id值                
6. 同样的计算方法可用于一个以上字段.
      这时同一方法在多个 compute 字段参数中使用,计算方法将为所有计算字段分配值.        

搜索和写入计算字段

1. 我们刚刚创建的计算字段可读取但不可搜索或写入. 
      (1) 默认情况下计算字段是实时计算,而不存储在数据库中           
      (2) 这也是为什么计算字段无法像普通字段那样进行搜索的原因.

2. 我们可通过实现特殊方法来开启搜索和写入操作.
      (1) compute 字段的计算逻辑
      (2) search 字段的搜索逻辑
      (3) 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',
    )


3. 计算字段中写入是计算的反向(inverse)逻辑, 因此处理写入操作的方法称为 inverse.
      本例中 inverse 方法很简单
      (1) 计算将 book.publisher_id.country_id 的值复制给 book.publisher_country_id
      (2) 反向操作是将写入 book.publisher_country_id 的值拷贝给 book.publisher_id.country_id 
    def _inverse_publisher_country(self):
        for book in self:
            book.publisher_id.country_id = book.publisher_country_id
      注意: 这会修改出版商partner 记录数据,因此也会修改相同出版商图书的相关字段. 
            常规权限控制对这类写操作有效,因此仅有对partner模型有写权限的当前用户才能执行成功

4. 要为计算字段开启搜索操作,需要实现search方法. (其实定义的时候进行  store=True 即可)       
...           

存储计算字段

1. 通过在定义时设置 store=True 还可以将计算字段值保存到数据库中.
      (1) 在任意依赖变更时值会重新计算
      (2) 因为值已经被存储,所以可以像普通字段一样被搜索,也就不需要使用search方法了

关联字段

1. 前面我们实现的计算字段仅仅是从关联记录中将值拷贝到模型自己的字段中.
      (1) 这种常用情况可以由Odoo使用关联字段功能自动处理.      
      (2) 关联字段 通过关联模型的字段可在模型中直接使用,并且可通过点号标记法直接访问.  
      (3) 这样在点号标记法不可用时(如 UI表单视图)也可以使用该字段

2. 要创建关联字段,我们像普通计算字段那样声明一个所需类型的字段
      (1) 但使用的不是 compute属性,而是 related属性.
      (2) 设置用点号标记链来使用所需字段.

3. 我们可以使用 关联字段 来获取于上列 publisher_country_id 计算字段相同的效果: 
    publisher_country_id = fields.Many2one(
        'res.country', string='Publisher Country',
        related='publisher_id.country_id',
    )

4. 本质上关联字段仅仅是快捷实现 search 和 inverse 方法的计算字段.  
5. 还应指出这些关联字段和计算字段一样可以是使用 store=True 来在数据库中存储        

模型约束

1. 通常应用需保证数据完整性,并执行一些验证来保证数据是完整和正确的.
2. PostgreSQL数据库管理器支持很多可用验证: 
      比如: 避免重复,检查值以符合某些简单条件.
3. 模型可以声明并使用 PostgreSQL约束. 
4. 一些检查要求更复杂的逻辑,最好是使用Python代码来实现. 
      对这些情况,我们可使用特定的模型方法来实现 Python约束逻辑.            

SQL模型约束

...

Python模型约束

1. Python约束可使用自定义代码来检查条件.
2. 检查方法应添加 @api.constrains 装饰器,并且包含要检查的字段列表.
      其中任意字段被修改就会触发验证,并且在未满足条件时抛出异常.
3. 就图书app来说,一个明显的示例就是防止插入不正确的ISBN号. 
      我们已经在 _check_isbn() 中书写ISBN的校验逻辑,可以在模型约束中使用它来防止保存错误数据: 
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)            

了解Odoo的base模型

1. 在前面文章中,
      (1) 我们一起创建了新模型,如图书模型
      (2) 但也使用了已有的模型,如Odoo自带的 Partner 模型
      (3) 下面就来介绍下这些内置模型.
2. Odoo内核中有一个base插件模块. 
      (1) 它提供了Odoo应用所需的基本功能.
      (2) 然后有一组内置插件模块来提供标准产品的官方应用和功能.
3. base模块中包含两类模型: 
      (1) 信息仓库(Information Repository), ir.*模型
      (2) 资源(Resources), res.* 模型
4. 信息仓库(ir.*)用于存储Odoo所需数据,以知道如何作为应用来运作,如菜单,视图,模型,Action等等
      Technical 菜单下的数据通常存储在信息仓库(ir.*)中,相关例子有: 
      (1) ir.action.act_window 用于窗口操作
      (2) ir.ui.menu 用于菜单项
      (3) ir.ui.view 用于视图
      (4) ir.model 用于模型
      (5) ir.model.fields 用于模型字段
      (6) ir.model.data 用于 XML ID 

5. 资源(res.*)包含基本数据,基本上用于应用. 
      以下是一些重要的资源模型: 
      (1) res.partner 用于业务伙伴,如客户,供应商和地址等等
      (2) res.company 用于公司数据
      (3) res.currency 用于货币
      (4) res.contry 用于国家
      (5) res.users 用于应用用户
      (6) res.groups 用于应用安全组

总结

1. 我们看到模型通常继承 models.Model类
      (1) 但还可使用 models.Abstract 来创建可复用的 mixin 模型
      (2) 使用 models.Transient 来创建向导或高级用户对话.

2. 我们还学习了常见的模型属性,如 _order 用于排序, _rec_name 用于记录展示的默认值.

3. 模型中的字段定义了所有它存储的数据,我们了解了可用的非关联字段属性以及它们支持的属性

4. 我们还学些了关联字段的几种类型: many2one,one2many,many2many,以及它们如何定义模型间的关系,包括父子关系

5. 大多数字段在数据库中存储用户的输入,但字段也可以通过 Python代码自动计算值.
      我们看到了如何实现计算字段,以及一些高级用法,如使计算字段可写以及可搜索

6. 还有模型定义的一部分是约束,保持数据一致性和执行验证,可通过PostgreSQL或Python代码实现.      

标签:12,ERP,fields,模型,关联,字段,book,Odoo,id
来源: https://www.cnblogs.com/Rowry/p/13405580.html

专注分享技术,共同学习,共同进步。侵权联系[admin#icode9.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有