コンテンツにスキップ

遺産

さまざまな種類のORMモデル継承の中で、ormarは現在、次の2つをサポートしています。

  • 混入
  • 具体的なテーブルの継承(親は抽象=Trueに設定されています)

継承型

異なるタイプの継承の簡単な要約:

  • Mixins[サポート]-ormar. Modelをサブクラス化しないでください。後で異なるモデルで使用されるフィールドを定義するだけです(各モデルで作成された_dateや更新された_dateなど)。実際のモデルだけがテーブルを作成しますが、ミキシンのフィールドを追加します
  • 具体的なテーブル継承[SUPPORTED]-親が抽象化としてマークされていることを意味し、各子には親からの列と独自の子列を含む独自のテーブルがあり、Mixinsに少し似ていますが、親もモデルです
  • 単一テーブル継承[サポートされていません]-フィールドが親モデルとすべてのサブモデルの組み合わせ/合計であるテーブルのみを作成することを意味しますが、サブモデルはデータベース内の列のサブセットのみを使用します(すべての親モデルと独自の列、他のサブモデルをスキップします の)
  • マルチ/接続テーブル継承[サポートされていません]-部分は親モデルに保存され、部分は子モデルに保存され、1対1の関係で相互に接続され、バックグラウンドで2つのモデルを操作することを意味します
  • プロキシモデル[サポートされていません]-親のみが実際のテーブルを持ち、子はメソッドの追加、設定の変更などを行うだけです。

混入

Mixinsを使用するには、ormar. Modelから継承されていないクラスを定義するだけで、ormar.Fieldsをクラス変数として定義します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
base_ormar_config=ormar. OrmarConfig(ベースの設定
    データベース=データベース。データベース(DATABASE_URL)、
    metadata=sqlalchemy. MetaData(),
    engine=sqlalchemy. create_engine(DATABASE_URL),
)


#mix inはフィールドを定義しますが、通常のPythonクラスです。
クラスAuditMixin:
    作成者: str=ormar.String(max_length=100)
    update_by: str=ormar.String(max_length=100,default="Sam")


class DateFieldsMixins:
    作成日: date time.date time=or mar.DateTime(デフォルト=datetime.datetime.now)
    update_date: date time.date time=or mar.DateTime(デフォルト=datetime.datetime.now)


#モデルは1つ以上のミキシンから継承できます
class Category(ormar. Model,DateFieldsMixins,AuditMixin):
    or_config=base_or_config. copy(tablename="カテゴリ")

    id: int=ormar.Integer(プライマリキー=True)
    name: str=ormar.String(max_length=50,unique=True,index=True)
    コード: int=ormar.Integer()

!!!tip Mixinはモデルではないため、ormar. Modelを継承し、最終的なモデルでormar_configフィールドを定義する必要があることに注意してください。

上記のCategoryクラスには、作成された_date、更新された_date、作成された_by、および更新された_byの4つの追加フィールドがあります。

CategoryクラスフィールドがすべてのMixinsフィールドと組み合わされたモデルCategory(カテゴリ)のテーブルのみが作成されます。

クラス名のMixinは任意ですが、これは良いPythonの練習であることに注意してください。

具体的なテーブルの継承

概念的には、特定のテーブル継承はMixinsと非常に似ていますが、実際のormar. Modelsを基本クラスとして使用します。

!!!は、基底クラスがormar_configオブジェクトに対して、抽象=Trueを設定していることに注意してください。

この抽象モデルは決して初期化されないため、ormar_config定義のメタデータとデータベースをスキップできます。

しかし、それを提供すれば、それは継承されるので、最終/具体的なクラスでメタデータとデータベースを提供する必要はありません

必要に応じて、子/特定のクラスで常に上書きできることに注意してください。

さらに、継承チェーン内の少なくとも1つのクラスがデータベースとメタデータを提供する必要があります。そうしないと、エラーが発生します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#基本クラスには抽象=Trueがあることに注意してください
#このモデルは決して初期化されないため、メタデータやデータベースをスキップすることができます。
class AuditModel(ormar. Model):
    or_config=base_or_config. copy(抽象=True)

    作成者: str=ormar.String(max_length=100)
    update_by: str=ormar.String(max_length=100,default="Sam")


