モジュールの構築

危険

このチュートリアルは古くなっています。代わりに サーバーフレームワーク101 を読むことをお勧めします。

警告

This tutorial requires having installed Odoo

Odoo サーバーの起動/停止

Odooはクライアント/サーバーアーキテクチャを使用し、クライアントがRPC経由でOdooサーバーにアクセスするWebブラウザです。

ビジネスロジックと拡張は、クライアント機能をサポートするものの、一般的にサーバー側で実行されます。 をクリックします。新しいデータ表現(対話マップなど)をクライアントに追加できます。

サーバーを起動するには、シェルで odoo-bin を呼び出します。 必要に応じてファイルにフルパスを追加します:

odoo-bin

ターミナルから Ctrl-C を2回押すか、対応する OS プロセスを停止します。

Odoo モジュールを作成

サーバー拡張とクライアント拡張の両方は modules としてパッケージされており、オプションで database にロードされます。

Odoo モジュールは、Odoo システムにまったく新しいビジネスロジックを追加することができます。 または既存のビジネスロジックを変更して拡張することもできます。モジュールを作成して、あなたの国の会計ルールをOdooの一般的な会計サポートに追加することもできます。 次のモジュールはバスフリートのリアルタイム可視化をサポートします

したがって、Odoo のすべてがモジュールで起動し、終了します。

モジュールの構造

Odoo モジュールには、いくつかの要素を含めることができます。

ビジネス オブジェクト

Python クラスとして宣言されました。これらのリソースは設定に基づいて自動的に Odoo によって保持されます。

Object views

ビジネス オブジェクト UI 表示の定義

データファイル

モデルメタデータを宣言する XML または CSV ファイル:

ウェブコントローラ

Webブラウザからのリクエストを処理します。

静的Webデータ

画像、CSS、またはJavaScriptファイルは、WebインターフェースまたはWebサイトで使用されます

モジュール構造

各モジュールは モジュールディレクトリ というディレクトリにあります。モジュールディレクトリは、 --addons-path オプションを使用して指定します。

ちなみに

ほとんどのコマンドラインオプションは、 :ref:`設定ファイル <reference/cmdline/config> ` を使用して設定することもできます。

Odoo モジュールは、 マニフェスト で宣言されます。

A module is also a Python package with a __init__.py file, containing import instructions for various Python files in the module.

例えば、モジュールに単一の mymodule.py ファイルがある場合、__init__.py`` が含まれている可能性があります。

from . import mymodule

Odooは新しいモジュールの設定を支援するメカニズムを提供します。 odoo-bin には空のモジュールを作成するためのサブコマンド scaffold があります。

$ odoo-bin scaffold <module name> <where to put it>

このコマンドは、モジュールのサブディレクトリを作成し、自動的にモジュールの標準ファイルの束を作成します。 それらのほとんどは単にコメントされたコードまたはXMLを含んでいます。これらのファイルのほとんどはこのチュートリアルで説明されます。

Exercise

モジュールの作成

空のモジュールを作成するには、上記のコマンドラインを使用してください Open Academy, そしてそれをOdooにインストールします。

オブジェクト-リレーショナルマッピング

A key component of Odoo is the ORM layer. This layer avoids having to write most SQL by hand and provides extensibility and security services2.

ビジネス オブジェクトは Model を拡張した Python クラスとして宣言され、それらを自動化された持続性システムに統合します。

モデルは、その定義に属性の数を設定することで構成できます。最も重要な属性は _name は必要で、Odooシステムのモデル名を定義します。モデルの最小限の完全な定義は次のとおりです。

from odoo import models
class MinimalModel(models.Model):
    _name = 'test.model'

モデルフィールド

フィールドは、モデルが格納できるものと場所を定義するために使用されます。フィールドは、モデルクラスの属性として定義されます:

from odoo import models, fields

class LessMinimalModel(models.Model):
    _name = 'test.model2'

    name = fields.Char()

一般ステータス

モデル自体と同様に、設定属性をパラメータとして渡すことでフィールドを設定できます:

name = fields.Char(required=True)

すべてのフィールドで利用可能な属性がいくつかあります。最も一般的な属性は次のとおりです。

