インポート可能なモジュールの書き込み

重要

このチュートリアルでは、 :doc:の server_framework_101 チュートリアルと :doc:の define_module_data チュートリアルに精通しています。

しかし、開発者はモジュールを書くのにPythonのフルパワーを持つことを好みますが、そうすることはできないことがあります。 通常 Odoo のようなカスタムPythonコードの展開を許可しないマネージドホスティングソリューションです。 om platform.

However, the flexible nature of Odoo is meant to allow customizations out of the box. Whilst a lot is possible with Studio, it is also possible to define models, fields and logic in XML Data Files. This makes it easier to develop, maintain and deploy these customizations.

このチュートリアルでは、XMLデータファイルでモデル、フィールド、ロジックを定義し、それらをモジュールにまとめる方法を学びます。 これらは*インポート可能なモジュール*、または*データモジュール*と呼ばれることがあります。モジュール開発に対するこのアプローチの制限もあります。

Problem ステートメント

:doc:の server_framework_101 チュートリアルと同様に、不動産の概念に取り組んでいます。

私たちの目標は、 サーバーフレームワーク101 チュートリアルに似た方法で不動産資産を管理する新しいアプリケーションを作成することです。 Pythonファイルの代わりにXMLデータファイルでモデル、フィールド、ロジックを定義します。

このチュートリアルの最後に、アプリで次のことができるようになります。

  • 販売中の不動産物件の管理

  • これらのプロパティをウェブサイトに公開

  • ウェブサイトからオンラインでオファーを承認する

  • プロパティが売却されたときに購入者に請求する

モジュール構造

どの開発プロジェクトでもそうであるように、明確な構造により、コードの管理と保守が容易になります。

Python ファイルと XML ファイルの両方を使用する標準の Odoo モジュールとは異なり、データモジュールは XML ファイルのみを使用します。 したがって、あなたの作業ツリーは次のようになります。

estate
├── actions
│   └── *.xml
├── models
│   └── *.xml
├── security
│   └── ir.model.access.csv
│   └── estate_security.xml
├── views
│   └── *.xml
├── __init__.py
└── __manifest__.py

The only Python files you will have are the __init__.py and __manifest__.py files. The __manifest__.py file will be the same as for any Odoo module, but will also import its models in the data list.

:file:`__manifest__.pyのセクションの`data`にあるファイルを、モデルファイルから始まる依存関係の順序でリストすることを忘れないでください。

The __init__.py file is empty, but is required for Odoo to recognize the module if you ever want to deploy your module in the classic way (by adding it in an addons path). It is not strictly necessary for modules that will be imported, but it is a good practice to keep it.

モジュールのデプロイ

To deploy the module, you will need to create a zip file of the module and upload it to your Odoo instance. Make sure that the module base_import_module is installed on your instance, then go to the Apps ‣ Import Module and upload the zip file. You must be in developer mode to see the Import Module menu item.

モジュールを変更する場合は、新しいzipファイルを作成し、再度アップロードする必要があります。 モジュール内のすべてのデータをリロードします。 ただし、以前に作成したフィールドのタイプを変更するなど、一部の操作は不可能であることに注意してください。 以前のバージョンのモジュールによって作成されたデータ(削除されたフィールドなど)は自動的に削除されません。 一般的に これを処理する最も簡単な方法は、新しいデータベースから始めるか、新しいバージョンをアップロードする前にモジュールをアンインストールすることです。

モジュールをアップロードするとき、ウィザードは2つのオプションを受け付けます:

  • Force init: if your module is already installed and you upload it again; このオプションをチェックすると、XMLファイル内で`noupdate="1"とマークされたすべてのデータが強制的に更新されます。

  • デモデータをインポート: selfexplanatory

:doc:`odoo-bin <../reference/cli>`コマンドラインツールを使用して、モジュールをデプロイすることもできます。

$ odoo-bin deploy <path_to_your_module> https://<your_odoo_instance> --login <your_login> --password <your_password>

このコマンドは、ウィザードの Force init オプションに相当する --force オプションも受け付けます。

モジュールのデプロイに使用するユーザーには、「管理/設定」アクセス権限が必要です。

Exercise

  1. 次のようにフォルダとファイルを作成してください。

    • /home/$USER/src/tutorials/estate/__init__.py

    • /home/$USER/src/tutorials/estate/__manifest__.py

    __manifest__.py`ファイルは、モジュールの名前と依存関係のみを定義する必要があります。 今のところ必要なフレームワークモジュールは ``base` (および base_import_module )だけですが、あなたのモジュールは厳密に言えば*依存*しません。 モジュールをインポートできるようにする必要があります)。

  2. モジュールのzipファイルを作成し、Odooインスタンスにアップロードします。

モデルと基本フィールド

