Odoo プラグイン アプリケーションのモデル (モデル) に関する詳細なディスカッション -- Alanhou

モデルの表示と並べ替え

vi models/library_book.py
# 在class内部加入如下代码
# 为模型添加更为友好的描述,更新后在Settings下可发现相应的变化(需开启调试模式Database Structure>Models)
	_description = 'Library Book' 
# 默认情况下Odoo使用内置的id进行排序,可以通过逗号分隔指定多个字段进行排序,desc表示降序,仅能使用数据库中存储的字段排序,外部计算后的字段不适用,_order有些类似SQL语句中的ORDER BY,但无法使用NULL FIRST之类的语句
	_order = 'date_release desc, name'
# _rec_name用于配置记录表示,模型记录在被其它模型引用时会有表示信息,如user_id为1时表示Administrator用户,当它在表单视图中显示时,会显示为用户名而非id,因为_rec_name的默认值是name,这也是建议在模型内添加name字段的原因,如没有name字段,则采用模型记录的标识符,类似(library.book, 1)
	_rec_name = 'short_name'
	short_name = fields.Char('Short Title', required=True)

レコード表現には、Odoo 8.0 の新しいマジック フィールド display_name もあり、その値は name_get() メソッドによって生成されます。デフォルトでは、name_get() は _rec_name 属性を使用してデータを検索します。より複雑なロジックが必要な場合は、このメソッドをオーバーライドして、レコード ID とレコード表現を含むタプルを返します。通常のワールド(2017-06-01)を表示するには、クラスに次のメソッドを定義します

def name_get(self):
	result = []
	for record in self:
		result.append(
		(record.id,
		"%s (%s)" % (record.name, record.date_release)
		))
	return result

モデルのデータ型の説明

# vi models/library_book.py
...
# Char用于字符串
    short_name = fields.Char('Short Title', required=True)
# Text用于多行字符串
    notes = fields.Text('Internal Notes')
# Selection用于选择列表,由值和描述对组成,选择的值将存储在数据库中,可以为字符串或整型,描述默认可翻译
# 虽然整型的值看似简洁,但注意Odoo会把0解析为未设置(unset),因而当存储值为0时不会显示描述
    state = fields.Selection(
        [('draft', 'Not Available'),
        ('available', 'Available'),
        ('lost', 'Lost')],
        'State')
# Html类似text字段,但一般用于存储富文本格式的HTML
    description = fields.Html('Description')
# Binary字段用于存储二进制文件,如图片或文档
    cover = fields.Binary('Book Cover')
# Boolean字段用于存储True/False布尔值
    out_of_print = fields.Boolean('Out of Print?')
# Date字段用于存储日期,ORM中以字符串格式对其进行处理,但以日期形式存放在数据库中,该格式在odoo.fileds.DATE_FORMAT中定义
    date_release = fields.Date('Release Date')
# Datetime用于存储日期时间,在数据库中以UTC无时区时间(naive)存放,ORM中以字符串和UTC时间表示,该格式在odoo.fields.DATETIME_FORMAT中定义
    date_updated = fields.Datetime('Last Updated')
# Integer即为整型
    pages = fields.Integer('Number of Pages')
# Float用于存储数值,其精度可以通过数字长度和小数长度一对值来进行指定
    reader_rating = fields.Float(
        'Reader Average Rating',
        digits=(14, 4), # Optional precision (total, decimals)
        )
# 此外Monetary用于存储货币金额
# 所有字段都有一些通用属性,如我们可以将pages字段丰富为:
pages = fields.Integer(
        string='Number of Pages',
        default=0,
        help='Total book page count',
        groups='base.group_user',
        states={
    
    'lost': [('readonly', True)]},
        copy=True,
        index=False,
        readonly=False,
        required=False,
        company_dependent=False,
        )
# Char和HTML字段分别一些特殊属性,如:
 
short_name = fields.Char(
        string='Short Title', 
        size=100, # 仅用于Char字段
        translate=False, # Text字段也可使用
        required=True)
 
description = fields.Html(
        string='Description',
        # 以下均为可选属性
        sanitize=True,
        strip_style=False,
        translate=False,
        )