string (unicode, default: field's name)

ユーザーが表示するUI内のフィールドのラベル。

required (bool, default: False)

True の場合、フィールドを空にすることはできません。デフォルト値を持つか、レコードを作成する際に常に値を与える必要があります。

help (unicode, default: ')

ロングフォームは、UI内のユーザーにヘルプツールチップを提供します。

index (bool, default: False)

Odoo が列に Database index を作成するよう要求します。

単純フィールド

フィールドには、モデルのテーブルに直接格納される単純なフィールドと、(同じモデルまたは異なるモデルの)レコードをリンクする「関連」フィールドの2つの幅広いカテゴリがあります。

単純なフィールドの例は、 BooleanDate、 :class:`~odoo.fields.Char`です。

予約済みフィールド

Odoo は 1 すべてのモデルにいくつかのフィールドを作成します。 これらのフィールドはシステムによって管理されており、書き込まれるべきではありません。これらは有用または必要に応じて読み込むことができます。

id (Id )

モデル内のレコードの一意の識別子

create_date (Datetime )

レコードの作成日

create_uid (Many2one )

レコードを作成したユーザー

write_date (Datetime )

レコードの最終更新日。

write_uid (Many2one )

最後に記録を更新したユーザー

特殊フィールド

デフォルトでは、Odoo はすべてのモデルに name フィールドを使用して様々な表示や検索を行う必要があります。 これらの目的で使用されるフィールドは、 _rec_name に設定することで上書きできます。

Exercise

モデルを定義

*openacademy*モジュールで新しいデータモデル*コース*を定義します。コースにはタイトルと説明があります。コースにはタイトルが必要です。

データファイル

Odooは高度なデータ駆動システムです。 モジュールの値の一部であるPython_コードを使用して動作をカスタマイズしますが、ロードされたときに設定するデータがあります。

ちなみに

一部のモジュールは、データを Odoo に追加するためだけに存在します

Module data is declared via data files, XML files with <record> elements. Each <record> element creates or updates a database record.

<odoo>

        <record model="{model name}" id="{record identifier}">
            <field name="{a field name}">{a value}</field>
        </record>

</odoo>
  • model は、レコードの Odoo モデルの名前です。

  • id外部識別子 で、(データベース内の識別子を知らなくても) レコードを参照することができます。

  • <field> 要素は、モデル内のフィールドの名前である name を持っています(例: description )。その本体はフィールドの値です。

ロードするマニフェストファイルでデータファイルを宣言する必要があります。 これらは 'data' リスト(常にロードされています)または 'demo' リスト(デモモードでのみロードされます)で宣言できます。

Exercise

デモデータを定義

いくつかのデモコースで*コース*モデルを埋めるデモデータを作成します。

ちなみに

データ ファイルの内容は、モジュールがインストールまたは更新されたときにのみ読み込まれます。

変更を加えた後は、データベースへの変更を保存するために、 odoo-bin -u openacadame を使用することを忘れないでください。

アクションとメニュー

アクションとメニューはデータベース内の通常のレコードで、通常はデータファイルを通じて宣言されます。アクションは以下の3つの方法でトリガーできます:

  1. メニューアイテムをクリックすることで(特定のアクションにリンク)

  2. ビュー内のボタンをクリックすることで(アクションに接続されている場合)

  3. オブジェクトに対するコンテキストアクションとして

メニューは宣言が複雑なため、ir を宣言するための <menuitem> ショートカットがあります。 i.menu`` と対応するアクションに簡単に接続します。

<record model="ir.actions.act_window" id="action_list_ideas">
    <field name="name">Ideas</field>
    <field name="res_model">idea.idea</field>
    <field name="view_mode">list,form</field>
</record>
<menuitem id="menu_ideas" parent="menu_root" name="Ideas" sequence="10"
          action="action_list_ideas"/>

危険

アクションは、 XML ファイル内の対応するメニューの前に宣言される必要があります。

データファイルは順番に実行され、メニューを作成する前にアクションの id がデータベースに存在する必要があります。

Exercise

新しいメニュー項目を定義

OpenAcademyのメニューエントリの下でコースにアクセスするための新しいメニューエントリを定義します。ユーザーは次のことができます:

  • すべてのコースのリストを表示する

  • コースの作成/変更

基本ビュー

format@@0では、モデルのレコードの表示方法を定義します。 各ビューは可視化モードを表しています (レコードのリスト、その集計のグラフ、 …)。 ビューはタイプ(e.)から一般的に要求することができます。 をクリックします。 一般的なリクエストの場合は 正しい型と優先度が最も低いビューが使用されます (したがって、各型の最も低い優先度のビューは、その型のデフォルトのビューです)。

View inherited from は、他の場所で宣言されたビューを変更することができます(コンテンツの追加や削除)。

一般的なビューの宣言

ビューはモデルの ir.ui.view のレコードとして宣言されます。ビュータイプは arch フィールドのルート要素によって暗示されます。

<record model="ir.ui.view" id="view_id">
    <field name="name">view.name</field>
    <field name="model">object_name</field>
    <field name="priority" eval="16"/>
    <field name="arch" type="xml">
        <!-- view content: <form>, <list>, <graph>, ... -->
    </field>
</record>

危険

ビューのコンテンツは XML です。

したがって、arch フィールドは type="xml" として正しく解析される必要があります。

リストビュー

リストビュー、リストビューとも呼ばれます。表形式でレコードを表示します。

Their root element is <list>. The simplest form of the list view simply lists all the fields to display in the table (each field as a column):

<list string="Idea list">
    <field name="name"/>
    <field name="inventor_id"/>
</list>

フォームビュー

フォームは、単一のレコードの作成と編集に使用されます。

ルート要素は <form> です。これらは高レベルの構造要素 (グループ、ノートブック) とインタラクティブ要素 (ボタンとフィールド) で構成されています。

<form string="Idea form">
    <group colspan="4">
        <group colspan="2" col="2">
            <separator string="General stuff" colspan="2"/>
            <field name="name"/>
            <field name="inventor_id"/>
        </group>

        <group colspan="2" col="2">
            <separator string="Dates" colspan="2"/>
            <field name="active"/>
            <field name="invent_date" readonly="1"/>
        </group>

        <notebook colspan="4">
            <page string="Description">
                <field name="description" nolabel="1"/>
            </page>
        </notebook>

        <field name="state"/>
    </group>
</form>

Exercise

XMLを使用してフォームビューをカスタマイズ

コースオブジェクトに独自のフォームビューを作成します。表示されるデータは、コースの名前と説明です。

Exercise

ノート

コースフォームビューで、説明フィールドをタブの下に置きます。 追加情報を含む他のタブを後で追加する方が簡単になります。

フォームビューでは、より柔軟なレイアウトにプレーンHTMLを使用することもできます。

<form string="Idea Form">
    <header>
        <button string="Confirm" type="object" name="action_confirm"
                invisible="state != 'draft'" class="oe_highlight" />
        <button string="Mark as done" type="object" name="action_done"
                invisible="state != 'confirmed'" class="oe_highlight"/>
        <button string="Reset to draft" type="object" name="action_draft"
                invisible="state not in ['confirmed', 'done']" />
        <field name="state" widget="statusbar"/>
    </header>
    <sheet>
        <div class="oe_title">
            <label for="name" class="oe_edit_only" string="Idea Name" />
            <h1><field name="name" /></h1>
        </div>
        <separator string="General" colspan="2" />
        <group colspan="2" col="2">
            <field name="description" placeholder="Idea description..." />
        </group>
    </sheet>
</form>

ビューを検索

検索ビューは、リストビュー(およびその他の集計ビュー)に関連付けられた検索フィールドをカスタマイズします。 ルート要素は <search> で、どのフィールドを検索できるかを定義するフィールドで構成されています。

<search>
    <field name="name"/>
    <field name="inventor_id"/>
</search>

モデルの検索ビューが存在しない場合、Odoo は name フィールドのみを検索できるビューを生成します。

Exercise

コースを検索

タイトルや説明に基づいてコースを検索できるようにします。

モデル間の関係

モデルからのレコードは、別のモデルからのレコードに関連している可能性があります。 例えば、販売注文レコードは、クライアントデータを含むクライアントレコードに関連しています。 売り注文書の記録にも関わっています

Exercise

セッション モデルを作成

モジュールオープンアカデミー用。 我々は、*セッション*のモデルを考えます: セッションは、特定の視聴者に与えられた時間に教えられるコースの発生です。

sessions のモデルを作成します。セッションには名前、開始日、継続時間、および多数の座席があります。 アクションとメニューアイテムを追加して表示します。メニューアイテムを介して新しいモデルを表示します。

リレーショナルフィールド

リレーショナルフィールドは、同じモデル (hierarchies) または異なるモデルのいずれかのレコードをリンクします。

リレーショナルフィールドタイプは次のとおりです。

Many2one(other_model, ondelete='set null')

他のオブジェクトへのシンプルなリンク:

print(foo.other_id.name)

関連項目

外部キー

One2many(other_model, related_field)

仮想リレーション、 Many2one の逆。 :class:`~odoo.fields。 ne2many`は、レコードのコンテナとして動作します。アクセスすると(おそらく空の場合)レコードのセットになります:

for other in foo.other_ids:
    print(other.name)

危険

Because a One2many is a virtual relationship, there must be a Many2one field in the other_model, and its name must be related_field

Many2many(other_model)

双方向多重関係、一方の側の任意のレコードは、反対側の任意の数のレコードに関連付けることができます。 レコードのコンテナとして動作します。これにアクセスすると、レコードの空のセットが生じる可能性があります:

for other in foo.other_ids:
    print(other.name)

Exercise

Many2one リレーションシップ

many21oneを使用して、*Course*および*Session*モデルを他のモデルとの関係を反映させるように変更します。

  • コースには responsible ユーザがいます。そのフィールドの値は組み込みモデル res.users のレコードです。

  • セッションには instructor があります。そのフィールドの値は組み込みモデル res.partner のレコードです。

  • セッションは course に関連しています。そのフィールドの値はモデルの openacemy.course のレコードであり、必須です。

  • ビューを適応します。

Exercise

逆1-2多数の関係

逆リレーショナルフィールドone2multiを使用して、コースとセッションの関係を反映するようにモデルを変更します。

Exercise

多数多数の関係

リレーショナルフィールドmany2multiを使用して、Session モデルを変更して、すべてのセッションを*参加者*のセットに関連付けます。 参加者はパートナーレコードで表されるため、組み込みモデル res.partner に関連します。それに応じてビューを適応します。

継承

型番の継承

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

最初の継承メカニズムにより、別のモジュールで定義されているモデルの動作をモジュールが変更できます。

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

  • モデルのフィールドの定義を上書きする

  • モデルに制約を追加する

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

  • モデルの既存のメソッドを上書きします。

2 番目の継承メカニズム(委任)により、モデルのすべてのレコードを親モデルのレコードにリンクすることができます。 をクリックすると、親レコードのフィールドへの透過的なアクセスが可能になります。

../../_images/inheritance_methods.png

関連項目

  • :attr:`~odoo.models.Model._inherited from

  • :attr:`~odoo.models.Model._inherited from

継承を表示

既存のビューを変更する代わりに(上書きすることによって) Odoo は、子の「拡張」ビューがルートビューの上に適用されるビュー継承を提供し、親からコンテンツを追加または削除できます。

拡張ビューは親を参照するために inherited from id フィールドを使用します。 そして、単一ビューの代わりに、その``arch`` フィールドは、親ビューの内容を選択して変更する任意の数の xpath 要素で構成されます。

<!-- improved idea categories list -->
<record id="idea_category_list2" model="ir.ui.view">
    <field name="name">id.category.list2</field>
    <field name="model">idea.category</field>
    <field name="inherit_id" ref="id_category_list"/>
    <field name="arch" type="xml">
        <!-- find field description and add the field
             idea_ids after it -->
        <xpath expr="//field[@name='description']" position="after">
          <field name="idea_ids" string="Number of ideas"/>
        </xpath>
    </field>
</record>
expr

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

position

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

inside

一致する要素の末尾に xpath の本体を追加します

replace

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

before

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

after

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

attributes

xpath のボディ内の特別な 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

既存のコンテンツを変更する

  • モデルの継承を使用して、既存の Partner モデルを変更して instructor boolean 項目を追加し、session-partner リレーションに対応する many2lot 項目を追加します。

  • ビューの継承を使用して、パートナーフォームビューにこのフィールドを表示します

ドメイン

Odooでは、 ドメインを検索 はレコードの条件をエンコードする値です。 ドメインは、モデルのレコードのサブセットを選択するために使用される基準のリストです。 各基準は、項目名、演算子、値を持つトリプルです。

例えば、*Product*モデルで使用する場合、以下のドメインは単価*1000*以上の*サービス*をすべて選択します。

[('product_type', '=', 'service'), ('unit_price', '>', 1000)]

デフォルトでは、条件は暗黙の AND と組み合わされます。 論理演算子 & (AND)、| (OR) および ! (NOT) は、明示的な条件を組み合わせるために使用できます。 これらはプレフィックス位置で使用されます (演算子は引数の前に挿入されます)。 例えば、「サービス*OR*である製品は1000から2000の間で*NOT*単価を持っています」を選択する場合は、:

['|',
    ('product_type', '=', 'service'),
    '!', '&',
        ('unit_price', '>=', 1000),
        ('unit_price', '<', 2000)]

domain パラメータをリレーショナルフィールドに追加することで、クライアントインターフェースでレコードを選択しようとする際にリレーションの有効なレコードを制限できます。

Exercise

リレーショナル分野のドメイン

Session のインストラクターを選択する場合、インストラクター(instructorTrue に設定したパートナー)のみが表示されます。

Exercise

より複雑なドメイン

新しいパートナーカテゴリ*先生/レベル1*と*先生/レベル2*を作成します。 セッションの講師は、講師または先生(任意のレベルの)のいずれかにすることができます。

計算されたフィールドとデフォルト値

これまでのところ、フィールドは直接データベースに格納され、直接データベースから取得されています。フィールドは computed にすることもできます。 その場合、フィールドの値はデータベースから取得されず、モデルのメソッドを呼び出して即座に計算されます。

計算フィールドを作成するには、フィールドを作成し、その属性 compute にメソッドの名前を設定します。 計算方法は、単純に self の各レコードで計算するフィールドの値を設定する必要があります。

危険

self はコレクションです

オブジェクト selfrecordset です。つまり、レコードの順序付きコレクションです。 len(self)iter(self) などのコレクションに対する標準的な Python 操作と、 recs1 + recs2 などの追加設定操作をサポートします。

self を繰り返すと、各レコードはサイズ1のコレクションになります。 record.name のようにドット表記を使用することで、単一のレコードのフィールドにアクセス/割り当てることができます。

import random
from odoo import models, fields, api

class ComputedModel(models.Model):
    _name = 'test.computed'

    name = fields.Char(compute='_compute_name')

    def _compute_name(self):
        for record in self:
            record.name = str(random.randint(1, 1e6))

依存関係

計算されたフィールドの値は通常、計算されたレコードの他のフィールドの値によって異なります。 ORMは、ディベロッパーがデコレーター depends() 指定された依存関係はORMによって依存関係の一部が変更されたときにフィールドの再計算をトリガーするために使用されます:

from odoo import models, fields, api

class ComputedModel(models.Model):
    _name = 'test.computed'

    name = fields.Char(compute='_compute_name')
    value = fields.Integer()

    @api.depends('value')
    def _compute_name(self):
        for record in self:
            record.name = "Record with value %s" % record.value

Exercise

計算されたフィールド

  • Session モデルにテイクシートのパーセンテージを追加します

  • リストとフォームビューにそのフィールドを表示します

  • フィールドをプログレスバーとして表示

デフォルト値

任意のフィールドにデフォルト値を与えることができます。 フィールド定義で、オプション default=X を追加します。ここで X は Python リテラル値 (boolean) のいずれかです。 integer, float, string), recordset を取得して値を返す関数:

name = fields.Char(default="Unknown")
user_id = fields.Many2one('res.users', default=lambda self: self.env.user)

注釈

オブジェクト self.env は、リクエストパラメータやその他の有用なものへのアクセスを提供します。

  • self.env.cr または self._cr はデータベースの cursor オブジェクトです。データベースへの問い合わせに使用されます。

  • self.env.uid または self._uid は現在のユーザーのデータベースIDです

  • self.env.user は現在のユーザーのレコードです

  • self.env.context または self._context はコンテキスト辞書です

  • self.env.ref(xml_id) は XML id に対応するレコードを返します

  • self.env[model_name] は与えられたモデルのインスタンスを返します。

Exercise

アクティブなオブジェクト – デフォルト値

  • start_date のデフォルト値を今日に定義します ( Date を参照)。

  • クラスセッションに active フィールドを追加し、デフォルトでセッションをアクティブに設定します。

Onchange

"onchange" メカニズムは、ユーザーがフィールドに値を入力するたびにフォームを更新するためのクライアントインターフェイスを提供します。 データベースに何も保存せずに

例えば、モデルが amountunit_priceprice の3つのフィールドを持つとします。 を選択し、他の項目が変更されたときにフォームの価格を更新する必要があります。 これを実現するために、self がフォームビューでレコードを表し、 onchange()self を変更すると、フォームに反映されます。

<!-- content of form view -->
<field name="amount"/>
<field name="unit_price"/>
<field name="price" readonly="1"/>
# onchange handler
@api.onchange('amount', 'unit_price')
def _onchange_price(self):
    # set auto-changing field
    self.price = self.amount * self.unit_price
    # Can optionally return a warning and domains
    return {
        'warning': {
            'title': "Something bad happened",
            'message': "It was very bad indeed",
        }
    }

計算されたフィールドでは、onchange の値が組み込まれており、Session フォームでプレイすることで見ることができます。シートまたは参加者の数を変更します。 そして taken_seats プログレスバーが自動的に更新されます。

Exercise

警告

無効な値について警告する明示的な onchange を追加します。例えば、座席数よりも多くの参加者です。

モデルの制約

Odoo では、自動的に検証された不変数を設定する方法を 2 つ提供しています: Python constraints と :attr:`SQL constraints <odoo.models.Model._sql_constraints> ` 。

Python制約は、 constraints() で飾られたメソッドと定義され、レコードセットで呼び出されます。 デコレータは、拘束に関与するフィールドを指定します。これにより、拘束のいずれかが変更されたときに自動的に評価されます。 このメソッドは、不変量が満たされていない場合に例外を発生させることが期待されます:

from odoo.exceptions import ValidationError

@api.constrains('age')
def _check_something(self):
    for record in self:
        if record.age > 20:
            raise ValidationError("Your record is too old: %s" % record.age)
    # all records passed the test, don't return anything

Exercise

Python constraints を追加

講師が自身のセッションの出席者に存在しないことを確認する制約を追加します。

SQL制約は、モデル属性 _sql_constraints によって定義されます。 後者は文字列 (name, sql_definition, message) のトリプルリストに割り当てられます。name は有効な SQL 制約名です。 sql_definitiontable_constraint 式、message はエラーメッセージです。

Exercise

SQLの制約を追加

PostgreSQLのドキュメント の助けを借りて、以下の制約を追加します。

  1. コースの説明とコースタイトルが異なることを確認してください

  2. コースの名前をユニークにする

Exercise

エクササイズ6 - 重複したオプションを追加

コース名の一意性に制約が追加されましたので、もう「複製」関数を使用することはできません(Form ‣ Duplicate)。

コースオブジェクトの複製を可能にする独自の「コピー」メソッドを再実装し、元の名前を「format@@0」に変更します。

アドバンスドビュー

リストビュー

リストビューでは、追加の属性を使用して動作をさらにカスタマイズできます。

decoration-{$name}

をオンにすると、対応するレコードの属性に基づいて行のテキストのスタイルを変更できます。

値は Python の式です。 各レコードに対して、式はレコードの属性をコンテキスト値として評価され、true の場合、対応するスタイルが行に適用されます。 コンテキストで利用可能なその他の値を次に示します。

  • uid: 現在のユーザの id

  • today: YYYY-MM-DD 形式の文字列として現在のローカル日付

  • now: today と同じで、現在の時刻を追加します。この値は YYYY-MM-DD hh:mm:ss でフォーマットされます。

{$name}bf (font-weight: bold), it (font-style: italic), または任意の bootstrapコンテキスト色 (dang, info, muted, primary, success または warning).

<list string="Idea Categories" decoration-info="state=='draft'"
    decoration-danger="state=='trashed'">
    <field name="name"/>
    <field name="state"/>
</list>
editable

"top" または "bottom" のいずれかです。 (フォームビューを通過するのではなく) リストビューを編集可能な場所にします。値は、新しい行が表示される位置です。

Exercise

リストの色付け

セッションが5日未満のセッションが青色になるようにセッションリストビューを変更します 15日以上続くものは赤色に染まります

カレンダー

レコードをカレンダーイベントとして表示します。そのルート要素は <calendar> で、最も一般的な属性は次のとおりです。

color

*カラーセグメンテーション*に使用されるフィールドの名前。 色はイベントに自動的に配布されます しかし、同じ色セグメント内のイベント(@color フィールドに同じ値を持つレコード)は同じ色になります。

date_start

レコードのイベントの開始日時を保持するフィールド

date_stop (任意)

イベントの終了日時を保持するレコードのフィールド

string

カレンダーイベントごとにラベルを定義するレコードフィールド

<calendar string="Ideas" date_start="invent_date" color="inventor_id">
    <field name="name"/>
</calendar>

Exercise

カレンダビュー

カレンダービューを Session モデルに追加し、ユーザーがOpen Academyに関連するイベントを表示できるようにします。

ビューを検索

検索ビュー <field> 要素は、指定したフィールドで検索するために生成されたドメインを上書きする @filter_domain を持つことができます。 指定されたドメインでは、self はユーザーによって入力された値を表します。 以下の例では、namedescription の両方のフィールドを検索するために使用されます。

Search views can also contain <filter> elements, which act as toggles for predefined searches. Filters must have one of the following attributes:

domain

現在の検索に指定されたドメインを追加

context

現在の検索にコンテキストを追加します。キーの group_by を使用して、指定したフィールド名の結果をグループ化します。

<search string="Ideas">
    <field name="name"/>
    <field name="description" string="Name and description"
           filter_domain="['|', ('name', 'ilike', self), ('description', 'ilike', self)]"/>
    <field name="inventor_id"/>
    <field name="country_id" widget="selection"/>

    <filter name="my_ideas" string="My Ideas"
            domain="[('inventor_id', '=', uid)]"/>
    <group string="Group By">
        <filter name="group_by_inventor" string="Inventor"
                context="{'group_by': 'inventor_id'}"/>
    </group>
</search>

アクションで非デフォルトの検索ビューを使用するには、アクションレコードの search_view_id フィールドを使用してリンクする必要があります。

アクションは context フィールドを通じて検索フィールドのデフォルト値を設定することもできます: search_default_field_name のコンテキストキーは、field_name を与えられた値で初期化します。 検索フィルタにはデフォルトを持つオプションの @name が必要で、ブール値として動作します (デフォルトでのみ有効にできます)。

Exercise

ビューを検索

  1. ボタンを追加すると、現在のユーザがコースの検索ビューで担当しているコースをフィルタリングできます。デフォルトで選択します。

  2. 責任あるユーザによってコースをグループ化するボタンを追加します。

ガント

警告

gantt ビューは、 enterial edition バージョンに存在する web_gantt モジュールを必要とします。

プロジェクト計画と進歩を示すために一般的に使用される水平棒グラフは、ルート要素は <gantt> です。

<gantt string="Ideas"
       date_start="invent_date"
       date_stop="date_finished"
       progress="progress"
       default_group_by="inventor_id" />

Exercise

Gantt charts

ガントチャートを追加すると、ユーザは、オープンアカデミーモジュールにリンクされたセッションスケジューリングを表示できます。セッションは教員ごとにグループ化される必要があります。

グラフ表示

グラフビューではモデルの概要と分析が可能になります。ルート要素は「<graph>」です。

注釈

ピボットビュー (要素 <pivot>) は多次元テーブルです。 を選択すると、よりグラフィカルな概要に移動する前に、適切な集計データセットを取得できます。 ピボットビューは、グラフビューと同じコンテンツ定義を共有します。

グラフビューには 4 つの表示モードがあり、デフォルトモードは @type 属性を使用して選択されます。

バー (既定)

棒グラフ、最初の寸法は、水平軸上のグループを定義するために使用され、その他の寸法は、各グループ内の集計された棒を定義します。

デフォルトのバーは並んでいますが、<graph>@stacked="True" を使用することで積み上げることができます。

ライン

二次元折れ線

円グラフ

2次元パイ

グラフビューには、値を取得する必須の @type 属性を持つ <field> が含まれています。

row (default)

このフィールドはデフォルトで集計される必要があります

measure

フィールドはグループ化するのではなく集計する必要があります

<graph string="Total idea score by Inventor">
    <field name="inventor_id"/>
    <field name="score" type="measure"/>
</graph>

警告

グラフビューはデータベース値の集計を行いますが、保存されていない計算されたフィールドでは動作しません。

Exercise

グラフビュー

バーチャートの形式で、コースごとに表示されるセッションオブジェクトにグラフビューを追加します。

かんばん

タスク、プロダクションプロセスなどを整理するために使用されます。… ルート要素は `<kanban> ``です。

かんばんビューには、列にグループ化されている可能性のあるカードのセットが表示されます。 各カードはレコードを表し、各列に集計項目の値を表します。

たとえば、プロジェクトタスクはステージごとに整理される(各列はステージです)、または責任ある(各列はユーザーです)などがあります。

カンバンビューでは、各カードの構造をフォーム要素(基本的なHTMLを含む)と :ref:`reference/qweb`の組み合わせとして定義します。

Exercise

かんばんビュー

コースごとにグループ化されたセッションを表示するカンバンビューを追加します(列はコースです)。

セキュリティ

一貫したセキュリティポリシーを達成するには、アクセス制御メカニズムを構成する必要があります。

グループベースのアクセス制御機構

グループは res.groups モデルで通常のレコードとして作成され、メニュー定義によるメニューアクセスが許可されます。 しかし、メニューがない場合でも、オブジェクトは間接的にアクセスできるため、実際のオブジェクトレベルの権限 (read、write、create、unlink) をグループに定義する必要があります。 これらは通常、CSVファイルを介してモジュール内に挿入されます。 また、このフィールドの groups 属性を使用して、ビューまたはオブジェクトの特定のフィールドへのアクセスを制限することもできます。

アクセス権

アクセス権はモデル ir.model.access のレコードとして定義されます。 各アクセス権は、モデル、グループ(またはグローバルアクセスのグループ)、および一連の権限(read、write、create、unlink)に関連付けられています。 このようなアクセス権は通常、モデル名にちなんで名付けられた CSV ファイルによって作成されます: ir.model.access.csv

id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink
access_idea_idea,idea.idea,model_idea_idea,base.group_user,1,1,1,0
access_idea_vote,idea.vote,model_idea_vote,base.group_user,1,1,1,0

Exercise

Odoo インターフェイスからアクセス制御を追加

新しいユーザー "John Smith" を作成し、Session モデルへの読み取りアクセスを持つグループ「OpenAcademy / Session Read」を作成します。

Exercise

モジュール内のデータ ファイルによるアクセス制御を追加

データファイルの使用

  • 全てのOpenAcademyモデルにフルアクセスできる*OpenAcademy/Manager*グループを作成

  • *セッション*と*コース*をすべてのユーザーが読み取れるようにする

ルールを記録

レコードルールは、アクセス権を与えられたモデルのレコードのサブセットに制限します。ルールは、モデル ir. ule はモデルに関連付けられており、いくつかのグループ (多数のフィールド)、制限が適用されるパーミッション、およびドメインに関連付けられています。 ドメインは、アクセス権が制限されているレコードを指定します。

以下に、cancel 状態にないリードの削除を防ぐルールの例を示します。 フィールド groups の値はORMのメソッド write() と同じ規則に従う必要があることに注意してください。

<record id="delete_cancelled_only" model="ir.rule">
    <field name="name">Only cancelled leads may be deleted</field>
    <field name="model_id" ref="crm.model_crm_lead"/>
    <field name="groups" eval="[(4, ref('sales_team.group_sale_manager'))]"/>
    <field name="perm_read" eval="0"/>
    <field name="perm_write" eval="0"/>
    <field name="perm_create" eval="0"/>
    <field name="perm_unlink" eval="1" />
    <field name="domain_force">[('state','=','cancel')]</field>
</record>

Exercise

レコードルール

モデルの記録ルールを追加 コースとグループ「OpenAcademy/Manager」を追加 これにより、writeunlink へのアクセスがコースの責任に制限されます。 コースに責任がない場合は、グループのすべてのユーザがそれを変更できる必要があります。

ウィザード

Wizards describe interactive sessions with the user (or dialog boxes) through dynamic forms. A wizard is simply a model that extends the class TransientModel instead of Model. The class TransientModel extends Model and reuse all its existing mechanisms, with the following particularities:

  • ウィザードのレコードは永続的なものではなく、一定時間後に自動的にデータベースから削除されます。 これらは*一時的*と呼ばれる理由です。

  • ウィザードレコードは、リレーショナルフィールド(many2oneまたはmany2many)を通じて通常のレコードまたはウィザードレコードを参照することができます。 しかし、通常のレコードは、多数のフィールドを介してウィザードのレコードを参照できません* 。

ユーザーが特定のセッションの出席者を作成したり、一度にセッションのリストを作成したりできるウィザードを作成したいと考えています。

Exercise

ウィザードを定義する

多数のリレーションでウィザードモデルを作成し、Session モデルと多数のリレーションと Partner モデルとのリレーションを作成します。

起動中のウィザード

Wizards are simply window actions with a target field set to the value new, which opens the view (usually a form) in a separate dialog. The action may be triggered via a menu item, but is more generally triggered by a button.

ウィザードを起動する他の方法としては、リストまたはフォームビューの Action メニューを使用します。 これはアクションの binding_model_id フィールドを介して行われます。 このフィールドを設定すると、アクションが「境界」にあるモデルのビューにアクションが表示されます。

<record id="launch_the_wizard" model="ir.actions.act_window">
    <field name="name">Launch the Wizard</field>
    <field name="res_model">wizard.model.name</field>
    <field name="view_mode">form</field>
    <field name="target">new</field>
    <field name="binding_model_id" ref="model_context_model_ref"/>
</record>

ちなみに

ウィザードは通常のビューとボタンを使用しますが、通常はフォーム内の任意のボタンをクリックすると、まずフォームが保存され、ダイアログが閉じます。 これはウィザードでは望ましくないことが多いので、特別な属性 special="cancel" が利用でき、フォームを保存せずにすぐにウィザードを閉じます。

Exercise

ウィザードを起動する

  1. ウィザードのフォームビューを定義します。

  2. Session モデルのコンテキストで起動するアクションを追加します。

  3. ウィザードでセッションフィールドのデフォルト値を定義します。現在のセッションを取得するには、コンテキストパラメータ self._context を使用します。

Exercise

参加者を登録する

ウィザードにボタンを追加し、指定したセッションに出席者を追加するための対応する方法を実装します。

Exercise

複数のセッションに参加者を登録する

参加者を複数のセッションに登録できるようにウィザードモデルを変更します。

国際化

各モジュールは、LANGという名前のファイルを持つことで、i18nディレクトリ内に独自の翻訳を提供することができます。 o 言語のロケールコードはLANG、または言語と国の組み合わせが異なるとき (e. をクリックします。 pt.poまたはpt_BR.po)。 翻訳はすべて有効になっている言語に対して自動的にOdooによって読み込まれます。 開発者は常にモジュールの作成時に英語を使用します そして、Odoo の gettext POT エクスポート機能を使用してモジュール用語をエクスポートします (:menuselection:`Settings ---> Translations --> Import/Export --> 言語を指定せずに翻訳をエクスポートします) をクリックしてモジュールテンプレートの POTファイルを作成し、翻訳されたPOファイルを導出します。 多くのIDEには、PO/POTファイルを編集およびマージするためのプラグインまたはモードがあります。

