第12章:継承

Odooの強力な側面は、そのモジュール性です。モジュールはビジネスニーズに特化していますが、モジュール同士で相互に作用することもできます。これは、既存のモジュールの機能を拡張するのに便利です。例えば、不動産のシナリオでは、営業担当者の物件リストを通常のユーザービューに直接表示させたいとします。

ですが、具体的なOdooモジュールの継承を行う前に、標準的なCRUD (Create, Retrieve, Update or Delete) メソッドの動作をどのように変更できるかを見てみましょう。

Pythonでの継承

注釈

目標: このセクションの最後には、

  • 新規またはキャンセルされていないプロパティを削除することはできません。

リンクを解除
  • オファーが作成されると、物件の状態が Offer Received に変更されるようになります。

  • 既存のオファーよりも低い価格のオファーを作成することはできなくなります。

作成

不動産モジュールでは、標準的なCRUDアクションを行うために特別なものを開発する必要はありませんでした。Odooのフレームワークはそれらを行うために必要なツールを提供します。実際、そのようなアクションは、標準的なPythonでの継承のおかげで、このモデルにすでに含まれています。

from odoo import fields, models

class TestModel(models.Model):
    _name = "test_model"
    _description = "Test Model"

    ...

class TestModelModel を継承しています。これは create()read()write()unlink() を提供します。

これらのメソッド(および Model で定義されている他のメソッド)は、特定のビジネスロジックを追加するために拡張することができます:

from odoo import fields, models

class TestModel(models.Model):
    _name = "test_model"
    _description = "Test Model"

    ...

    @api.model
    def create(self, vals):
        # Do some business logic, modify vals...
        ...
        # Then call super to execute the parent method
        return super().create(vals)

デコレーターの model() は、 create() メソッドに必要です。 レコードセットの self の内容は、作成のコンテキストでは関連性がないからです。 しかし、他のCRUDのメソッドには必要ありません。

It is also important to note that even though we can directly override the unlink() method, you will almost always want to write a new method with the decorator ondelete() instead. Methods marked with this decorator will be called during unlink() and avoids some issues that can occur during uninstalling the model's module when unlink() is directly overridden.

Python 3 では super()super(TestModel, self) と同等です。 後者は、変更されたレコードセットを使用して親メソッドを呼び出す場合に、必要になるかもしれません。

危険

  • フローを壊さないように、 常に super() を呼び出すことは非常に重要です。 super() を呼び出したくない場合は、非常に特殊なケースに限られます。

  • 常に 親メソッドと一致したデータを返すようにしてください。例えば、親メソッドが dict() を返す場合、オーバーライドしたメソッドも dict() を返さなければなりません。

Exercise

CRUD メソッドにビジネス・ロジックを追加してみましょう。

  • 状態が「新規」または「キャンセル」でない場合、プロパティの削除を防止します

ヒント: ondelete() デコレータで新しいメソッドを作成し、self は複数のレコードを持つレコードセットにすることができます。

  • オファーの作成時に、物件情報の状態を Offer Received に設定します。また、ユーザが既存のオファーよりも低い金額のオファーを作成しようとすると、エラーを発生させるようにします。

ヒント: property_id フィールドは vals で利用できますが、 int 型です。 estate.property オブジェクトをインスタンス化するには、 self.env[model_name].browse(value) () を使用します。

モデルの継承

参考: このトピックに関連する文書は 継承と拡張 にあります。

不動産モジュールでは、営業担当者にリンクされた物件のリストを、設定/ユーザーと会社/ユーザーのフォームビューに直接表示したいと考えています。そのためには、 res.users モデルにフィールドを追加し、それを表示するようにビューを変更する必要があります。

Odoo は 2 つの 継承 メカニズムを提供し、モジュラー方法で既存のモデルを拡張します。