想像できるように、XMLファイル内のモデルとフィールドを定義することは、Pythonほど簡単ではありません。

データファイルは順番に読み込まれるため、要素を正しい順序で定義する必要があります。 たとえば、そのモデルにフィールドを定義する前に、モデルを定義する必要があります。 を選択し、ビューにフィールドを追加する前にフィールドを定義する必要があります。

さらに、XML は Python よりもはるかに冗長なものです。

まず、モジュールの models ディレクトリに不動産プロパティを表す単純なモデルを定義しましょう。

Odoo モデルは ir.model レコードとしてデータベースに保存されます。他のレコードと同様に、XML ファイルで定義することができます:

<?xml version="1.0" encoding="utf-8"?>
<odoo>
    <record id="model_real_estate_property" model="ir.model">
        <field name="name">Real Estate Property</field>
        <field name="model">x_estate.property</field>
    </record>
</odoo>

データファイルで定義されているすべてのモデルとフィールドの接頭辞は x_ でなければならないことに注意してください。 これは必須で、Pythonファイルで定義されているモデルやフィールドと区別するために使用されます。

Python で定義された古典的なモデルと同様に、Odoo は自動的に複数のフィールドをモデルに追加します。

  • id (Id ) モデルのレコードに対する一意の識別子。

  • create_date (Datetime ) レコードの作成日時。

  • create_uid (Many2one ) レコードを作成したユーザ。

  • write_date (Datetime ) レコードの最終更新日。

  • write_uid (Many2one ) レコードを最後に更新したユーザ。

新しいモデルに複数のフィールドを追加することもできます。 名前(文字列)、値(float)、説明(html)、郵便番号(文字列)のような単純なフィールドを追加しましょう。

モデルと同様に、フィールドは単純に ir.model.fields モデルのレコードであり、データファイルのように定義することができます。

<?xml version="1.0" encoding="utf-8"?>
<odoo>
    <!-- ...model definition from before... -->
    <record id="field_real_estate_property_name" model="ir.model.fields">
        <field name="model_id" ref="estate.model_real_estate_property" />
        <field name="name">x_name</field>
        <field name="field_description">Name</field>
        <field name="ttype">char</field>
        <field name="required">True</field>
    </record>

    <record id="field_real_estate_property_selling_price" model="ir.model.fields">
        <field name="model_id" ref="estate.model_real_estate_property" />
        <field name="name">x_selling_price</field>
        <field name="field_description">Selling Price</field>
        <field name="ttype">float</field>
        <field name="required">True</field>
    </record>

    <record id="field_real_estate_property_description" model="ir.model.fields">
        <field name="model_id" ref="estate.model_real_estate_property" />
        <field name="name">x_description</field>
        <field name="field_description">Description</field>
        <field name="ttype">html</field>
    </record>

    <record id="field_real_estate_property_postcode" model="ir.model.fields">
        <field name="model_id" ref="estate.model_real_estate_property" />
        <field name="name">x_postcode</field>
        <field name="field_description">Postcode</field>
        <field name="ttype">char</field>
    </record>
</odoo>

新しいフィールドには、さまざまな属性を設定できます。基本フィールドの場合は、次のようになります。

  • name: フィールドの技術的な名前 (`x_`で始まる必要があります)

  • field_description: フィールドのラベル

  • help: インターフェイスに表示されるフィールドのヘルプテキスト

  • ttype: フィールドのタイプ (例: char, integer, float, html, など)

  • required: フィールドが必須かどうか(デフォルト: False)

  • readonly: フィールドが読み取り専用かどうか(デフォルト: False)

  • index: フィールドがインデックスされているかどうか (デフォルト: False)

  • copied: レコードを複製するときにフィールドをコピーするかどうか(デフォルト:リレーショナルではない非計算フィールドの場合は`True`、リレーショナルおよび計算されたフィールドの場合は`False`)

  • translate: フィールドが翻訳可能かどうか (デフォルト: False)

HTML のサニタイゼーションやその他のより高度な機能を制御するための属性も用意されています。完全なリストについては、 ir.model を参照してください。 :menuselection:`Settings --> Technical --> Database Structure --> Fields メニューにあるデータベース内の ields` モデルまたは、 `ir. `base`モジュール内のodel.fields`モデル定義。

Exercise

次の基本フィールドをテーブルに追加します

フィールド

必須

x_date_availability

日付

x_expected_price

フロート

True

x_bedrooms

整数

x_living_area

整数

x_facades

整数

x_garage

Boolean

x_garden

Boolean

x_garden_area

整数

x_garden_orientation

選択