#しかし、それを提供すれば継承されます-DRY(自分を繰り返さないでください)
class DateFieldsModel(ormar. Model):
    base_ormer_config. copyを使用します。
        抽象=真、
        metadata=メタデータ,
        データベース=db,
    )

    作成日: date time.date time=or mar.DateTime(デフォルト=datetime.datetime.now)
    update_date: date time.date time=or mar.DateTime(デフォルト=datetime.datetime.now)


#そうすれば、具体的なクラスでメタデータやデータベースを提供する必要はありません。
classカテゴリー(DateFieldsModel, AuditModel):
    or_config=base_or_config. copy(tablename="カテゴリ")

    id: int=ormar.Integer(プライマリキー=True)
    name: str=ormar.String(max_length=50,unique=True,index=True)
    コード: int=ormar.Integer()

継承されるオプション/設定のリストは、メタデータ、データベース、および制約です。

もちろん、これに加えて、基本クラスのすべてのフィールドは、最終モデルの特定のテーブルに結合されて作成されます。

!!!tip最終クラスでabstarct=Falseを指定する必要はないことに注意してください。これは継承されないデフォルトの設定です。

サブクラスのフィールドを再定義します

以前に作成したフィールドを通常のPythonクラスの継承と同様に再定義できることに注意してください。

同じ名前と新しい定義を持つフィールドを定義するたびに、以前に定義されたフィールドが完全に置き換えられます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#基本クラス
class DateFieldsModel(ormar. Model):
    OrmarConfig(オーマコンフィグ)
        抽象=真、
        metadata=メタデータ,
        データベース=db,
        UniqueColumnsにはsqlalchemy dbカラム名が必要です。
        制約=[ormar. UniqueColumns("作成_日付","変更_日付")]
    )

    作成された日付: date time.date time=ormar.DateTime(
        デフォルト=datetime.datetime.now、name="create_date"
    )
    update_date: date time.date time=または.date Time(
        デフォルト=datetime.datetime.now、名前="変更日"
    )


class再定義フィールド(DateFieldsModel):
    OrmarConfig(オーマコンフィグ)
        tablename="再定義",
        metadata=メタデータ,
        データベース=db,
    )

    id: int=ormar.Integer(プライマリキー=True)
    #ここでは、create_dateはStringフィールドに置き換えられます。
    create_date: str=ormar.String(max_length=200,name="create_date")


#最終フィールドが正しく宣言され、作成されていることを確認できます。
change_field=再定義フィールド. or_config.model_field["作成日"]
assert change_field. defaultはNone
assert change_field. alias=="create_date"
アサートany(x.name=="create_date"for x in Redefined edField. or_config.table.column)
isinstanceをアサート(
    再定義フィールド. ormar_config.table.column["create_date"].type,
    sqlalchemy. sql.sqltypes.String,
)

!!!警告UniqueColumns制約をカラム名で宣言する場合、最終モデルは同じ名前のカラムを宣言する必要があります。そうしないと、ModelDefinitionErrorが発生します。

1
2
3
4
5
6
上記の例では、提供しない場合は `name` ために `created_date` で `RedefinedField` モデル
オーマーは文句を言う

`created_date: str = ormar.String(max_length=200) # exception`

`created_date: str = ormar.String(max_length=200, name="creation_date2") # exception`

継承関係

親クラスでも子クラスでも、継承の各ステップで関係を宣言できます。

子モデルレベルでリレーションシップを定義すると、親モデルで定義されているリレーションシップが上書きされます(同じフィールド名が使用されている場合)。

リレーションシップを継承するときは、同じリレーションシップを継承する複数のサブクラスを定義するときに、リレーションシップモデル内で一意である必要がある関連_nameパラメーターに常に注意を払う必要があります。

関連する_nameパラメータを指定しない場合、ormarはあなたのために計算します。これは継承の場合に適用されます。なぜなら、すべてのサブモデルには、デフォルトの関連する_name(class.name. lower()+'s')を計算するために使用される異なるクラス名が必要だからです。

ただし、関連する_nameを指定すると、関連するモデル側で互いに上書きされるため、すべてのサブモデルでその名前を再利用することはできません。

したがって、2つのオプションがあります。

  • サブモデルのリレーショナルフィールドを再定義し、別の関連_nameパラメータを手動で指定します
  • はormarに処理させます->自動調整された関連_nameは、元の関連_name+"_"+サブモデルテーブル名です