1つ目の継承メカニズムでは、他のモジュールで定義されたモデルの動作を、次の手順で変更することができます。

  • モデルにフィールドを追加し、

  • モデル内のフィールドの定義をオーバーライドし、

  • モデルに制約を加え、

  • モデルにメソッドを追加し、

  • モデルの既存のメソッドをオーバーライドする。

2つ目の継承メカニズム (デリゲーション) は、モデルのすべてのレコードが親モデルのレコードにリンクされ、この親レコードのフィールドへの透過的なアクセスを可能にします。

継承メソッド

Odooでは、1つ目のメカニズムが最もよく使われています。今回のケースでは、既存のモデルにフィールドを追加したいので、1つ目のメカニズムを使用することになります。例えば、次のようになります。

from odoo import fields, models

class InheritedModel(models.Model):
    _inherit = "inherited.model"

    new_field = fields.Char(string="New Field")

モデルに2つのフィールドを追加する実用的な例は こちら です。

慣例的に、継承されたモデルはそれぞれのPythonファイルで定義されます。この例では、 models/inherited_model.py となります。

Exercise

Users にフィールドを追加してみましょう。

  • 次のフィールドを res.users に追加します。

フィールド

プロパティ id

estate.property のセールスマンを参照するフィールドの One2multiの逆数

  • このフィールドにドメインを追加して、利用可能な物件情報のみをリストアップするようにします。

次のセクションでは、ビューにフィールドを追加し、すべてが正常に動作していることを確認しましょう!

ビューの継承

参照: このトピックに関連するドキュメントは、 :ref:`reference/view_records/inherited from にあります。

注釈

目標: このセクションの最後には、営業担当者にリンクされた利用可能な物件情報のリストが、そのユーザーフォームのビューに表示されるようになっています。

ユーザ

Odooでは、既存のビューをそのまま変更する (上書きする) のではなく、ルートビューのトップに子の 拡張 ビューを適用できるといった、ビューの継承を提供しています。この拡張ビューは、親ビューにコンテンツを追加・削除することができます。

拡張ビューは inherit_id フィールドを使用して親を参照します。単一のビューの代わりに、 arch フィールドには、親ビューのコンテンツを選択・変更する複数の xpath 要素を含むようにします。

<record id="inherited_model_view_form" model="ir.ui.view">
    <field name="name">inherited.model.form.inherit.test</field>
    <field name="model">inherited.model</field>
    <field name="inherit_id" ref="inherited.inherited_model_view_form"/>
    <field name="arch" type="xml">
        <!-- find field description and add the field
             new_field after it -->
        <xpath expr="//field[@name='description']" position="after">
          <field name="new_field"/>
        </xpath>
    </field>
</record>
expr

親ビューで単一の要素を選択している XPath 式。要素がない、または複数の要素に一致する場合にエラーが発生します。

position

マッチした要素に適用する操作:

inside

マッチした要素の末尾に xpath のボディを追加します。

replace

マッチした要素を xpath のボディーに置き換え、新しいボディの $0 ノードの発生を元の要素に置き換えます。

before

一致する要素の前に xpath の本体を兄弟として挿入します。

after

一致する要素の後に xpaths の本体を兄弟として挿入します。

attributes

xpath のbodyで指定された attribute 要素を使って、マッチした要素の属性を変更します。

単一の要素にマッチする場合、 position 属性を検索対象の要素に直接設定することができます。次のどちらの継承も同じ結果になります。

<xpath expr="//field[@name='description']" position="after">
    <field name="idea_ids" />
</xpath>

<field name="description" position="after">
    <field name="idea_ids" />
</field>

ビュー継承拡張の例は こちら にあります。

Exercise

Usersビューにフィールドを追加してみましょう。

新しいノートブックページの base.view_users_formproperty_ids フィールドを追加します。

ヒント: ユーザービューの継承例は こちら です。

Odooではモジュラーコンセプトのため、継承が広く使われています。詳細については、対応するドキュメントをお読みください。

:doc:`next chapter <13_other_module>`では、他のモジュールとやり取りする方法を学びます。