x_garden_orientation フィールドには、'North'、'South'、'East'、'West'の4つの値が必要です。 選択リストは、最初にフィールド自体の ir.model.fields レコードを作成してから、 ir.model.fields.selection レコードを作成する必要があります。 これらのレコードには、field_id`name`(UIの名前)、`value`(データベースの値)の3つのフィールドがあります。 `シーケンス`フィールドを設定することもできます は、選択項目が UI に表示される順序を制御します (下のシーケンス値が最初に表示されます)。

デフォルト値

Python では、フィールド宣言の default 引数を使用してフィールドにデフォルト値を設定できます。 データモジュールでは、デフォルト値は ir を作成することで設定されます。 各フィールドの efault レコード。 例えば、以下のレコードを作成することで、すべてのプロパティに対して x_selling_price フィールドのデフォルト値を 100000 に設定することができます。

<odoo>
    <!-- ...model definition from before... -->
    <record id="default_real_estate_property_selling_price" model="ir.default">
        <field name="field_id" ref="estate.field_real_estate_property_selling_price" />
        <field name="json_value">100000</field>
    </record>
</odoo>

詳細は ir. を参照してください。 :menuselection:`Settings --> Technical --> Actions --> User-defined Defaults メニューにあるデータベース内の efault` モデルまたは、 `ir. `base`モジュール内のefault`モデル定義。

警告

これらのデフォルトは静的ですが、ir.default レコードの user_idcompany_id フィールドを使用して、会社やユーザーによって設定できます。 例えば、x_date_availability フィールドに対する動的なデフォルト値が "today" であることは不可能です。

セキュリティ

データモジュールのセキュリティはPythonモジュールとまったく同じで、 第4章:セキュリティー-簡単な紹介 にあります。

詳細はこのチュートリアルを参照してください。

Exercise

  1. :file:`ir.model.access.csv`ファイルを適切なフォルダに作成し、 :file:`__manifest__.py`ファイルで定義します。

  2. base.group_user というグループに読取、書込、追加、解除の許可を与えます。

ちなみに

ログ内の警告メッセージは、ほとんどの解を与えます ;-)

ビュー

ビューは、ユーザーがデータとやり取りできるUIコンポーネントです。 これらは XML ファイルで定義されており、モジュールの views ディレクトリにあります。

ビューとアクションは 第5章:最後に、いくつかのUIで遊べるもの第6章:基本ビュー で既に定義されているため、ここでは詳細を説明しません。

Exercise

`estate`モジュールに基本的なUIを追加します。

`estate`モジュールに基本的なUIを追加すると、ユーザーは不動産のプロパティを表示、作成、編集、削除できます。

  • モデル x_estate.property のアクションを作成します。

  • モデル x_estate.property のツリー ビューを作成します。

  • モデル x_estate.property のフォームビューを作成します。

  • アクションにビューを追加します。

  • ユーザーがアクションにアクセスできるように、メインメニューにメニュー項目を追加します。

関連

Odooのようなリレーショナルシステムの本当の力は、レコードをリンクする能力にあります。 通常の Python モジュールでは、単一のコード行の他のモデルにリンクするために、モデル上の新しいフィールドを定義することができます。 データモジュールではまだ可能ですが、Pythonと同じ構文を使うことができないため、もう少しレグワークが必要です。

:doc:`server_framework_101/07_relations`のように、`estate`モジュールにいくつかリレーションを追加します。次のリンクを追加します:

  • その物件を購入した顧客

  • 不動産を売却した不動産業者は

  • 物件の種類:家、アパート、ペントハウス、お城...

  • 物件を特徴づけるタグのリスト:居心地が良い、改装されている...

  • 受け取ったオファーのリスト

Many-to-one

many-to-oneは別のオブジェクトへのシンプルなリンクです。例えば、``resへのリンクを定義するためです。 artner``ではモデルの新しいフィールドを定義できます

<odoo>
    <!-- ...model definition from before... -->
    <record id="field_real_estate_property_partner_id" model="ir.model.fields">
        <field name="model_id" ref="estate.model_real_estate_property" />
        <field name="name">x_partner_id</field>
        <field name="field_description">Customer</field>
        <field name="ttype">many2one</field>
        <field name="relation">res.partner</field>
    </record>
</odoo>

多対一のフィールドの場合、いくつかの属性をリレーションの詳細に設定できます。

  • relation: リンクするモデルの名前 (必須)

  • ondelete: レコードが削除されたときに実行するアクション (デフォルト: set null)

  • domain: リレーションに適用するドメインフィルタ

Exercise

  1. 以下のフィールドで新しいモデル x_estate.property.type を作成します。

    フィールド

    必須

    名前

    文字

    True

  2. x_estate.property.type モデルにアクションとリストビューとメニューアイテムを追加します。

  3. アクセス権をユーザーに与えるために x_estate.property.type モデルに追加します。

  4. x_estate.property モデルに次のフィールドを作成します。

    フィールド

    必須

    x_property_type_id

    Many2one (x_estate.property.type)

    True

    x_partner_id (buyer)

    Many2one (res.partner)

    x_user_id (salesperson)

    Many2one (res.users)

  5. x_estate.property モデルのフォームビューに新しいフィールドを含めます。