上記の例から、フィールドに属性を含めることができることもわかります。

  • 文字列は、UI のビュー ラベルで使用されるフィールドのタイトルです。これはオプションです。設定されていない場合は、下線付きのタイトル本文がフィールド名のスペースに置き換えられて表示されます。
  • sizeは Char フィールドにのみ適用され、許可される最大文字数を設定するために使用されます。通常は推奨されません
  • このフィールドは、translate がTrue に設定されている場合に翻訳可能であり、UI 言語に応じて異なる値を含めることができます。
  • default はデフォルト値を設定するために使用され、default=_compute_default のようにメソッド名を後に続けることもできます。メソッドの定義はフィールド定義の前に定義する必要があります。
  • help UIページに表示される説明プロンプトの内容を設定します
  • groupsこのフィールドを使用できるセキュリティ グループを設定します。値は、カンマで区切られた一連のセキュリティ グループの XML ID の文字列です。詳細については、アクセス許可の制御に関するフォローアップ記事を参照してください。
  • 状態では、状態フィールドの設定に応じて、ユーザー インターフェイスで読み取り専用、必須、非表示の属性を動的に設定できます。
  • copy は、レコードがコピーされたときにコピーであるかどうかを示すために使用されます。デフォルト値は、非リレーショナル フィールドとMany2one フィールドの場合は True、One2many フィールドと計算フィールドの場合は False です。
  • インデックスが True に設定されている場合、データベース内のこのフィールドに対してインデックスが作成され、非推奨の select=1 属性が置き換えられます。
  • readonly は、 UI でフィールドをデフォルトで読み取り専用に設定します。
  • ユーザーインターフェイスではデフォルトで必須設定フィールドが必須です
  • sanitizeは HTML フィールドに使用され、セキュリティ上の脅威を伴うタグを削除し、グローバル入力をサニタイズします。より細かく制御するには、サニタイズの設定後にいくつかのキーワードを使用できます。
  • santitize_tags =True: ホワイトリストにないタグをサニタイズします (odoo/odoo/tools/mail.py の allowed_tags で定義されています)
  • sanitize_attributes =True: ホワイトリスト以外の属性 (odoo/odoo/tools/mail.py のsafe_attrs で定義) を追加でサニタイズします。
  • sanitize_style =True: ホワイトリスト外のスタイル属性をサニタイズします (odoo/odoo/tools/mail.py の _style_whitelist で定義)
  • strip_style =True: すべてのスタイル要素を削除します
  • ストリップクラス=True: クラス属性をクリアします
  • company_dependentは会社ごとに異なる値を保存するために使用され、非推奨の Property フィールドを置き換えます。

知識を広げる

前の例のようなオプションを含めるだけでなく、Selection フィールドをメソッド参照に使用して、オプションのリストを動的に取得することもできます。これについては、この記事で後ほど説明します。

Date フィールドと Datetime フィールドには、操作をより便利にするためのツール メソッドがいくつかあり、Date には次のメソッドがあります。

  • field.Date.from_string(string_value) は文字列を日付オブジェクトに解析します。
  • field.Date.to_string(date_value) は、 Date オブジェクトを文字列として表します。
  • field.Date.today()は今日の日付を文字列形式で返します。これはデフォルト値の取得に使用できます。
  • field.Date.context_today(record, timestamp) は、レコード (またはレコードセット) コンテキストのタイムゾーンによる文字列形式のタイムスタンプを返します (タイムスタンプ パラメーターが渡されない場合は日付を返します)。

Datetime には次のメソッドがあります。

  • field.Datetime.from_string(string_value) は、文字列を日時オブジェクトに解析します。
  • field.Datetime.to_string(date_value) は日時オブジェクトを文字列として表します
  • field.Datetime.now()は現在の日時を文字列形式で返します。これはデフォルト値の取得に使用できます。
  • field.Datetime.context_timestamp(record, timestamp) は、レコード コンテキストのタイム ゾーンに従って、タイム ゾーン フリー時間を文字列形式に変換します。このメソッドは、デフォルト値の取得には適していませんが、外部システムにデータを転送するために使用できます。基本フィールドに加えて、リレーショナル
    タイプのフィールドもあります:Many2one、One2many、Many2many (後で詳しく説明します)。さらに、後で紹介する値を自動的に計算するフィールド コンピューティングもあります。

注意深い読者であれば、Odoo モデルはデフォルトでいくつかのフィールドも作成することに気づくでしょう。そのため、モデルを定義するときにこれらの名前を使用しないでください。これらのフィールド名には、自動的に生成された識別子 ID といくつかのログ監査フィールドが含まれます。

  • create_date : レコードが作成された時刻
  • create_uid : レコードを作成したユーザー
  • write_date : レコードが最後に編集されたときの終了時刻
  • write_uid : 最後にレコードを編集したユーザー。
    これらの自動的に作成されたログ フィールドは、_log_access=False 属性を追加することでオフにできます。モデルに追加できる別の特別なフィールド名がアクティブです。これは、レコードが使用可能かどうかを識別するブール型フィールドです。