これは複雑に聞こえるかもしれませんが、次の例を見てみましょう。

外部キー関係

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#関係で使用される通常のモデル
class Person(ormar. Model):
    base_or_config. copy()

    id: int=ormar.Integer(プライマリキー=True)
    name: str=ormar.String(max_length=100)


#親モデル-抽象的である必要があります
class Car(ormar. Model):
    or_config=base_or_config. copy(抽象=True)

    id: int=ormar.Integer(プライマリキー=True)
    name: str=ormar.String(max_length=50)
    オーナー: Person=ormar.ForeignKey(Person)
    #Personモデルを再度参照するため、**は**に関連する名前を提供する必要があることに注意してください
    co_Owner: Person=ormar.ForeignKey(Person、関連する名前="共有")
    作成日: date time.date time=or mar.DateTime(デフォルト=datetime.datetime.now)


クラストラック(車):
    base_or_config. copy()

    max_キャパシティ: int=ormar.Integer()


クラスバス(車):
    order_config=base_order_config. copy(tablename="バス")

    max_人: int=ormar.Integer()

さて、Personモデルのフィールドを調べると、次のようになります。

1
2
3
4
5
6
7
8
9
Person. ormer_config.model_fieldの設定
"""
{'id': <class 'ormar.fields.model_fields.Integer'>、 
'名前': <class 'ormar.fields.model_fields.String'>、 
'トラック': <class 'ormar.fields.foreign_key.ForeignKey'>、 
'coowned_trucks': <class 'ormar.fields.foreign_key.ForeignKey'>、 
'バス': <class 'ormar.fields.foreign_key.ForeignKey'>、 
'coowned_bus': <class 'ormar.fields.foreign_key.ForeignKey'>}
"""

トラックとバスのフィールドがどのように所有されているかに注意してください。これは、その人が所有するトラックとバスのカテゴリにつながります。関連する_nameパラメーターがないため、デフォルトの名前が使用されます。

同時に共有されている車は、共有された_trucksと共有された_busを介して参照する必要があります。Ormarは、サブモデルテーブル名から取られた_trucksと_busサフィックスを追加します。

見た目は良いのですが、所有トラックのデフォルトの名前は良い(トラック)ですが、バスは見にくいので、どうやって変更しますか?

解決策は非常に簡単です-Busクラスのフィールドを再定義し、異なる関連_nameパラメータを提供するだけです。

1
2
3
4
5
6
7
#上記の例の残りは同じままです。
クラスバス(車):
    order_config=base_order_config. copy(tablename="バス")

    #関連する名前を変更する新しいフィールド
    所有者: Person=ormar.ForeignKey(Person、関連する名前="バス")
    max_人: int=ormar.Integer()

今、柱はずっと良く見えます。

1
2
3
4
5
6
7
8
9
Person. ormer_config.model_fieldの設定
"""
{'id':<class 'ormar.fields.model_fields.Integer'>,
'name':<class 'ormar.fields.model_fields.String'>,
'trucks':<class 'ormar.fields.foreign_key.ForeignKey'>,
'coowned_trucks':<class 'ormar.fields.foreign_key.ForeignKey'>,
'バス':<class 'ormar.fields.foreign_key.ForeignKey'>,
'coowned_bus':<class 'ormar.fields.foreign_key.ForeignKey'>}
"""

!!!(注)所有者フィールドに関連する_nameを指定して、正しい接尾辞を追加することもできます。

1
2
3
`owner: Person = ormar.ForeignKey(Person, related_name="owned")`

そして、Personが所有する車のモデルフィールドは `owned_trucks` と `owned_buses` になります。

多対多の関係

同様に、ManyTomany関係を宣言したモデルから継承できますが、実質的な違いが1つあります。

Throughモデルは他のフィールドを保存できるようになり、2つのテーブル(1つのテーブルからと1つのテーブル)のみをリンクするようになったため、m2 mリレーションシップフィールドを継承する各サブアイテムには、個別のThroughモデルが必要です。

もちろん、各サブモデル内の関係を上書きすることもできますが、これには追加のコードが必要で、継承全体の意味が損なわれます。デフォルトの命名規則に同意する場合は、Ormarがこの問題を処理し、必要に応じて、いつでも手動でサブアイテム内の規則を上書きできます。