ちなみに

Odoo によって生成された Portable Object ファイルは Transifex に公開され、ソフトウェアの翻訳が容易になります。

|- idea/ # The module directory
   |- i18n/ # Translation files
      | - idea.pot # Translation Template (exported from Odoo)
      | - fr.po # French translation
      | - pt_BR.po # Brazilian Portuguese translation
      | (...)

ちなみに

デフォルトでは、Odoo の POT は、XML ファイル内または Python コード内のフィールド定義内のラベルのみを抽出します。 しかし、任意の Python 文字列は、関数 odoo で囲むことでこのように変換することができます。 ` (例: ``_("Label")`())

Exercise

モジュールを翻訳

Odoo インストール用の 2 番目の言語を選択します。Odoo が提供する機能を使用してモジュールを翻訳します。

レポーティング

印刷レポート

Odoo は QWeb テンプレートTwitter BootstrapWkhtmltopdf に基づいてレポートエンジンを使用します。

レポートは、2 つの要素の組み合わせです。

  • レポートのさまざまな基本的なパラメータを設定する ir.actions.report (デフォルトの型、生成後にレポートをデータベースに保存するかどうか、…)

    <record id="account_invoices" model="ir.actions.report">
        <field name="name">Invoices</field>
        <field name="model">account.invoice</field>
        <field name="report_type">qweb-pdf</field>
        <field name="report_name">account.report_invoice</field>
        <field name="report_file">account.report_invoice</field>
        <field name="attachment_use" eval="True"/>
        <field name="attachment">(object.state in ('open','paid')) and
            ('INV'+(object.number or '').replace('/','')+'.pdf')</field>
        <field name="binding_model_id" ref="model_account_invoice"/>
        <field name="binding_type">report</field>
    </record>
    

    ちなみに

    なぜならそれは大きく標準的な行動だからです ウィザード と同様に、binding_model_id フィールドを介して報告されるモデルの コンテキストアイテム としてレポートを追加することは一般的に便利です。

    ここでは、レポートが action ではなく、report コンテキストメニューに表示されるように、binding_type も使用します。 技術的な違いはありませんが、要素を適切な場所に置くことは、ユーザーを助けます。

  • 実際のレポートの標準 QWeb ビュー :

    <t t-call="web.html_container">
        <t t-foreach="docs" t-as="o">
            <t t-call="web.external_layout">
                <div class="page">
                    <h2>Report title</h2>
                </div>
            </t>
        </t>
    </t>
    

    標準的なレンダリングコンテキストでは、いくつかの要素が提供されます。最も重要な要素です。

    docs

    レポートが印刷された記録は

    user

    利用者はレポートを印刷し