active = fileds.Boolean('Active', default=True)

デフォルトでは、アクティブに True に設定されているレコードのみが表示されます。非表示のレコードを取得するには、ドメイン コントローラー [('active', '=', False)] を渡すことができます。別の方法は、ドメイン コントローラーに 'active_test': False を追加することです。アンビエント コンテキスト、そうでない場合、ORM は使用できないレコードをフェッチできません。

豆知識: コンテキストを変更しても、表示されるレコードと不可視のレコードの両方を取得できない場合があります。この場合は、['|', ('active', '=', True), ('active', '=' 、False)] フィールド。[('acitve', 'in' (True, False))] は期待どおりに機能しないことに注意してください。

浮動小数点フィールドの高精度構成

浮動小数点数フィールドを使用する場合、エンド ユーザーは精度を構成する必要がある場合があります。以下では、Decimal Precision Configuration プラグインを使用してそれを実現し、ユーザーが指定できるコスト フィールドを Library Book モデルに追加します。精度。

1. まず、バックグラウンドで [アプリ] をクリックして [10 進精度構成] を検索し、[インストール] をクリックしてインストールします。
ここに画像の説明を挿入
2. 開発者モードをオンにして、[設定] > [テクニカル] > [データベース構造] > [10 進精度] をクリックし、書籍価格を追加し、精度を小数点以下 2 桁に設定します。
ここに画像の説明を挿入
3. 依存関係を追加する

vi local-addons/my_module/__manifest__.py
# 加入依赖
'depends': ['base', 'decimal_precision'],

4.cost_priceを追加し、前述の精度設定を使用します

vi local-addons/my_module/models/library_book.py
# 相入数字精度
from odoo.addons import decimal_precision as dp
# 通过get_precision方法在Decimal Accuracy中插件前面设置的精度配置
cost_price = fields.Float('Book Cost', dp.get_precision('Book Price'))

以上の方法により、エンドユーザー自身がバックグラウンドで精度を制御することが可能となります。

モデルにMonetaryフィールドを追加する

Money フィールドには、金額を保存するための追加の通貨フィールドが必要です。

vi local-addons/my_module/models/library_book.py
# 在类中添加字段
    currency_id = fields.Many2one('res.currency', string='Currency')
    retail_price = fields.Monetary(
        'Retail Price',
        # optional: currency_field='currency_id',
        )

更新後、[設定] > [技術] > [データベース構造] > [モデル] で対応する変更を確認できます。通貨フィールドは通常、currency_id を使用しますが、もちろん他の名前も使用できます。この場合、オプションのcurrency_field 属性で指定する必要があります。 。

ヒント: これは非常に便利で、販売注文通貨や会社決済通貨など、複数のフィールドMany2one(res.currency)を設定することで、異なる通貨を管理できます。

リレーショナル フィールドをモデルに追加する

Odoo のモデル間の関係はリレーショナル フィールドで表され、多対 1 (m2o と省略)、1 対多 (o2m と省略)、多対多 (m2m と省略) の 3 つの異なる関係があります。 )。私たちの本の場合、各本には出版社があるため、本と出版社の関係は多対 1 です。出版の観点から見ると、本との関係は 1 対多であり、各本は次のことができます。には複数の著者がおり、各著者が複数の本を持つこともできるため、著者と本の関係は多対多になります。

Odoo はパートナー モデル res.partner を使用して人、機関、住所を表すため、ここでは著者と発行者が res.partner を使用します。

vi local-addons/my_module/models/library_book.py 
 
# 为出版商添加many-to-one字段,many-to-one字段会在数据表中添加一列并存储相关联记录的ID,在数据库层面上会为其创建一个外键约束,以确保所存储的这些ID在关联表为有效引用 。通常应考虑添加索引,默认不会添加,需通过index=True属性来设置
    publisher_id = fields.Many2one(
        'res.partner', string='Publisher',
        # 以下为可选项
# ondelete属性用于决定many-to-one字段相关联记录删除时的操作,如本例中出版社删除后对书本如何操作?默认值为set null,将字段设为空值,还可以为restrict:防止相关联记录的删除,cascade:删除相关联记录
        ondelete='set null',
# context和domain在其它关联字段中也有效,主要用于客户端。在通过字段点击到相关联记录视图时,context为客户端上下文添加变更 ,如通过该视图创建新记录时设置默认值
        context={
    
    },
# domain为相关联记录添加过滤以控制可用记录
        domain=[]
        )
 