繰り返しになりますが、概念を把握しやすくするためにサンプルを見てみましょう。

上記の前述の例を、co_オーナーのm2 m関係を使用するように変更します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#人は上記と同じまま
class Person(ormar. Model):
    base_or_config. copy()

    id: int=ormar.Integer(プライマリキー=True)
    name: str=ormar.String(max_length=100)

#PersonとCar 2の間の新しいスルーモデル
class PersonsCar(ormar. Model):
    table name="cars_x_人")

共同所有者がManyTomany関係になったことに注意してください
class Car 2(ormar. Model):
    or_config=base_or_config. copy(抽象=True)

    id: int=ormar.Integer(プライマリキー=True)
    name: str=ormar.String(max_length=50)
    #note関連する名前-人全体で一意である必要があります
    #model、人につながるモデルの数に関係なく
    所有者: Person=ormar.ForeignKey(Person、関連する名前="所有")
    co_オーナー: List[Person]=ormar.ManyTomany(
        人、通じ=PersonsCar、関連する名前="共有"
    )
    作成日: date time.date time=or mar.DateTime(デフォルト=datetime.datetime.now)


#子モデルは追加のフィールドのみを定義します
class Truck 2(Car 2):
    or man_config=base_or man_config. copy(tablename="trucks 2")

    max_キャパシティ: int=ormar.Integer()


class Bus 2(Car 2):
    or_config=base_or_config. copy(tablename="buses 2")

    max_人: int=ormar.Integer()

Ormarは、フィールドの関連_nameを自動的に変更して、サブモデルのテーブル名を含めます。デフォルトの名前は、元の関連_name+'_'+サブテーブル名です。

このようにTruck2クラスでは、定義された関係 owner: Person = ormar.ForeignKey(Person, related_name="owned") が所有\trucks2になります

人物モデル上に存在するフィールドのリストを確認することで、名前を確認できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
Person. ormer_config.model_fieldの設定
{
#すべてのリレーションフィールドがPersonで一意である必要があることに注意してください
#自動生成または手動で上書きされた場合に関係なく
'id':<class 'ormar.fields.model_fields.Integer'>,
'name':<class 'ormar.fields.model_fields.String'>,
#前の例を拡張したので、すべての「古い」フィールドがここにあることに注意してください。
'trucks':<class 'ormar.fields.foreign_key.ForeignKey'>,
'coowned_trucks':<class 'ormar.fields.foreign_key.ForeignKey'>,
'バス':<class 'ormar.fields.foreign_key.ForeignKey'>,
'coowned_bus':<class 'ormar.fields.foreign_key.ForeignKey'>,
#新たに定義された関連フィールド
'owned_trucks 2':<class 'ormar.fields.foreign_key.ForeignKey'>,
'coowned_trucks 2':<class 'abc.ManyToMany'>,
'owned_buses 2':<class 'ormar.fields.foreign_key.ForeignKey'>,
'coowned_bus 2': <class 'abc.ManyToMany'>
}

しかし、それだけではありません。ormarの内部構造ですが、データベース内のデータ構造に影響を与えますので、Bus 2とTruck 2モデルのパススルーモデルを確認しておきましょう。

1
2
3
4
5
6
7
8
9
Bus 2. ormer_config.model_field['co_オーナー'].through
<class 'abc.PersonsCarBus2'>
Bus 2. or_config.model_field['co_オーナー'].through.or_config.tablename
'cars_x_person_buses 2'

Truck 2. or mer_config.model_field['co_オーナー'].through
<class 'abc.PersonsCarTruck2'>
Truck 2. ormar_config.model_field['co_オーナー'].through.ormar_config.tablename
'cars_x_person_trucks 2'

上記のように、ormarはサブクラスごとにThroughモデルをクローンし、末尾にサブクラス名を追加すると同時に、クローンフィールドのテーブル名を変更し、サブクラスのテーブル名を使用します。

元のモデルが使用されていないだけでなく、そのモデルのテーブルもメタデータから削除されていることに注意してください。

1
2
3
Bus 2. ormer_config.metadata.tables.keys()
dict_keys(['test_date_model','カテゴリー','被験者','人','トラック','バス',
           'cars_x_person_trucks 2','trucks 2','cars_x_person_buses 2','buses 2'])

