遺産
さまざまな種類の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が発生します。
| 上記の例では、提供しない場合は `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モデルのフィールドを調べると、次のようになります。
| 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パラメータを提供するだけです。
| #上記の例の残りは同じままです。
クラスバス(車):
order_config=base_order_config. copy(tablename="バス")
#関連する名前を変更する新しいフィールド
所有者: Person=ormar.ForeignKey(Person、関連する名前="バス")
max_人: int=ormar.Integer()
|
今、柱はずっと良く見えます。
| 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を指定して、正しい接尾辞を追加することもできます。
| `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モデルのパススルーモデルを確認しておきましょう。
| 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モデルをクローンし、末尾にサブクラス名を追加すると同時に、クローンフィールドのテーブル名を変更し、サブクラスのテーブル名を使用します。
元のモデルが使用されていないだけでなく、そのモデルのテーブルもメタデータから削除されていることに注意してください。
| 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モデルを生成することに注意してください。これは、新しいデータベーステーブルも意味します。
| つまり、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()
|
このように、必要に応じてクラスの作成と更新から継承できます。そうでない場合は、どちらか一方のクラスのみを継承できます。