# 添加出版商和书的one-to-many(需要继承partner模型)以及书和作者的many-to-many,
class ResPartner(models.Model):
# _inherit在后面会进行更深入的讨论
    _inherit = 'res.partner'
# one-to-many与many-to-one相反,其在数据库中并没有体现,更多的是程序端的shortcut来让视图能表示相关联的记录
    published_book_ids = fields.One2many(
        'library.book', 'publisher_id',
        string = 'Published Books'
        )
# 书到作者的关系已通过author_ids定义,此处用于添加作者到书的关系
# many-to-many关联不在模型的表中添加列,此类关联在数据库中通过中间表实现,创建新记录时在该表中创建书与作者间的关联,Odoo自动创建关联表,默认表名使用按字母排序的两个模型名加上_rel,当然我们可以使用relation属性来覆盖该默认值
# 注意:如果两表的名称较长,会导致自动生成的名称超PostgreSQL 63个字符的上限,因此建议在两表的表名起过23个字符时使用relation属性来指定一个更短的名称
    authored_book_ids = fields.Many2many(
        'library.book',
        string='Authored Books',
        # optional: relation = 'library_book_res_partner_rel'
        )

更新後、[設定] > [テクニカル] > [データベース構造] > [モデル] で対応する変更を確認できます。
拡張ナレッジ
Many2one フィールドには、ORM がこのフィールドで SQL マージを使用できるようにする auto_join 属性があり、ユーザー権限制御によって制限されず、録音許可。これにより、場合によってはパフォーマンスが向上する可能性がありますが、通常は推奨されません。

関連するフィールドを定義する前述の方法は比較的簡潔であり、導入を完全にするために次の補足が行われます。

One2many フィールドのプロパティ:

  • comodel_name : これはターゲット モデル識別子であり、関連するすべてのフィールドに必須ですが、このキーワードを使用せずに特定の場所に定義できます。

  • inverse_name : One2many にのみ使用され、逆 Many2one に関連付けられたターゲット モデルのフィールド名です。

  • limit : One2many および Many2many の場合のみ、ユーザー インターフェイスの
    Many2many フィールド プロパティで読み取りレコード数の制限を設定します

  • comodel_name : 上記を参照

  • relationship : 関連するデータテーブル名をサポートし、デフォルトの自動生成名をオーバーライドします。

  • column1 : このモデルに関連付けられたデータテーブルMany2oneフィールド名

  • column2 : コモデルモデルに関連付けられたデータテーブルのMany2oneフィールド名
    Many2manyアソシエーションの場合、ほとんどの場合、ORMはこれらの属性のデフォルト値を適切に処理し、逆Many2manyアソシエーションと既存のリレーションテーブルを検索し、column1を適切に反転することもできます。および列2の値。ただし、人間の介入が 2 つの場合に必要です。1 つのケースでは、2 つのモデル間に複数のMany2many アソシエーションが必要です。このとき、競合を避けるためにリレーション属性を指定する必要があります。もう 1 つのケースでは、自動的に関連付けられる長さ生成された関連付けテーブル名が PostgreSQL データベース オブジェクト名の 63 文字制限を超えています。

自動生成される関連テーブルの名前は _rel ですが、同時に関連テーブルの主キーのインデックスが作成され、識別子はrel id _id_key となり、これも 63 文字の上限に制限されます。

モデルに階層を追加する

階層はそれ自体へのモデル関連付けとして動作し、各レコードは同じモデル内に親レコードといくつかの子レコードを持ちます。これは、モデル自体との多対 1 の関連付けを通じて実現できます。Odoo では、このタイプのフィールドに対するネストされたセット モデルのサポートも追加されており、アクティブ化後は、ドメイン フィルタリングで child_of 演算子を使用するクエリが大幅に高速化されます。

書籍を分類するための階層分類ツリーを作成し、library_book_categ.py ファイルを追加しましょう。

  1. このファイルをロードするコードを __init__.py に追加します
vi local-addons/my_module/models/__init__.py 
 
from . import library_book_categ

2. library_book_categ.py ファイルの内容を追加します

vi local-addons/my_module/models/library_book_categ.py
 