レポートは標準の Web ページであるため、URL を介して利用可能であり、この URL を介して出力パラメータを操作することができます。 例えば、Invoice レポートの HTML バージョンは http://localhost:8069/report/html/accountから入手できます。 eport_invoice/1 (account がインストールされている場合) および http://localhost:8069/report/pdf/account.report_invoice/1.

危険

PDFレポートにスタイルが欠落していると表示される場合(つまり、 テキストが表示されますが、スタイル/レイアウトはhtmlのバージョンと異なります)。おそらく、wkhtmltopdf_プロセスはウェブサーバーに届きません。

サーバーのログを確認し、PDFレポートを生成する際にCSSスタイルがダウンロードされていないことを確認してください。 最も確かにこれが問題だ

wkhtmltopdf プロセスは web.base を使用します。 すべてのリンクされたファイルへの*ルートパス*としてrl システムパラメータですが、管理者がログインするたびにこのパラメータは自動的に更新されます。 サーバーが何らかのプロキシの後ろに存在する場合、到達できませんでした。これらのシステムパラメータのいずれかを追加することで修正できます。

  • report.url は、サーバーから到達可能な URL を指しています (おそらく http://localhost:8069 など)。この特定の目的のためにのみ使用されます。

  • web.base.url.freezeTrue に設定されている場合、web.base.url の自動更新を停止します。

Exercise

セッションモデルのレポートを作成する

セッションごとに、セッションの名前、開始と終了を表示し、セッションの参加者を一覧表示する必要があります。

ダッシュボード

Exercise

ダッシュボードを定義

作成したグラフビュー、セッションカレンダービュー、コースのリストビューを含むダッシュボードを定義します (フォームビューに切り替えることができます)。 このダッシュボードは、メニューのメニュー項目から利用でき、OpenAcademyのメインメニューが選択されると自動的にWebクライアントに表示されます。

1

it is possible to disable the automatic creation of some fields

2

SQLクエリを生で書くことは可能ですが、Odoo認証とセキュリティメカニズムをすべて回避するため、注意が必要です。