多対多で

多対多とは、オブジェクトのリストへのリレーションです。この例では、新しい``x_estateへの多対多のリレーションを定義します。 roperty.tag`` モデル。このタグはプロパティの特性を表します。例えば、改装、居心地の良い等です。

プロパティは多くのタグを持つことができ、タグは多くのプロパティに割り当てることができます - これは典型的な多対多の関係です。

多対多のフィールドは多対一のフィールドと同じ方法で定義されていますが、ttype`は`many2many`に設定されています。 `relation 属性は、リンクするモデルの名前にも設定されます。他の属性は、リレーションを制御するために設定できます。

  • relation_table: リレーションに使用するテーブルの名前

  • column1column2: リレーションに使用するカラムの名前

これらの属性はオプションであり、競合を避けるために2つのモデルの間に複数の多対多のフィールドがある場合にのみ指定する必要があります。 ほとんどの場合、Odoo ORMは使用する正しいリレーションテーブルと列を決定することができます。

Exercise

  1. 以下のフィールドで新しいモデル x_estate.property.tag を作成します。

    フィールド

    必須

    名前

    文字

    True

  2. x_estate.property.tag モデルにアクションとリストビューとメニューアイテムを追加します。

  3. ユーザーへのアクセスを許可するために x_estate.property.tag モデルにアクセス権を追加します。

  4. x_estate.property モデルに次のフィールドを作成します。

    フィールド

    x_property_tag_ids

    Many2multi(x_estate.property.tag

  5. x_estate.property モデルのフォームビューに新しいフィールドを含めます。

1対多です

1対多のオブジェクトは、オブジェクトのリストへのリレーションです。この例では、新しい``x_estateへの1対多のリレーションを定義します。 roperty.offer`` モデル。このオファーは、顧客がプロパティを購入するために行ったオファーを表します。

1対多のフィールドは多対一のフィールドと同じ方法で定義されますが、ttype`は`one2many`に設定されています。 `relation 属性は、リンクするモデルの名前にも設定されます。リレーションを制御するためには、別の属性を設定する必要があります。

  • relation_field: 親モデルへの参照を含む関連モデル上のフィールドの名前 (多対一のフィールド)。 これは二つのモデルをリンクするために使用されます。

Exercise

  1. 新しいモデル x_estate.property.offer を以下のフィールドで作成します。

    フィールド

    必須

    x_price

    フロート

    True

    x_status

    選択

    承認、拒否

    x_partner_id

    Many2one (res.partner)

    True

    x_property_id

    Many2one (x_estate.property)

    True

  2. ユーザーへのアクセスを許可するために x_estate.property.offer モデルにアクセス権を追加します。

  3. 価格、partner_id、ステータスフィールドでツリービューとフォームビューを作成します。
    アクションやメニューを作成する必要はありません。
  4. フィールド x_offer_idsx_estate.property モデルとフォームビューに追加します。

計算されたフィールド

計算されたフィールドは、Odoo のコア概念であり、他のフィールドに基づいて計算されるフィールドを定義するために使用されます。 これは、他のフィールドから派生したフィールドに便利です。 例えば、サブレコードの合計 (販売注文に含まれるすべての商品の価格を足します)。

参考: このトピックに関連するドキュメントは、 計算フィールド にあります。

データモジュールは、任意の型の計算フィールドを定義することができますが、Python モジュールと比較して非常に限られています。 実際、データモジュールは任意のコードを実行できないシステムにデプロイされることになっています。 許可されている Python コードは非常に限られています。

注釈

データモジュールのために書かれたすべての Python コードは、実行可能な操作を制限する Sandbox 環境で実行されます。 たとえば、ライブラリをインポートすることはできず、OSファイルにアクセスすることもできず、コンソールに印刷することもできません。 いくつかのユーティリティが提供されていますが、これは使用されるサンドボックス化された環境によって異なります。

計算方法の場合 サンドボックスは限られていてコードの実行を可能にする 最低限のユーティリティしか提供しない Python ビルドに加えて、 datetimedateutiltime モジュール (たとえば、日付の計算に役立つ) にもアクセスできます。

また、Sandbox で "dot assignation" が無効になっているため、計算方法で property.x_total_area = 1 を書くことはできません。 辞書アクセスを使用する必要があります: property['x_total_area'] = 1 . access フィールドのドット表記は正常に動作します: property.x_garden_areax_garden_area フィールドの値を返します。

x_estate.property モデルに 2 つの "area" フィールドを定義しました: living_areagarden_area 。 2つの領域の合計を返すモデル上の計算フィールドを定義する データモジュールに次のコードを追加できます

<odoo>
    <!-- ...model definition from before... -->
    <record id="field_real_estate_property_total_area" model="ir.model.fields">
        <field name="model_id" ref="estate.model_real_estate_property" />
        <field name="name">x_total_area</field>
        <field name="field_description">Total Area</field>
        <field name="ttype">float</field>
        <field name="depends">x_living_area,x_garden_area</field>
        <field name="compute"><![CDATA[
for property in self:
    property['x_total_area'] = property.x_living_area + property.x_garden_area
        ]]>
        </field>
    </record>
</odoo>

注釈

サーバーのアクションでは、変数`records`を繰り返しますが、計算されたフィールドの場合 フィールドが計算されるレコードセットを含む self 変数を繰り返します。

depends 属性は、計算されたフィールドが依存するフィールドを定義するために使用され、(Python コードを使用して) フィールドを計算するために実行されるコードを定義するために compute 属性が使用されます。

Python モジュールとは異なり、計算されたフィールドはデフォルトで保存されます。計算されたフィールドを保存しないようにしたい場合(e. 、パフォーマンス上の理由やデータベースの膨張を避けるために)、store 属性を False に設定できます。 同じことが readonly にも適用されます: 計算フィールドを編集できないようにしたい場合は、readonly 属性を True に設定する必要があります。

`CDATA`セクションはXMLではなく文字列であることをXMLパーサに指定するために使用されます。 これにより、パーサーは Python コードを XML として解釈したり、余分なスペースを追加したりすることができなくなります。 コードがデータベースに挿入されると、モジュールのインストール時刻になります。

Exercise

  1. 上に示すように、x_living_areax_garden_area フィールドの合計を返す計算フィールドを x_estate.property モデルに追加します。

  2. x_estate.property モデルのフォームビューにフィールドを含めます。

注釈

Python モジュールとは異なり、計算されたフィールドに対して逆または検索メソッドを定義することはできません。

コードとビジネス ロジック。

サーバーアクション

Pythonモジュールでは、モデル上の任意のメソッドを自由に定義できます。 一般的な使用パターンの1つは、いわゆる"actions"メソッドをモデルに追加し、これらのメソッドをUIのボタンにバインドすることです(e. を選択し、見積書を確認し、請求書を投稿するなどします。

In a data module, you can achieve the same effect by defining Server Actions bound to your model. Server actions represent pieces of logic that are run dynamically on the server. These actions can be configured manually in the database directly via the Settings ‣ Technical ‣ Actions ‣ Server Actions menu and can be of different types; in our case, we will use the code type which allows us to run any Python code in a sandboxed environment.

この環境には、Odoo データベースとやり取りするのに役立つユーティリティがいくつか含まれています:

  • env: レコードの環境

  • model: レコードのモデル

  • useruid : 現在のユーザーと id

  • datetime, dateutil, timezone, timezone: 日付/時刻の計算に役立つライブラリ

  • float_compare: 2 つのフロート値と与えられた精度を比較するユーティリティ関数

  • b64encodeb64decode : base64 の値をエンコードおよびデコードするユーティリティ関数

  • Command: 複雑な式とコマンドを構築するのに役立つユーティリティクラス ( :ref:`ORMリファレンス <reference/fields/relational> `の`Command`クラスを参照)

加えて、 には、アクションが実行されるレコードセットへのアクセス権があります (通常はフォームビューからアクションが実行されると単一のレコードです)。 そして、アクションがリストビューから実行された場合に複数のレコードが recordrecords 変数を介して実行されます。

注釈

アクションがクライアントにアクションを返す必要がある場合(例えば、ユーザーを別のビューにリダイレクトするなど) サーバーアクションのコード内の action 変数に割り当てることができます。 コードの Sandbox は実行後にコード内で定義された変数を検査し、action 変数の存在を検出すると自動的に返します。

website モジュールがインストールされている場合 request オブジェクトは、コードの Sandbox で使用できます。response オブジェクトを response 変数に割り当てると、同様の方法でクライアントにレスポンスを返します。 これは、 :ref:の tutorials/importable_modules/website_controllers セクションで詳しく説明されています。

例えば、x_estate.property モデルでは、すべてのオファーの x_status フィールドを Refused に設定するアクションを定義できます。

<record id="action_x_estate_property_refuse_all_offers" model="ir.actions.server">
    <field name="name">Refuse all offers</field>
    <field name="model_id" ref="estate.model_real_estate_property"/>
    <field name="state">code</field>
    <field name="code"><![CDATA[
for property in records:
    property.x_offer_ids.write({'x_status': 'refused'})
    ]]></field>
</record>

このアクションを x_estate のフォームビューにボタンとして含めること。 roperty モデルでは、フォームビューのヘッダーに次の button ノードを追加できます。

<!-- form view definition from your code... -->
<header>
    <button name="estate.action_x_estate_property_refuse_all_offers" type="action" string="Refuse all offers"/>
</header>

この操作を実行するためにギアアイコン()にエントリを追加することもできます。 をクリックして、既に混雑しているビューにボタンを追加しないようにします。 これを行うには、サーバーアクションをモデルや特定の種類のビューに*バインド*することができます。

<record id="action_x_estate_property_refuse_all_offers" model="ir.actions.server">
    <field name="name">Refuse all offers</field>
    <field name="model_id" ref="estate.model_real_estate_property"/>
    <field name="state">code</field>
    <field name="binding_model_id" ref="estate.model_real_estate_property"/>
    <field name="binding_view_types">tree,form</field>
    <field name="code"><![CDATA[
for property in records:
    property.x_offer_ids.write({'x_status': 'refused'})
    ]]></field>
</record>

これにより、x_estateのギアアイコン(:icon:`fa-gear`)でアクションを利用できるようになります。 roperty モデル、リスト内(チェックボックスを介して1つまたは複数のレコードが選択されている場合)とフォームビュー。

Exercise

  1. x_estate.property にサーバーアクションを追加します。 オファーの ``x_status フィールドを Accepted に設定し、オファーがそれに応じてアタッチされるプロパティの販売価格と購入者を更新するffer`` モデル。 このアクションは Refused と同じプロパティにある他のすべてのオファーをマークする必要があります。

  2. この操作を実行できるオファーのリストビューにボタンを含める

../../_images/offer_accept_button.png

Pythonモデルをオーバーライド

UI 要素経由で

Python モジュールとは異なり、Python モデルのメソッドをきれいにオーバーライドすることはできません。

ただし、 場合によっては、これらのメソッドを呼び出すUIの要素を置き換え、サーバーアクション内でこれらのメソッドへの呼び出しを傍受することが可能です。

典型的な例として、Odoo の Sales アプリとの統合が挙げられます。 特定の製品が販売されるときに、不動産モジュールが販売アプリケーションと統合されていることを想像してみましょう(e. 、プロパティの販売を管理するための見積もり)、自動的にあなたのモジュールに新しいプロパティレコードを作成したいです。

これを達成するには、次のことが必要です:

  • ボタンの元のメソッドを呼び出し、メソッド呼び出しの前後にカスタムロジックを追加するサーバーアクションを作成します

  • ビュー内のボタンをサーバーアクションを呼び出すカスタムボタンで置き換えます

<record id="view_sale_order_form" model="ir.ui.view">
    <field name="name">sale.order.form.inherit.estate</field>
    <field name="model">sale.order</field>
    <field name="inherit_id" ref="sale.view_order_form" />
    <field name="arch" type="xml">
        <xpath expr="//button[@name='action_confirm'][@type='object']" position="attributes">
            <attribute name="type">action</attribute>
            <attribute name="name">estate.action_x_estate_property_create_from_sale_order</attribute>
        </xpath>
        <!-- since the button is present twice in the original view, we need to replace it twice -->
        <xpath expr="//button[@name='action_confirm'][@type='object']" position="attributes">
            <attribute name="type">action</attribute>
            <attribute name="name">estate.action_x_estate_property_create_from_sale_order</attribute>
        </xpath>
    </field>
</record>

<record id="action_x_estate_property_create_from_sale_order" model="ir.actions.server">
    <field name="name">Confirm and create property from sale order</field>
    <field name="model_id" ref="sale.model_sale_order"/>
    <field name="state">code</field>
    <field name="code"><![CDATA[
for order in records:
    order.action_confirm()
    property_type = env['x_estate.property.type'].sudo().search([('x_name', '=', 'Other')], limit=1)
    property = env['x_estate.property'].sudo().create({
        'x_name': order.name,
        'x_expected_price': 0,
        'x_selling_price': 0,
        'x_sale_order_id': order.id,
        'x_property_type_id': property_type.id,
    })
    ]]></field>
</record>

自動化ルールによる

自動化ルールは、特定のトリガーに基づいてデータベース内のレコードに対して自動的にアクションを実行する方法です。 国家の変更やタグの追加などです 例えば、オファーが承認されたときにメールを送信するなどして、レコードのライフサイクルイベントに行動を結び付けるのに役立ちます。

オートメーションルールを使用して標準動作を拡張することは、UIベースのアプローチよりも堅牢になります。これは、ライフサイクルイベントがボタン経由で発生した場合にも実行されるためです(例えば。 Webhookまたはメソッドへの直接呼び出しを介して; 例えば、見積もりがポータルや電子商取引を介して確認された場合)。 しかし、彼らは適切に設定するにはもう少し細かいです。 自動化を確実に実行する必要があります特定のフィールドを監視するなどによって 適切な時期にのみ実行されます

ドキュメント: このトピックに関連するより完全なドキュメントは、 自動化規則 にあります。

注釈

自動化ルールは base モジュールの一部ではありません。base_automation モジュールが付属しています。 データモジュールでオートメーションルールを定義する場合は、base_automation がモジュールの依存関係の一部であることを確認する必要があります。

インストールすると、オートメーションルールは Settings -‣ Technical ‣ Automations ‣ Automation Rules メニューから管理されます。

自動化ルールは、データモジュールと既存の標準的なOdooモジュールを結び付ける場合に特に便利です。 データモジュールはメソッドをオーバーライドできないため、自動化を標準モデルのライフサイクル変更と結び付けることは、標準モジュールを拡張する一般的な方法です。

オートメーションルールを使用して前のセクションから例を書き直す場合、いくつかの変更が必要になります。

  • サーバーアクションはボタンの元のメソッドを呼び出さないでください (代わりに、ボタンの元のメソッドを呼び出してください)。 最初の方法で自動化ルールを解除する変更が起動します)

  • ビュー拡張機能は必要ありません

  • 適切なイベントでサーバーアクションをトリガーするために自動化ルールを定義する必要があります

<record id="action_x_estate_property_create_from_sale_order" model="ir.actions.server">
    <field name="name">Create property from sale order</field>
    <field name="model_id" ref="sale.model_sale_order"/>
    <field name="state">code</field>
    <field name="code"><![CDATA[
for order in records:
    property_type = env['x_estate.property.type'].sudo().search([('x_name', '=', 'Other')], limit=1)
    property = env['x_estate.property'].sudo().create({
        'x_name': order.name,
        'x_expected_price': 0,
        'x_selling_price': 0,
        'x_sale_order_id': order.id,
        'x_property_type_id': property_type.id,
    })
    ]]></field>
</record>

<record id="automation_rule_x_estate_property_create_from_sale_order" model="base.automation">
    <field name="name">Create property from sale order</field>
    <field name="model_id" ref="sale.model_sale_order"/>
    <field name="trigger">on_state_set</field>
    <field name="trg_selection_field_id" ref="sale.selection__sale_order__state__sale"/>
    <field name="trigger_field_ids" eval="[(4, ref('sale.field_sale_order__state'))]"/>
    <field name="action_server_ids" eval="[(4, ref('estate.action_x_estate_property_create_from_sale_order'))]"/>
</record>

XML ID から標準の Odoo モデル、フィールド、選択値などに注意してください。 Odoo インスタンス自体には、テクニカルメニュー内の適切なレコードに移動し、デバッグメニューの View Metadata メニューエントリを使用しています。 モデルの XML ID は単にモデル名で、アンダースコアに置き換えられ、 model_ (e. 、sale.model_sale_ordersales です。 sale モジュールで定義されている rder`` ); フィールドの XML ID は、モデル名にアンダースコアに置き換えられ、モデル名とフィールド名 (e. 、sale.field_sale_order__name は、 sale.order モデルの name フィールドの XML ID です。

ウェブサイトコントローラ

Odoo の HTTP コントローラは通常、モジュールの controllers ディレクトリで定義されます。 データモジュールでは、ウェブサイトモジュールがインストールされている場合、コントローラとして動作するサーバアクションを定義することができます。

ウェブサイトモジュールがインストールされているとき サーバアクションは ウェブサイトで利用可能 としてマークし、パスを与えることができます(URLの衝突を避けるために、フルパスは /website/action の前に常に追加されます) グローバルの request オブジェクトは、コードサーバーアクションのローカルスコープで利用可能になります。

request オブジェクトは、リクエストの本文にアクセスするためのいくつかのメソッドを提供します。

  • request.get_http_params(): クエリ文字列と本文に存在するフォームからキーと値のペアを抽出します(application/x-www-form-urlencodedmultipart/form-data の両方)。

  • request.get_json_data(): リクエストの本文からJSONデータを抽出します。

サーバーアクション内から値を返すことはできないため、リターンへの応答を定義することができます。 response-like object を response 変数に割り当てると、自動的にウェブサイトに戻ります。

URL /website/action/estate が呼び出されたときにプロパティのリストを返すシンプルなウェブサイトコントローラの例を以下に示します。

<record id="server_action_estate_list" model="ir.actions.server">
    <field name="name">Estate List Controller</field>
    <field name="model_id" ref="estate.model_real_estate_property" />
    <field name="website_published">True</field>
    <field name="website_path">estate</field>
    <field name="state">code</field>
    <field name="code"><![CDATA[
html = '<html><body><h1>Properties</h1><ul>'
for property in request.env['x_estate.property'].search([]):
    html += f'<li>{property.x_name}</li>'
html += '</ul></body></html>'
response = request.make_response(html)
    ]]></field>
</record>

request オブジェクトには、レスポンスオブジェクトの生成を促進するためにいくつかの便利なメソッドがあります。

  • request.render(template, qcontext=None, lazy=True, **kw) は xmlid を使用してQWeb テンプレートをレンダリングします。追加のキーワード引数は `werkzeugに転送されます。 esponse`オブジェクト (例えば、クッキー、ヘッダーなど)

  • request. 別の URL にリダイレクトする場合は edirect(location, code=303, local=True) を使用します; local 引数を使用して、リダイレクトをウェブサイトからの相対位置にするかどうかを指定します(デフォルトは True)。

  • `request.notfound()`は`werkzeug.HTTPException`例外を返し、ウェブサイトに404エラーを通知します。

  • `request.make_response(data, headers=None, cookies=None, status=200)`を手動で`werkzeug.Response`オブジェクトを作成します。`status`引数は返すHTTPステータスコードです(デフォルト: 200)。

  • request.make_json_response(data, headers=None, cookies=None, status=200) は JSON レスポンスを手動で作成します。データは `json を使用して json-serialized になります。 umps`ユーティリティ; これは、API呼び出しを介してサーバー間の通信を設定するのに便利です。

実装の詳細やその他(一般的ではない)メソッドについては、`odoo.http`モジュール内の`Request`オブジェクトの実装を参照してください。

セキュリティ上の懸念は開発者に委ねられていることに注意してください (通常はセキュリティルールを通して、またはレコードにアクセスするには sudo を使用しています)。

注釈

サーバーアクションの model_id フィールドで使用されるモデルは、このサーバーアクションを実行するための書き込み操作を公開ユーザーがアクセスできる必要があります。 さもなければサーバーアクションは403エラーを返します。 アクセスを避ける方法は、公開ユーザがアクセス可能なモデルにサーバーアクションをリンクすることです。 典型的な例は `ir. にサーバーのアクションをリンクすることです。 ilters`モデル

Exercise

JSON APIをモジュールに追加して、外部サービスが販売のためのプロパティのリストを取得できるようにします。

  1. モデルに x_api_published フィールドを追加し、プロパティをAPI上で公開するかどうかを制御します。

  2. 公開ユーザがモデルを読み書きできるようにアクセス権レコードを追加します

  3. 不可能なドメインを持つ書き込み操作のレコードルールを追加することで、公開ユーザーからの書き込みを防ぎます(例:[('id', '=', False)]

  4. x_api_published としてマークされたプロパティが公開ユーザーによって読み取れるようにレコードルールを追加します。

  5. URL `/website/action/estate`が呼び出された場合、JSON形式でプロパティのリストを返すサーバーアクションを追加します

JavaScriptの<unk>

インポート可能なモジュールにはPythonファイルを含めることはできませんが、JavaScriptファイルにそのような制限は存在しません。 JavaScriptファイルをインポート可能なモジュールに追加するのは、標準のOdooモジュールに追加するのとまったく同じです。

これは、インポート可能なモジュールが新しいフィールドコンポーネントやまったく新しいビューを含めることができることを意味します。

例として、簡単なツアーをEstate モジュールに追加しましょう。 ツアーはOdooの標準的なメカニズムであり、アプリケーションを通してユーザーを案内することによってオンボードユーザーに使用されます。

`static/src/js/tour.js`にこのファイルを追加することで、単一のステップを持つ非常に最小限のツアーを追加できます。

import { registry } from "@web/core/registry";


registry.category("web_tour.tours").add('estate_tour', {
    url: "/web",
    steps: () => [{
    trigger: '.o_app[data-menu-xmlid="estate.menu_root"]',
    content: 'Start selling your properties from this app!',
    }],
});

次に、マニフェストファイルに適切なバンドルにファイルを含める必要があります。

{
    "name": "Real Estate",
    # [...]
    "assets": {
        "web.assets_backend": [
            "estate/static/src/js/tour.js",
        ],
    },
}

ツアーが表示されるように、データ フォルダー内の新しい estate_tour.xml ファイルに xml レコードを追加する必要があります。

<record id="estate_tour" model="web_tour.tour">
    <field name="name">estate_tour</field>
    <field name="sequence">2</field>
    <field name="rainbow_man_message">Welcome! Happy exploring.</field>
</record>

注釈

通常のPythonモジュールとは異なり、glob展開はインポート可能なモジュールではサポートされていません。 モジュールに含めたいファイルをリストする必要があります