from odoo import models, fields, api
 
class BookCategory(models.Model):
    _name = 'library.book.category'
    name = fields.Char('Category')
# Many2one关联用于创建引用父记录字段
    parent_id = fields.Many2one(
        'library.book.category',
        string='Parent Category',
# 此处值必须为restrict或cascade
        ondelete='restrict',
# 添加索引用于快速发现子记录
        index=True)
# One2many关联不会在数据库中添加额外字段,而是在获取所有记录时快捷地把此记录作为父记录
    child_ids = fields.One2many(
        'library.book.category', 'parent_id',
        string='Child Categories')
# 为使用等级的支持,加入如下代码。通过以下三项配置可获取更快的数据读取速度,但同时写入的开销也会更大,第一项配置为True时后两项将会用于存储等级树中的搜索数据;默认使用parent_id字段作为记录的父级,也可通过_parent_name来进行指定,如_parent_name = 'parent_id'
    _parent_store = True
    parent_left = fields.Integer(index=True)
    parent_right = fields.Integer(index=True)
# 防止循环关联,添加如下方法。以下配置防止记录同时存在于上升和下降树级中,这会导致无限循环,models.Model中有一个_check_recursion工具方法可在此处复用
    @api.constrains('parent_id')
    def _check_hierarchy(self):
        if not self._check_recursion():
            raise models.ValidationError(
                'Error! You cannot create recursive categories.')

追加知識: 上記のメソッドは「静的」レベルで使用されます。つまり、一般的な操作は読み取りとクエリであり、更新はほとんど実行されません。当然のことながら、本の分類は比較的固定されており、読者は分類別に検索することが多いため、非常に応用可能です。その理由は、ネストされたセット モデル (Nested Set Model) を使用するには、新しいクラスが挿入、削除、または変更されるときに、parent_left とparent_right およびその他の関連するデータベース インデックスを更新する必要があり、多くの問題が発生するためです。並列トランザクションが同時に実行されると、システムのオーバーヘッドが大きくなります。

ヒント: 動的階層を扱う場合、標準のparent_idとchild_idsの関連付けにより、テーブルレベルのロックが回避され、パフォーマンスが向上します。

モデルに制約検証を追加する

予期せぬ状況を回避するためにモデルを検証できます。制約には 2 種類あります。1 つはデータベース レベルのチェック、もう 1 つはサーバー レベルのチェックです。前者は PostgreSQL でサポートされている制約に限定されており、最も一般的に使用されるのは UNIQUE 制約であり、CHECK 制約と EXCLUDE 制約もあります。これらのいずれも要件を満たさない場合は、Python を記述することで Odoo サーバー レベルで制約を適用できます。コード。

以下では、本のタイトルの重複を防ぐためにデータベース制約を追加します。Python モデル制約は、発売日が現在の日付より遅くなるのを防ぎます:
1. データベース制約を作成します。

vi local-addons/my_module/models/library_book.py
... 
# _sql_constraints可通过列表来设置约束,每个约束为一个包含三个元素的元组:1.约束标识符后缀,如此处的name_uniq会形成一个library_book_name_uniq约束名;2.在PostgreSQL中修改或创建数据表的SQL指令;3.在违反约束时返回给用户的消息。前面提到过还可以使用其它数据表约束,但请注意如NOT NULL这样的列约束是无法使用的,可在官网获取更多<a href="https://www.postgresql.org/docs/current/static/ddl-constraints.html" target="_blank" rel="noopener">PostgreSQL约束知识</a>
    _sql_contraints = [
        ('name_uniq',
        'UNIQUE (name)',
        'Book title must be unique.')
        ]

2. Python コードを使用してサーバーレベルの制約をクラスに追加します

vi local-addons/my_module/models/library_book.py
 
from odoo import api
...
# 此处使用@api.contrains装饰器,表示在参数列表中字段发生变化时进行检测,如果检测失败,则抛出ValidationErorr异常
    @api.constrains('date_release')
    def _check_relase_date(self):
        for record in self:
            if(record.date_release and record.date_release > fields.Date.today()):
                raise models.ValidationErorr('Release date must be the past')

注: _constraints モデル属性はまだ存在しますが、バージョン 8.0 以降は非推奨です。 @api.constrains デコレータを使用することをお勧めします。

計算フィールドをモデルに追加する

