イントロダクション
Odoo開発を始めたとき、必ずと言っていいほど出てくるテーマが「継承」です。OdooのORMは、モデル同士がフィールドを共有したり拡張したりして重複を避ける設計になっており、同じデータを何度も定義せずに再利用することを前提に組まれています。
継承されたフィールドはこの仕組みの中核です。あるモデルが別のモデルに定義されたフィールドを透過的に参照・公開できることで、データの一貫性と可読性が保たれます。仕組みを理解すれば、Odooのデータ構造がぐっと明快になります。
本ガイドでは、継承フィールドの定義、継承の3種類、それらがデータベース上でどう振る舞うか、そして実務でのカスタマイズでの使いどころをわかりやすく解説します。
Odooにおける「継承されたフィールド」とは?
OdooのORMでは、あるモデルが別モデルのフィールドにアクセスできるようになっている場合、そのフィールドを「継承されたフィールド」と見なします。既存のフィールドを再定義する代わりに、子モデルがそのまま使えるようになる設計です。
この仕組みがあるからこそ、データモデルはスリムで一貫性が保たれます。例えば顧客の〈name〉はパートナーに1箇所だけ存在し、営業オーダーや請求書、CRM画面は同じ値を参照します。
Odooの三種類の継承
Odooには用途ごとに異なる3種類の継承があり、それぞれフィールドの扱い方が違います。
1. クラシカル継承(Classical Inheritance)
最も一般的なパターンです。_inheritだけを使って既存モデルをそのまま拡張します。新しいデータベーステーブルは作られず、追加したフィールドは元のモデルのテーブルにそのまま置かれます。
標準モジュールにフィールドを追加する際の典型的な方法で、CRMを有効にするとres.partnerにCRM特有の項目が追加されるのもこの方式です。
2. プロトタイプ継承(Prototype Inheritance)
_inheritと_nameを両方使うパターンです。親モデルの構造をコピーして新しいモデルを作り、独自のテーブルを持ちます。親のフィールドは子モデルに複製され、子側での変更は親に影響しません。
日常的な小改造ではあまり使われませんが、既存構造をベースにまったく別のモデルを作りたいときに有効です。
3. デリゲーション継承(Delegation Inheritance)
_inheritsで定義する特徴的な方式です。子モデルが親モデルへのMany2oneリンクを持ち、そのリンクを経由して親の全フィールドを自分のもののように公開します。データは親のテーブルに保存されます。
厳密な意味で「継承されたフィールド」と呼ばれるのはこのケースです。フィールドが複製されるのではなく、リンクを辿って読み書きされます。
標準例としてはres.usersとres.partnerの関係があり、ユーザーは内部的にパートナーを参照して名前やメール、電話を扱います。
フィールドの働き方
デリゲーション継承の内部動作
_inheritsを使うと、ORMが2つのテーブルの間に透過的な橋を作ります。子レコードで継承フィールドを読めば、ORMが自動的にMany2oneをたどって親の値を返します。
子モデルからそのフィールドに書き込むと、ORMは親レコードに直接書き込みます。開発者から見るとフィールドはそのモデル固有のように見えますが、実際のデータは親テーブルにあります。
重要な結果として、データの重複が発生しません。たとえばパートナー名を更新すれば、そのパートナーを参照するすべての場所に即座に反映されます。
クラシカル継承とデータベース
クラシカル継承では追加フィールドは元のモデルのテーブルに追加され、別テーブルは作られません。データベース上では拡張後のモデルと元のモデルは同一テーブルを使うため、Odooカスタマイズで最もクリーンかつよく使われる方法です。
relatedフィールドは軽量な継承の代替
relatedフィールドは、リンクされたレコードの値を計算して読み出す特殊な算出フィールドです。モデル継承そのものではありませんが、モデル構造を変更せずに他モデルの値を表面化するためによく使われます。
例として、受注にpartner_country_idを定義してpartner_id.country_idを参照することで、受注に国情報を直接表示できます。値はパートナー由来です。
relatedフィールドはstore=Trueにしてデータベースに保存できます。検索やフィルタは速くなりますが、ストレージが増え、参照元の変更時に再計算が必要になります。
実行時のフィールド解決
Odooはモデル定義を読み込む際に、継承されたフィールドも含めた完全なフィールドマップを解決します。稼働準備が整う段階で、各フィールドがどのソース(自モデル、親テーブル、関連チェーン)にあるかがマッピングされ、起動時にキャッシュされます。
ビジネスでの活用例
継承フィールドは単なる技術仕様ではなく、データの一貫性を保つための実務上の解決策です。重複を書かずに複数箇所で同じ情報を参照できる点が利点です。
1. CRMと営業:連絡先情報の一元化
営業がCRMでリードを作るとき、顧客名や電話、メールはパートナーからMany2oneで引き出されます。パートナーの情報を更新すれば、紐づくリードや見積、受注すべてに即座に反映されます。
これはクラシカル継承とrelatedフィールドが連携して働く典型例で、CRMはres.partnerにCRM特有の項目を追加しつつ、受注側は関連フィールドで連絡先情報を見せます。
2. 製品とバリアント
製品モデルでは、バリアントを扱うためにデリゲーション継承を使います。product.product(バリアント)はproduct.templateへリンクし、テンプレート側に共通フィールド(名称、カテゴリー、販売価格、説明など)が置かれ、バリアント固有の項目は各バリアントに残ります。
この設計により、シャツの色違い50種類があっても名称や説明を50回保存する必要がありません。
3. ユーザーとパートナーの関係
Odooでは全ユーザーが連絡先でもあります。res.usersがres.partnerを継承することで、名前やメール、電話、住所、プロフィール画像などをパートナー側で一元管理できます。従業員のメールを更新すればユーザー設定と連絡先帳の両方に反映されます。
4. 在庫:入出庫と製品情報の表示
在庫のムーブ行は製品テンプレート経由で製品説明を表示します。倉庫担当者は最新の製品情報をそのままピッキングリストで確認でき、在庫モジュール側で製品データを重複して保持する必要がありません。
5. 会計:請求書行のデータ一貫性
仕訳行は製品やパートナーを参照します。請求書行に表示される製品名や勘定科目、税設定は製品・パートナーの設定から引き出されるため、会計担当者は営業側で設定した内容と一致するデータを参照できます。
継承フィールドの作成・カスタマイズ方法
Odoo Studioの活用
Odoo Studioはコードを書かずにモデルやビューをカスタマイズできるツールです。Studioでフィールドを追加すると、内部的にはクラシカル継承でモデル定義に新フィールドを加えます。
Studioではrelatedフィールドも作れます。フィールドタイプで「関連」を選び、現在のモデルからたどれる任意のフィールドを指定すれば、例えば受注に顧客の国やVAT番号を直接表示できます。
ビジネスユーザーやコンサルタントにとって、Studioはフィールド追加の安全で便利な手段です。フィールド命名やDB移行、ビューへの配置まで自動的に処理されます。
Pythonによる技術カスタマイズ
開発者がモジュールを作る際、継承フィールドはOdooのORMのmodels.Modelを使ってPythonで定義します。
既存モデルにカスタムフィールドを追加するにはクラシカル継承を使います。
(ここに対応するコード例を挿入して、具体的な定義方法を示します)
既存モデルのフィールド群を共有する新モデルを作るにはデリゲーション継承を使います。
(ここにデリゲーション継承のコード例を挿入して、親のMany2one定義などを示します)
relatedフィールドでリンク先の値を表面化する方法
(ここにrelatedフィールドのコード例を挿入して、relatedパスやstoreオプションの使い方を示します)
XML-RPCによる遠隔設定
フィールドはXML-RPC API経由でir.model.fieldsを操作してプログラム的に作成することもできます。Dasoloのリモート設定ノートブックがこの方法を使っています。これはStudioと同等の処理をAPI経由で行う手段です。
APIでrelatedフィールドを作るには、適切なタイプ(例:many2one)を選び、relatedパラメータで関係パスを定めたir.model.fieldsレコードを作成します。自動セットアップやデプロイに向いた方法です。
ベストプラクティス
用途に応じた継承タイプの選択
単純に既存モデルにフィールド・メソッドを追加したいだけならクラシカル継承が最良で簡単です。対して、連絡先を拡張した別概念のレコード群が必要な場合など、親子で独立したレコードセットが要るときはデリゲーション継承を検討します。
読み取り目的ならrelatedフィールドを優先
もしフォームやリストでリンク先の値を見せるだけなら、構造的な依存を増やさないrelatedフィールドのほうが分かりやすく保守もしやすいです。
relatedフィールドのstore=Trueには注意
保存しておくと検索やソートが高速になりますが、あくまでコピーになるためソースが変わったときの再計算や同期問題が発生する可能性があります。大規模にフィルタやグループ化をしたい場合のみ使うのが賢明です。
カスタムフィールドは必ずx_で始めるか名前空間を使う
標準モデルに追加するフィールドは将来の衝突を避けるためにx_接頭辞か適切なモジュール命名規約を使いましょう。
継承チェーンをドキュメント化する
複雑なカスタマイズでは、フィールドがどこから来ているかを短いコメントや設計ノートに残してください。受注のx_country_codeが実はpartner_id.country_id.codeを参照している、という推測だけでは後から理解できないことがあります。
カスケード削除の扱いを明確にする
デリゲーション継承では、子レコード作成時に親が自動で生成されます。子削除時に親を残すべきか削除するべきか設計で決め、通常はMany2oneにondelete='cascade'を設定して孤立レコードを防ぎます。
よくある落とし穴
継承の種類を混同すること
初学者が陥りやすいミスは、意図せず_inheritと_nameを両方書いて新モデルを作ってしまうことです。既存モデルを拡張したいだけなら_nameは書かないでください。
_inheritsで親レコードを作成し忘れること
デリゲーション継承では、APIやスクリプトで子を作る際に親が自動作成されない場合があります。通常のORMのcreateを使えば親も作られますが、手順を踏まないと制約エラーになります。
フィールド型を継承で変更しようとすること
既存フィールドの型を継承で上書きすることはできません。ラベルやヘルプは変えられても、例としてCharをIntegerに変えることは許されず、モジュールインストールでエラーになります。
storedなrelatedフィールドの乱発
パフォーマンス目的で関連フィールドをすべて保存してしまうと、DBサイズが膨らみ、頻繁に値が変わる場合は再計算負荷が増えます。本当にフィルタや集計で必要なケースに限定してください。
継承フィールドが子モデルのアクセス制御を自動適用すると思い込むこと
デリゲーションで継承されたフィールドは親モデルに保存されます。子モデル側でアクセス権を制限しても、親側の権限設定が別なら直接親を通じて値を読めてしまう可能性があります。デリゲーションを使う際は両方のセキュリティルールを確認してください。
まとめ
継承フィールドは特殊機能ではなくOdooの基本設計です。ユーザーとパートナー、テンプレートとバリアント、受注と顧客情報など、多くの相互関係は継承によってデータの整合性を保っています。
どの継承を使うべきか、いつrelatedを選ぶべきか、そしてそれらがDB上でどう振る舞うかを理解すれば、Odooカスタマイズの効率は大きく上がります。よりクリーンなコードと設計が可能になり、想定外のフィールド挙動でデバッグに時間を取られることが減ります。
要点はシンプルです:OdooのORMはデータを一箇所に置き、複数箇所から参照するよう設計されています。これが継承フィールドの原理であり、多数のモジュールにまたがる大規模なシステムでもデータ整合性を保つ理由です。
Odoo導入でお手伝いが必要ですか?
Dasoloでは、企業のOdoo導入・カスタマイズ・最適化を支援しています。カスタムモジュールの開発、標準モデルへのフィールド追加、あるいはデータモデルの挙動に関するトラブルシュートなど、実務に裏打ちされた支援を提供します。
Odooのデータモデルやカスタムフィールドの方針、技術的な実装についてご相談があれば、状況を整理し最適なアプローチをご提案します。 お問い合わせはこちらから 一緒に最適な実装方針を検討しましょう。