したがって、継承を導入し、モデルを抽象的な親モデルに変換する場合、注意しないとテーブル内のデータが失われる可能性があることに注意してください。

!!!Note Throughモデルの元のテーブル名とモデル名は決して使用されないことに注意してください。クローンされたモデルテーブルのみが作成および使用されます。

!!!警告ManyTomanyリレーションシップを定義するモデルの各サブクラスは、新しいThroughモデルを生成することに注意してください。これは、新しいデータベーステーブルも意味します。

1
2
つまり、Childモデルを定義するたびに、手動で作成する必要があります。
データベース内のテーブル、または(alembicを使用して)マイグレーションを実行します。

親フィールドの除外

Ormarを使用すると、親モデルから継承モデルの特定のフィールドをスキップできます。

!!!Noteモデルをより抽象的なモデルとミックスインに分割することで、同じ動作を実現できることに注意してください。

サブモデルの特定のフィールドをスキップするには、スキップするすべてのフィールドをリストします。model.ormar_config.exclude_parent_fields パラメーターは次のとおりです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
base_or_config=オーマコンフィグ(
    Metadata=sa. MetaData(),
    データベース=データベース。データベース(DATABASE_URL)、
)


class AuditModel(ormar. Model):
    or_config=base_or_config. copy(抽象=True)

    作成者: str=ormar.String(max_length=100)
    update_by: str=ormar.String(max_length=100,default="Sam")


class DateFieldsModel(ormar. Model):
    or_config=base_or_config. copy(抽象=True)

    作成された日付: date time.date time=ormar.DateTime(
        デフォルト=datetime.datetime.now、name="create_date"
    )
    update_date: date time.date time=または.date Time(
        デフォルト=datetime.datetime.now、名前="変更日"
    )


classカテゴリー(DateFieldsModel, AuditModel):
    base_ormer_config. copyを使用します。
        tablename="カテゴリ",
        #スキップすべきフィールドを設定する
        除外親フィールド=["update_by","update_date"],
    )

    id: int=ormar.Integer(プライマリキー=True)
    name: str=ormar.String(max_length=50,unique=True,index=True)
    コード: int=ormar.Integer()

#カテゴリーの更新フィールドがすべての場所でなくなったことに注意してください->ormarフィールド、pydanticフィールド、sqlachemyテーブル列
#カテゴリーで利用可能なフィールドの完全なリストは次のとおりです:["create_by","create_date","id","name","code"]

フィールド名を指定するだけで、そのフィールドがどの親モデルのものであっても、親フィールドは除外されることに注意してください。

!!!注サブモデル内のフィールドを上書きする場合は、それを除外する必要はなく、サブモデル内のフィールド宣言を同じフィールド名で上書きするだけでよいことに注意してください。

警告この動作はmypyと静的型チェッカーを混乱させる可能性があることに注意してくださいが、存在しないフィールドへのアクセスは実行時に失敗します。これが、基本クラスの分割が好まれる理由です。

基本クラスを分割すると、次のように同じ効果を得ることができます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
base_or_config=オーマコンフィグ(
    Metadata=sa. MetaData(),
    データベース=データベース。データベース(DATABASE_URL)、
)


class AuditCreateModel(ormar. Model):
    or_config=base_or_config. copy(抽象=True)

    作成者: str=ormar.String(max_length=100)


class AuditUpdateModel(ormar. Model):
    or_config=base_or_config. copy(抽象=True)

    update_by: str=ormar.String(max_length=100,default="Sam")

クラスCreateDateFieldsModel(ormar. Model):
    or_config=base_or_config. copy(抽象=True)

    作成された日付: date time.date time=ormar.DateTime(
        デフォルト=datetime.datetime.now、name="create_date"
    )

クラスUpdateDateFieldsModel(ormar. Model):
    or_config=base_or_config. copy(抽象=True)

    update_date: date time.date time=または.date Time(
        デフォルト=datetime.datetime.now、名前="変更日"
    )


クラスカテゴリ(CreateDateFieldsModel, AuditCreateModel):
    or_config=base_or_config. copy(tablename="カテゴリ")

    id: int=ormar.Integer(プライマリキー=True)
    name: str=ormar.String(max_length=50,unique=True,index=True)
    コード: int=ormar.Integer()

このように、必要に応じてクラスの作成と更新から継承できます。そうでない場合は、どちらか一方のクラスのみを継承できます。