このレコードまたは関連レコードからフィールド値を計算する必要がある場合がありますが、一般的な方法は、単価と数量に基づいて合計価格を計算することであり、Odoo では計算フィールドを通じて実現できます。この例では、リリース日に基づいて日数を計算することでこれを学習します。計算可能なフィールドも編集および検索できます。

vi local-addons/my_module/models/library_book.py
 
from datetime import timedelta
from odoo import api
from odoo.fields import Date as fDate
...
# 首先添加新字段,定义方法与普通字段相似,多了一个compute属性来指定执行计算的方法。虽然相似,但可计算字段和普通字段却大不相同,它在运行时动态计算,除非自己添加逻辑,默认是不可写、不可搜索的
    age_days = fields.Float(
        string='Days Since Release',
# 计算方法应保持为可计算字段设置值,否则会抛出错误并且很难调试,通常if条件语句会导致未能成功赋值
        compute='_compute_age',
# 写的支持可通过inverse方法来完成,它使用可计算字段的值来更新原始值
        inverse='_inverse_age',
# 通过search属性也可以让未存储计算字段可被搜索。本方法并不是完成实际搜索,而是接收字段用于搜索的操作符和值作为参数,返回替换了搜索条件的域。配合把Date Since Release字段搜索转化为等价于Release Date的搜索。
        search='_search_age',
# 可选的store=True配置将字段存入数据库,这时计算后值可像普通字段那样被获取,但借助@api.depends装饰器,ORM可以获知何时需重新计算并更新值,可以理解一个永久缓存。这样字段就可以使用搜索条件、排序、分组,而无需添加search方法了。
        store=False,
# compute_sudo=True用于需要提升权限完成计算的情况,需谨慎使用,它会忽略所有权限规则 ,包括多公司设置中的公司隔离规则 
        compute_sudo=False,
        )
# 添加计算逻辑,计算方法是在运行时动态计算的,但ORM采用缓存技术避免每次获取值时的无效运算,因而需要通过@depends装饰器来了解所依赖字段,已在需要时更新缓存中的值。
    @api.depends('date_release')
    def _compute_age(self):
        today = fDate.from_string(fDate.today())
        for book in self.filtered('date_release'):
            delta = (today - fDate.from_string(book.date_release))
            book.age_days = delta.days
# 添加方法用于在可计算字段上完成写的逻辑
    def _inverse_age(self):
        today = fDate.from_string(fDate.context_today(self))
        for book in self.filtered('date_release'):
            d = today - timedelta(days=book.age_days)
            book.date_release = fDate.to_string(d)
# 添加方法允许可计算字段的搜索
    def _search_age(self, operator, value):
        today = fDate.from_string(fDate.context_today(self))
        value_days = timedelta(days=value)
        value_date = fDate.to_string(today - value_days)
        # convert the operator
        # book with age > value have a date < value_date
        operator_map = {
    
    
            '>': '<', '>=': '<=',
            '<': '>', '<=': '>=',
        }
        new_op = operator_map.get(operator, operator)
        return [('date_release', new_op, value_date)]

他のモデルに保存されている関連フィールドを公開する

Odoo クライアントがサーバーからデータを読み取る場合、クエリ モデル内のフィールド値のみを取得でき、クライアントはサーバーのようにドット表記を使用してデータを取得できません。ただし、関連フィールドとして追加することで取得できます。以下では、発行者の都市に対してこれを実行します。

vi local-addons/my_module/models/library_book.py 
 
...
    publisher_city = fields.Char(
        'Publisher City',
        related='publisher_id.city',
        readonly=True
        )

関連フィールドは通常のフィールドに似ていますが、別の一連のトラバーサル チェーンを採用する追加の関連属性があります。この例では、publisher_id を使用して発行者に関連するレコードが取得され、次に city フィールドが読み取られます。publisher_id.country_id.country_code などのより長いチェーン構造も使用できます。上記では readonly 属性も使用しましたが、それ以外の場合はユーザーが値を変更でき、関連付けられた発行者の都市が変更されてしまいます。
拡張知識:
関連フィールドは実際には計算フィールドであり、関連モデルの値を読み取るための単なるショートカットです。これは計算可能なフィールドであるため、ストア フィールドもここで使用でき、関連するフィールドの名前、翻訳可能、必須などの属性を参照することもできます。さらに、compute_sudo に似た RELATED_sudo があり、これが True に設定されている場合、フィールド チェーン トラバーサルではユーザー権限のチェックが実行されません。

