はじめに
地味だが重要——実運用のOdooではバイナリフィールドが至るところで働いています。ユーザーが契約書をアップロードしたり、製品の仕様書を添付したり、会社ロゴをレコードに保存したりするとき、背後には必ずバイナリ型のフィールドが存在します。どこにデータが保存されるのか、実際のファイル管理はどう仕組まれているのか、そしていつ別の型を選ぶべきかを理解することで、カスタムフォーム設計やモデル拡張の品質が大きく変わります。
本ガイドでは、バイナリフィールドが何を保持するか、attachmentモードが保存方式とパフォーマンスに与える影響、Odoo StudioやPythonでの作成方法、およびCRM・人事・在庫などの実務的な活用例を解説します。
Odooにおけるバイナリ(ファイル)フィールドとは?
OdooのORMでは、バイナリフィールド(fields.Binary)はファイルやドキュメント、画像などの生データを扱います。画面上ではファイルをアップロードするボタンとして表示され、アップロード後は同じ場所からワンクリックでダウンロードできます。
最も重要なのは“どこに保存されるか”です。近年のOdooはデフォルトでattachmentモードを採用しており、実際のファイルはir.attachmentテーブルとサーバのfilestoreに置かれ、モデルのカラムには添付レコードを指す参照(ID)だけが格納されます。これにより主要テーブルが膨らまず、ファイル管理をOdooの仕組みに任せられます。
Odoo Studioのフィールド選択ではバイナリは「File」と表示され、フォーム上ではシンプルなアップロード/ダウンロード操作で扱えます。画像専用にはfields.Imageという派生型があり、自動リサイズやサムネイル表示を行うため、画像用途はそちらを使うのが適切です(後述)。
Pythonモデルでの定義イメージ
from odoo import fields, models
class ResPartner(models.Model):
_inherit = 'res.partner'
x_signed_contract = fields.Binary(
string='Signed Contract',
attachment=True,
)
x_signed_contract_filename = fields.Char(
string='Signed Contract Filename',
)
ここで注目したいのがx_signed_contract_filenameのようなファイル名用の補助Charフィールドです。アップロードした元のファイル名を保存しておくことで、ユーザーがダウンロードしたときに意味のある名前で保存されます。これを省くとダウンロード時に一般的な名前になってしまうため、実務ではセットで用意するのが標準的です。
フィールドの仕組みについて
モデルにバイナリフィールドを追加すると、モジュールのインストールやアップグレード時にOdooが必要なカラムを自動で作成します。開発者側で手動のSQLを用意する必要はありません。
保存モードについて
バイナリフィールドのattachmentパラメータはファイルの実際の格納先を制御します。
- attachment=True(推奨):ファイル実体はir.attachmentに保存され、レコードはモデル名とIDで紐付けられます。モデルのカラムには参照情報のみが残るため、テーブルが軽く保たれ、Odooのfilestoreによる効率的なファイル管理が利用できます。
- attachment=False:base64化されたデータが直接モデルのカラムに入ります。テーブルサイズが急増し、クエリ全体の遅延を招くため、サムネイル程度以外では避けるべき設定です。
データ形式について
バイナリフィールドはbase64エンコード済みの文字列で入出力されます。ORMやXML-RPCで読み書きする際はbase64文字列を受け取り、書き込む場合も同様の形式が必要です。
実務では、書き込む前にエンコードし、読み出し後にデコードするワークフローを守ります。
import base64
# Writing a file to a Binary field
with open('document.pdf', 'rb') as f:
encoded = base64.b64encode(f.read()).decode('utf-8')
record.write({'x_signed_contract': encoded})
# Reading a file from a Binary field
raw_bytes = base64.b64decode(record.x_signed_contract)
設定可能な主な属性
バイナリフィールドでよく使う主要属性をまとめます。
- attachment: Boolean。ir.attachmentに保存するか(True)カラムに直接保存するか(False)を制御します。近年のデフォルトはTrueです。
- string: 画面に表示するラベル。
- required: レコード保存時に必須にするかどうか。
- compute: Pythonメソッドで動的に生成する場合に使います。たとえばPDFをオンザフライで生成する用途などです。
- store: computeと組み合わせて計算結果をDBに保存するかを制御します。
- groups: 特定ユーザーグループだけに表示・編集を許す制限。機密文書の扱いで重要です。
- copy: レコード複製時に値をコピーするかを制御。attachmentモードやOdooバージョンで既定値が変わります。
fields.Imageについて
fields.Imageはfields.Binaryの派生で、Odoo 13以降に導入されました。自動リサイズやプレビュー用サムネイルを生成し、フォームで画像プレビューを表示します。製品画像や社員写真、ロゴのような視覚コンテンツにはfields.Imageを使うと、不要に大きなアップロードを防ぎUXも向上します。
ビューでの見え方
フォーム上のバイナリは基本的にアップロード/ダウンロード用のコントロールとして表示されます。画像ならwidgetにimageを指定してサムネイル表示にすると便利です。一覧ビューでは全行のファイル本体を読み込むと通信コストが高くなるため、直接表示せずに添付の有無を示すアイコンやブール値を出すのが一般的です。
実務での活用例
以下では、業務でよく使われる具体例をいくつか紹介します。
CRM:顧客レコードに署名済みのNDAや契約書を格納
営業活動の中で、取引先やリードに紐づく署名済み文書を直接保存しておくと便利です。res.partnerやcrm.leadにバイナリを追加すれば、営業担当は画面上でワンクリックで契約書を参照できます。簡易的なドキュメント管理として十分機能し、別途システムを用意する手間を省けます。
人事(HR):従業員関連書類の保管
身分証明書、就業契約書、就労許可証、研修証明書など、HRが扱う個人情報はhr.employeeにバイナリで添付しておくのが一般的です。groups属性で閲覧権限を制限することで、HRマネージャーのみがファイルを確認できるようにし、プライバシーやコンプライアンス要件に対応します。
在庫(Inventory):製品仕様書や安全データシートの添付
技術資料やSDS(安全データシート)、品質証明書などをproduct.templateに保持すると、購買や倉庫担当が必要な情報にすぐアクセスできます。製造業や卸売業でよく依頼されるカスタマイズで、Odoo Studioやモジュールで簡単に追加できます。
営業(Sales):捺印や署名画像の管理
見積書や注文書に会社印や代表者の署名を自動で挿入したいケースがあります。res.companyにfields.Imageでロゴや印影を保持しておけば、QWebテンプレートから参照して印刷物に反映できます。大量の見積発送がある環境で手作業を減らし、未署名送信のミスを防げます。
会計(Accounting):経費明細に領収書を添付
経費精算では領収書のスキャンを添付することが必須のワークフローです。標準の経費機能なら添付機能が備わっていますが、カスタムモデルや外部連携を使う場合はバイナリフィールドを直接用いてレコードに保持し、承認フローに組み込むのが実用的です。
バイナリフィールドの作成方法とカスタマイズ
バイナリフィールドを追加する方法は主に3つ。技術レベルや運用要件に応じて選びます。
Odoo Studio(ノーコード)で追加する方法
Odoo Studioはローコードのカスタマイズツールで、開発知識が無くてもフィールド追加が可能です。手順は次の通り。
- メインメニューからOdoo Studioを起動。
- フィールドを追加したいフォームを開く。
- フィールドピッカーからFileをドラッグしてフォームに配置。
- プロパティパネルでラベルや表示条件を設定。
- 保存してStudioを閉じる。
Studioはx_studio_プレフィックス付きでフィールドを作成し、attachmentモードを自動で使います。DB側の設定は不要で、ビジネスユーザーが短時間でファイルアップロード機能を追加できる有用な手段です。
カスタムモジュール(Python)で定義する方法
複数環境で管理したりバージョン管理が必要な場合は、Pythonでモデルにフィールドを定義するのが標準的で推奨される手法です。
from odoo import fields, models
class HrEmployee(models.Model):
_inherit = 'hr.employee'
x_id_document = fields.Binary(
string='ID Document',
attachment=True,
groups='hr.group_hr_user',
)
x_id_document_filename = fields.Char(
string='ID Document Filename',
)
フィールド定義後はフォームビューにbinaryウィジェットを追加し、filename属性で補助のCharフィールドを指すようにします。モジュールのインストール/アップグレード時にOdooがDBカラムを自動生成するため、Odoo.shやオンプレ環境でも動作します。
XML-RPC APIで作成する方法
リモートの構成スクリプトやデプロイ用ノートブックから自動的にフィールドを作る場合にはXML-RPC経由で定義できます。
field_id = models.execute_kw(
ODOO_DB, uid, ODOO_API_KEY,
'ir.model.fields', 'create',
[{
'name': 'x_custom_document',
'field_description': 'Custom Document',
'model_id': model_id,
'ttype': 'binary',
'state': 'manual',
}]
)
stateにmanualを指定すると、そのフィールドがモジュール由来ではなく手動作成であることを示します。現行バージョンではAPI経由で作られたフィールドもデフォルトでattachmentモードになります。自動設定スクリプトやリモートデプロイに向く手法です。
運用上のベストプラクティス
1. 常にattachment=Trueを使うこと
特別な理由がない限り、ファイル実体をDBカラムに格納するのは避けてattachmentモードを使ってください。テーブル肥大化やクエリ遅延のリスクが高く、大きなファイルを扱う場合は事実上必須の設定です。近年のOdooではデフォルトがTrueなので、明示的に指定しなくてもOKな場合が多いですが、運用方針として徹底しましょう。
2. ファイル名用のCharフィールドをセットで用意すること
アップロードされた元のファイル名を保持するフィールド(_filename系)は必須級のUX改善です。無いとダウンロード時にgenericな名前になりユーザーの混乱を招きます。コード一行で済む小さな投資が利用感を大きく向上させます。
3. 画像はfields.Imageを使うこと
製品画像や社員写真、ロゴなど視覚データにはfields.Imageを選びましょう。自動でサイズ制限やサムネイル生成が働き、不要な大容量ファイルの保存を防げます。型を用途に合わせることは健全なデータ設計の基本です。
4. groupsでアクセス制御を設けること
従業員文書や契約書、財務関連ファイルなど機密性の高いデータはgroups属性で読み書きを制限してください。プライバシー保護や監査要件の遵守に直結する重要な設定です。
5. コード中でのbase64処理を厳密に扱うこと
プログラムで読み書きする際は必ずエンコード・デコードを明示的に行ってください。書き込み前はbase64.b64encode(file_bytes).decode('utf-8')、読み出し後はbase64.b64decode(field_value)を使うのが安全です。形式の想定違いは実運用で頻繁にバグを生みます。
よくある落とし穴
attachment=Falseを選ぶリスク
ファイルをDBカラムに直接入れるとPostgreSQLテーブルが急速に肥大化します。数十件のPDFでも数百MB単位で膨らみ、そのモデルの全クエリが遅くなるため非常に大きな問題です。一度こうしてしまうとattachmentモードへ移行するのは手間のかかるマイグレーション作業になります。
ファイル名フィールドを忘れること
補助のファイル名フィールドが無いと、ユーザーがファイルをダウンロードした際に意味のない名前になりがちです。ユーザー体験を整えるために必ず追加しておきましょう。
BinaryとImageを混同すること
画像に対して単純なBinaryを使うと自動リサイズやサムネイルが働かず、極端に大きな画像が保存されてフォーム表示が重くなります。逆にPDFなど非画像にImage型を使うと処理エラーが起きます。用途に合った型を選んでください。
一覧ビューに直接バイナリを含めること
リストビューにバイナリをそのまま表示すると、表示される各行のファイル本体を読み込むため大容量のデータ転送が発生します。多数レコードを扱う一覧では、添付の有無を示すブールやアイコンで代替するのが正しい設計です。
処理時にFalseチェックを忘れること
値がないバイナリはPythonではFalseが返ります。空文字や空バイトではありません。チェックなしでbase64デコードしようとするとTypeErrorで処理が落ちるため、常にif record.x_document: のように存在チェックしてから処理してください。
まとめ
バイナリフィールドは単純だが重要な要素です。ファイルやドキュメントをOdooの画面・添付システム・アクセス制御の下で自然に扱えるようにする役割を果たします。
身につけるべき習慣は明快です:attachmentモードを使う、ファイル名用のCharフィールドを添える、画像はfields.Imageで扱う、機密ファイルにはgroupsで制御する、そしてコードではbase64処理を慎重に行う。これらを守れば運用上のトラブルの多くは未然に防げます。
Odoo Studioでのフィールド追加、カスタムモジュールの実装、あるいはORMやXML-RPC経由での自動作成いずれの場合でも、バイナリの扱いを初期段階で正しく設計することで、より堅牢で管理しやすいOdoo環境が得られます。
Dasoloについて お気軽にご相談ください 貴社のOdoo導入・カスタマイズ・最適化を支援します。データモデル設計からファイル管理ワークフローの構築、モジュール開発まで、当社チームがサポートいたします。