ヒント: リレーショナル フィールドで create() を使用すると、作成が完了するまで計算が待機するため、パフォーマンスが低下します。したがって、sale.order モデルや sale.order.line モデルなどの One2many 関連付けがあり、注文モデルのフィールドが明細モデルで参照されている場合、レコードの作成時に注文モデルのフィールドを明示的に読み取る必要があります。特に複数の行レコードがある場合、関連するフィールド「ショートカット」を使用する代わりに、「ショートカット」が作成されます。

「参照」フィールドを使用して動的関連付けを追加する

関連するフィールドを使用するには、関連するターゲット モデル (コモデル) を事前に決定する必要がありますが、これはユーザーが決定する必要がある場合もあり、使用するモデルと参照するレコードを設定するだけで済みます。 、関連するフィールドを使用して達成します

vi local-addons/my_module/models/library_book.py 
 
....
# 按helper方法的定义添加引用字段
    ref_doc_id = fields.Reference(
        selection='_referencable_models',
        string='Reference Document'
        )
...
# 添加helper方法来动态创建可选目录模型列表
    @api.model
    def _referencable_models(self):
        models = self.env['res.request.link'].search([])
        return [(x.object, x.name) for x in models]

参照フィールドは多対 1 フィールドに似ていますが、ユーザーがどのモデルを指すかを選択できる点が異なります。ターゲット モデルは、選択フィールドで指定されたリストから選択できます。このリストには、2 つのパラメータを持つタプルのセットが含まれている必要があります。最初のパラメータはモデルの内部識別子で、2 番目のパラメータはテキストの説明です。例えば

[('res.users', 'ユーザー'), ('res.partner', 'パートナー')]

一般に、固定リストを使用する代わりに、エンド ユーザーがモデル リストを構成できるようにすることができます。これは、[設定] > [技術] > [データベース構造] にある組み込みの参照可能モデルの目的でもあります。このモデルの組み込み識別子は res です。 .リクエスト.リンク。上記では、選択属性リストを動的に作成するために参照できるすべてのモデル レコードを参照するメソッドを使用しました。このメソッドは選択後に直接参照できますが、ここでは引用符を追加しています。このメソッドはより柔軟であり、後で定義するフィールドで宣言されるメソッド。@api.model デコレーターは、レコードセットではなくモデル レベルで操作するために使用されます。

: この方法は非常に便利ですが、負荷が比較的高く、たとえば、多数のレコード参照フィールドをリスト ビューで表示する場合、値ごとに個別のクエリが発生するため、データベースに負荷がかかります。同時に、通常の関連フィールドのように使用することはできません データベースへの参照整合性。

継承を使用してモデルに機能を追加する

Odoo の重要な機能は、元の機能を編集せずに、モジュール プラグイン内の他のモジュール プラグインの機能を継承できることです。特性では、フィールドまたはメソッドの追加、既存のフィールドの変更、または既存のメソッドの継承のためのロジックを追加できます。これは最も一般的に使用される継承方法であり、公式ドキュメントでは伝統的/古典的継承と呼ばれています。一緒に、組み込みのパートナー モデルを継承し、著者ごとの書籍の数を計算可能フィールドに追加します。

vi local-addons/my_module/models/library_book.py 
 
 
class ResPartner(models.Model):
# 使用_inherit则是继承的模型进行修改而非替换,在继承类中定义的方法会替换父类中的,所以想要使用原方法时,需要使用super关键字
    _inherit = 'res.partner'
    _order = 'name'
    authored_book_ids = fields.Many2many(
        'library.book',
        string='Authored Books',
        # optional: relation = 'library_book_res_partner_rel'
        )
    count_books = fields.Integer(
        'Number of Authored Books',
        compute='_compute_count_books')
 
    @api.depends('authored_book_ids')
    def _compute_count_books(self):
        for r in self:
            r.count_books = len(r.authored_book_ids)

拡張された知識:

_inherit 従来の継承を使用すると、クラスの _name 属性に別の識別子を追加することで、親モデルの特性を新しいモデルにコピーすることもできます。

class LibraryMember(models.Model):
	_inherit = 'res.partner'
	_name = 'library.member'

新しいモデルは、res.partner 親モデルから独立した独自のデータ テーブルを持ちます。これは Partner モデルを継承しているため、その後の変更も新しいモデルに反映されます。公式文書ではプロトタイプ継承と呼ばれていますが、委任継承はより効率的な方法でこの要件を満たしており、後で紹介する一連のデータ構造をコピーする必要がないため、実際のアプリケーションではほとんど使用されません。

抽象モデルを使用してモデルの機能を再利用する

場合によっては、複数のモデルで使用したい特別な機能がある場合があります。コーディングを繰り返すことはコードの習慣としては良くないため、一度書いて何度も再利用するのが最善です。抽象モデルを使用して一般的なモデルを作成でき、その特性を他の一般的なモデルに継承して使用できます。以下に、アクティブ フィールドをモデルに追加し、アクティブ フラグを切り替えるためのアーカイブ メソッドを公開するアーカイブ機能を記述します。これが機能するのは、active がマジック フィールドであり、デフォルトでモデルに存在する場合、active=False を持つレコードがクエリから除外されるためです。

アーカイブ機能はプラグイン単独でも使用できますが、簡単にするために、以前から使用していた library_book.py ファイルを引き続き使用します。

# 抽象模型通过models.AbstractModel定义,拥有所有普通模型的属性,不同处在于ORM不会在数据库中创建一个实际表现,因而无法存储数据。它的用处就是作为模板来提供可复用特性。这个抽象模型非常简单,仅包含了一个active字段和一个切换active标识的方法。
class BaseArchive(models.AbstractModel):
    _name = 'base.archive'
    active = fields.Boolean(default=True)
 
    def do_archive(self):
        for record in self:
            record.active = not record.active
 
            
class LibraryBook(models.Model):
    _name = 'library.book'
# _inherit后可接字符串或列表,列表可用于继承多个模型类
    _inherit = ['base.archive']

拡張知識
メール プラグイン モデルによって提供される組み込みの抽象化モデル mail.thread があり、これによりモデル内のディスカッション機能が有効になります (多くのフォームの下部にあるメッセージ ウォールに使用されます)。AbstractModel に加えて、models.TransientModel もあります。性能はmodels.Modelと似ていますが、作成されるレコードは一時的なものでサーバー側のスケジュールされたタスクによってクリーンアップされ、その他は通常のモデルと同じです。これは、プロセスやレポートを実行するためにユーザー入力を必要とするなど、より複雑なユーザー操作 (ウィザードと呼ばれる) に役立ちます。

委任継承を使用して特性を別のモデルにコピーする

従来の継承では _inherit を使用して変更のためにモデル機能を継承しますが、既存の機能を使用したいだけで既存のモデルを変更する場合も依然としてあり、これは委任された継承を使用して _inherits で Odoo に実装されています。従来の継承はオブジェクト指向プログラミングの概念とは異なりますが、デリゲート エージェントはより似ており、親モデルの特性を含む新しいモデルを作成でき、ポリモーフィック継承、つまり 2 つ以上のモデルからの継承をサポートします。 。

次に、ブック メンバーシップを追加しましょう。これには、パートナー モデルに存在する ID と住所のデータに加え、開始日、終了日、カード番号などのメンバーが使用する情報が必要です。

vi local-addons/my_module/models/library_book.py 
 
class LibraryMember(models.Model):
    _name = 'library.member'
# 字典中的key为所继承的模型,值为进行关联的字段名
    _inherits = {
    
    'res.partner': 'partner_id'}
    partner_id = fields.Many2one(
    'res.partner',
    ondelete='cascade')
# 添加一些图书会员需用的字段
    date_start = fields.Date('Member Since')
    date_end = fields.Date('Termination Date')
    member_number = fields.Char()
    date_of_birth = fields.Date('Date of Birth')

メンバーを作成すると、データベースの res_partner テーブルに新しいレコードが作成され、library_member にも新しいレコードが作成され、library_member の Partner_id フィールドが res_partner に作成されたレコードの ID に設定されます。メンバー レコードは、新しいパートナー レコードに自動的に関連付けられます。では、メンバーが削除されるとどうなるでしょうか? これは、ondelete の値によって制御されます。ここで使用されるカスケードは、パートナーが削除されるとメンバーも削除されることを意味します。より保守的な設定は制限であり、パートナーを削除してもメンバーは削除されません。

委任の継承はフィールドにのみ使用でき、メソッドには使用できないことに注意してください。

知識を広げる

デリゲートによって継承されたユーザー モデル res.users は、res.partner から継承されます。つまり、User に格納されている一部のフィールドは、実際には Partner モデル (name フィールドなど) に格納されます。ユーザーを作成すると、Partner も自動的に作成されます。作成した。

-------------------------------------アランホーより抜粋

おすすめ

転載: blog.csdn.net/iuv_li/article/details/126094730