コンテンツにスキップ

接続とサブクエリ

あるテーブルを別のテーブルに結合して、関連するモデルも読み込むには、次の方法を使用します。

  • select_related(related: Union[List, str]) -> QuerySet

  • select_all(follow: bool = True) -> QuerySet

  • prefetch_related(related: Union[List, str]) -> QuerySet

  • モデル

    • Model. load()メソッド
    • クエリセットエージェント

    • QuerysetProxy.select_related(related: Union[List, str]) 方法

    • QuerysetProxy.select_all(follow: bool=True) 方法
    • QuerysetProxy.prefetch_related(related: Union[List, str]) 方法

関連を選択

select_related(related: Union[List, str]) -> QuerySet

同じクエリ中に関連するモデルを事前にフェッチできます。

select_atedを使用すると、常にデータベースに対して1つのクエリのみが実行されます。つまり、(場合によっては複雑な)接続が生成され、後でネストされたモデルがPythonで処理されます。

関連するモデルを取得するには、外部キー名を使用します。

関連するモデル関係をリンクするには、名前の間に二重下線を使用します。

!!!注djangoから来た場合、djangoのormarselect_atedは->とは異なり、単一の関係タイプしか選択できないことに注意してください。ormarでは、外部キー関係を越えて関連、外部キーの反対側を選択できます(したがって、仮想的に自動的に生成されるキー) ManyTomanyフィールド(したがって、現在のバージョンのすべての関係)。

!!!tipどのモデルフィールドを選択するかを制御するには、field()メソッドを使用します。

!!!tipモデルの順序(マスターモデルまたはネストモデル)を制御するには、order_by()メソッドを使用します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Album(ormar. Model):
    base_or_config. copy()

    id: int=ormar.Integer(プライマリキー=True)
    name: str=ormar.String(max_length=100)
    is_best_セラー: bool=ormar.Boolean(デフォルト=False)

class Track(ormar. Model):
    base_or_config. copy()

    id: int=ormar.Integer(プライマリキー=True)
    アルバム:オプション[Album]=ormar. ForeignKey(Album)
    title: str=ormar.String(max_length=100)
    位置: int=ormar.Integer()
    play_count: int=ormar.Integer(nullable=True)


#ジャンゴスタイル
アルバム=awaitアルバム.オブジェクト.選択関連("tracks"). all()

#Pythonスタイル
アルバム=awaitアルバム.オブジェクト.関連する選択(アルバム.トラック). all()

#すべての列トラックを含むアルバムを返します。

文字列または文字列のリスト(またはフィールド/フィールドのリスト)を指定できます

 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
40
41
42
43
class SchoolClass(ormar. Model):
    or_config=base_or_config. copy(tablename="学校のクラス")

    id: int=ormar.Integer(プライマリキー=True)
    name: str=ormar.String(max_length=100)
    部門:オプション[部門]=ormar. ForeignKey(部門、nullable=False)


class Category(ormar. Model):
    or_config=base_or_config. copy(tablename="カテゴリ")

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


class Student(ormar. Model):
    base_or_config. copy()

    id: int=ormar.Integer(プライマリキー=True)
    name: str=ormar.String(max_length=100)
    School class:オプション[SchoolClass]=ormar. ForeignKey(SchoolClass)
    カテゴリー:オプション[カテゴリー]=ormar. ForeignKey(カテゴリー、nullable=True)


クラスの先生(ormar. Model):
    base_or_config. copy()

    id: int=ormar.Integer(プライマリキー=True)
    name: str=ormar.String(max_length=100)
    School class:オプション[SchoolClass]=ormar. ForeignKey(SchoolClass)
    カテゴリー:オプション[カテゴリー]=ormar. ForeignKey(カテゴリー、nullable=True)


#ジャンゴスタイル
クラス=await SchoolClass. object.select_関連(
    ["教師__カテゴリー","学生"]). all()

#Pythonスタイル
クラス=await SchoolClass. object.select_関連(
    [SchoolClass.教師.カテゴリー, SchoolClass.学生]).all()

#教師と教師のカテゴリーでクラスを返します。
#クラスの生徒だけでなく

Many 2 manyフィールドはまったく同じように動作し、Many 2 manyフィールドの名前を入力して最終的なモデルを取得できます。

!!!警告すべてのクエリ中にForeignKeyフィールドを空にしないように設定した場合(したがって必須)、空にしないモデルはselect_atedに含まれていなくても自動的にプリフェッチされます。

!!!ローを返さないすべてのメソッドは、QuerySetインスタンスを明示的に返すため、それらをリンクできます

1
2
3
そのため、`filter()`、`select_related()`、`limit()`、`offset()` などの操作をチェーン化することができます。

のようなもの `Track.object.select_related("album").filter(album__name="Malibu").offset(1).limit(1).all()`

すべて選択

select_all(follow: bool = False) -> QuerySet

デフォルトではall()を選択してもリレーションシップはロードされませんが、同様にselect_ated()を使用する場合はロードするリレーションシップを明示的に指定する必要があります。ネストされたリレーションシップも含めたい場合は面倒かもしれません。

これがselect_all()が導入された理由で、モデルのすべての関係がデフォルトでロードされます(all()メソッドとは逆)。

デフォルトでは、クエリが実行される親モデルに直接関連するモデルのみが追加されます。

follow=Trueを設定すると、関連モデルの関連モデルも追加されます。

!!!関連するモデルも親モデルとの関係を維持するため、infoは無限ループに陥らないようにします。

1
2
3
4
そうすれば、ネストされたすでに訪問しているモデルがロードされますが、ロードはロードされません
それらinside.SoモデルA->モデルB->モデルC->モデルA->モデルX
2番目のモデルAをロードしますが、モデルXには決して追随しません。
この種のネストされたリレーションは手動で読み込む必要があります。

サンプルの日付は次のとおりです。

 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
base_or_config=オーマコンフィグ(
    Database(DATABASE_URL, force_rollback=True),
    metadata=sqlalchemy. MetaData(),
)


class Address(ormar. Model):
    or_config=base_or_config. copy(tablename="address")

    id: int=ormar.Integer(プライマリキー=True)
    street: str=ormar.String(max_length=100,nullable=False)
    number: int=ormar.Integer(nullable=False)
    post_code: str=ormar.String(max_length=20,nullable=False)



class Branch(ormar. Model):
    or_config=base_or_config. copy(tablename="ブランチ")

    id: int=ormar.Integer(プライマリキー=True)
    name: str=ormar.String(max_length=100,nullable=False)
    address=ormar. ForeignKey(アドレス)


class Company(ormar. Model):
    or_config=base_or_config. copy(tablename="企業")

    id: int=ormar.Integer(プライマリキー=True)
    name: str=ormar.String(max_length=100,nullable=False,name="company_name")
    設立: int=ormar.Integer(nullable=True)
    Branch=ormar. ManyTomany(Branch)

すべての支店と住所を持つすべての会社を選択するには、クエリを実行するだけです。

1
2
3
4
企業=await企業.オブジェクト. select_all(follow=True).all()

これは同等です:
企業=await企業.オブジェクト.関連する選択('ブランチ__アドレス'). all()

もちろん、この場合、select_atedで明示的なリレーションシップ名を発行するのは非常に簡単ですが、複数のリレーションシップを持っている場合、select_all()の利点が現れます。

たとえば、Companyに3つのリレーションシップがあり、それらすべてが独自の3つのリレーションシップを持っている場合、select_atedに9つのリレーションシップ文字列を発行する必要があり、select_all()はリレーションシップ名を変更できません。

!!!注意: select_all()は、filter(filter)や_fieldを除外するなど、他のQuerySetメソッドとリンクすることができます。 リレーションシップを除外するには、除外するリレーションシップの名前(これもネストされています)を使用して、がらくた_field()を呼び出します。

プリフェッチ関連

prefetch_related(related: Union[List, str]) -> QuerySet

クエリ中に関連するモデルを事前にフェッチできますが、select_atedとは対照的に、後続の各モデルは個別のデータベースクエリでフェッチされます。

prefetch_atedを使用すると、各モデルはデータベースに対してクエリを実行します。つまり、複数のクエリを1つずつ実行します。

関連するモデルを取得するには、外部キー名を使用します。

関連するモデル関係をリンクするには、名前の間に二重下線を使用します。

!!!tipどのモデルフィールドを選択するかを制御するには、field()メソッドを使用します。

!!!tipモデルの順序(マスターモデルまたはネストモデル)を制御するには、order_by()メソッドを使用します。

 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
class Album(ormar. Model):
    base_or_config. copy()

    id: int=ormar.Integer(プライマリキー=True)
    name: str=ormar.String(max_length=100)
    is_best_セラー: bool=ormar.Boolean(デフォルト=False)

class Track(ormar. Model):
    base_or_config. copy()

    id: int=ormar.Integer(プライマリキー=True)
    アルバム:オプション[Album]=ormar. ForeignKey(Album)
    title: str=ormar.String(max_length=100)
    位置: int=ormar.Integer()
    play_count: int=ormar.Integer(nullable=True)


#ジャンゴスタイル
アルバム=awaitアルバム.オブジェクト. prefetch_関連("tracks").all()

#Pythonスタイル
アルバム=awaitアルバム.オブジェクト. prefetch_関連(アルバム.トラック).all()


#アルバムを返しますすべての列のトラック

文字列または文字列のリストを指定できます

 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
40
41
42
class SchoolClass(ormar. Model):
    or_config=base_or_config. copy(tablename="学校のクラス")

    id: int=ormar.Integer(プライマリキー=True)
    name: str=ormar.String(max_length=100)
    部門:オプション[部門]=ormar. ForeignKey(部門、nullable=False)


class Category(ormar. Model):
    or_config=base_or_config. copy(tablename="カテゴリ")

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


class Student(ormar. Model):
    base_or_config. copy()

    id: int=ormar.Integer(プライマリキー=True)
    name: str=ormar.String(max_length=100)
    School class:オプション[SchoolClass]=ormar. ForeignKey(SchoolClass)
    カテゴリー:オプション[カテゴリー]=ormar. ForeignKey(カテゴリー、nullable=True)


クラスの先生(ormar. Model):
    base_or_config. copy()

    id: int=ormar.Integer(プライマリキー=True)
    name: str=ormar.String(max_length=100)
    School class:オプション[SchoolClass]=ormar. ForeignKey(SchoolClass)
    カテゴリー:オプション[カテゴリー]=ormar. ForeignKey(カテゴリー、nullable=True)


#ジャンゴスタイル
クラス=await SchoolClass. object.prefetch_関連(
    ["教師__カテゴリー","学生"]). all()

#Pythonスタイル
クラス=await SchoolClass. object.prefetch_関連(
    [SchoolClass.教師.カテゴリー, SchoolClass.学生]).all()
#教師と教師のカテゴリーでクラスを返します。
#クラスの生徒だけでなく

Many 2 manyフィールドはまったく同じように動作し、Many 2 manyフィールドの名前を入力して最終的なモデルを取得できます。

!!!警告すべてのクエリ中にForeignKeyフィールドを空にしないように設定した場合(したがって必須)、空にしないモデルはselect_atedに含まれていなくても自動的にプリフェッチされます。

!!!ローを返さないすべてのメソッドは、QuerySetインスタンスを明示的に返すため、それらをリンクできます

1
2
3
そのため、`filter()`、`select_related()`、`limit()`、`offset()` などの操作をチェーン化することができます。

のようなもの `Track.object.select_related("album").filter(album__name="Malibu").offset(1).limit(1).all()`

選択相関とプリフェッチ相関

「->select_ated」と「prefetch_ated」のどちらを使えばいいですか?

まあ、それは実際にはあなたのデータに依存します。最良の答えは、自分で試して、システムの制限の下でどちらがより速く/より良く機能するかを確認することです。

何を覚えて:

パフォーマンス

クエリの数: select_atedは常にデータベースに対して1つのクエリを実行しますが、prefetch_atedは複数のクエリを実行します。通常、クエリ(I/O)操作は最も遅い操作ですが、必ずしもそうである必要はありません。

行数:テーブルAに10,000個のオブジェクトがあり、各オブジェクトがテーブルBに3つのサブオブジェクトを持ち、次にテーブルBの各オブジェクトがテーブルCに2つのサブオブジェクトを持つと仮定します。次のようになります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
                     モデルC
                   /
           モデルB-モデルC
         / 
モデルA-モデルB-モデルC
       \           \ 
        モデルC
         \
           モデルB-モデルC
                   \ 
                     モデルC

これは、select_atedが常に60,000行(10,000*3*2)を返し、後で10,000モデルに圧縮されることを意味します。

prefetchを返す行は何行ありますか?

モデルBとCのそれぞれが一意である場合、最初のクエリで10,000行、2番目のクエリで30,000行を返します(テーブルBのAの3つのサブ項目) のそれぞれが一意である)、および60,000行(テーブルBの各サブアイテムは一意である。テーブルCのモデルBの2つのサブレベルは、3番目のクエリで一意である)。

この場合、3つのプリフェッチと比較してクエリを実行するだけでなく、100,000のプリフェッチ(10+30+60 k)と比較して60を返します 000行。

しかし、すべてのモデルAにまったく同じ3つのモデルBがあり、すべてのモデルCにまったく同じモデルCがある場合はどうなりますか? select_atedは60,000行を返しますが、prefetch_atedはモデルAに10,000行、モデルBに3行、モデルCに2行を返します。 したがって、合計10 006行です。これで、モデルの構造によっては(つまり、長いText()フィールドなどがある場合)、1つではなく3つの個別のクエリを実行する必要がありますが、prefetch_atedの方が高速になる場合があります。

記憶

ormarはロードされたモデルのレジストリを保持しません。

これは、上記のselect_atedの例では、常に10,000のモデルAと30,000のモデルBがあることを意味します(データベースの一意の行数が3であっても)。select_atedプロセスは、親モデルごとに新しいものを生成します 子モデル)。そして60,000のモデルC。

1行目、10行目、100行目などが同じモデルBを共有していて、それらのいずれかを更新した場合、同じサブアイテムを共有している残りの行はすぐには更新されません。 変更をデータベースに保存した場合、変更は再読み込み(各サブアイテムを個別に読み込むか、クエリ全体を再度読み込む)後にのみ使用できます。これは、各サブオブジェクトが新しいオブジェクトとしてインスタンス化されるため、select_atedがより多くのメモリを使用することを意味します

  • 明らかに自分の空間を利用した。

!!!注キャッシングの導入を決定した場合、これは将来のバージョンで変更される可能性があります。

!!!警告デフォルトでは、すべてのサブアイテム(または同じモデルが2回以上読み込まれるイベント)は、データベース内の同じ行を表していますが、完全に独立した別のPythonオブジェクトです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
比較するとTrueと評価されるので、上記の例では:

```python
# will return True if child1 of both rows is the same child db row 
row1.child1 == row100.child1

# same here:
model1 = await Model.get(pk=1)
model2 = await Model.get(pk=1) # same pk = same row in db
# will return `True`
model1 == model2
```

but

```python
# will return False (note that id is a python `builtin` function not ormar one).
id(row1.child1) == id(ro100.child1)

# from above - will also return False
id(model1) == id(model2)
```

代わりに、prefetch_atedを使用すると、一意の異なるサブモデルはそれぞれ1回だけインスタンス化され、同じサブモデルがすべての親モデル間で共有されます。 これは、上記のprefetch_atedの例で、テーブルBに3つの異なるモデルがあり、テーブルCに2つの異なるモデルがある場合、すべてのモデルAインスタンス間で5つのサブネストされたモデルのみが共有されることを意味します。 また、プロパティを更新すると、同じ子オブジェクトを共有しているため、すべての親オブジェクトがプロパティを更新します。

モデルメソッド

各モデルインスタンスには、それ自体を保存、更新、または読み込むための一連のメソッドがあります。

ロード

load()メソッドを呼び出すことで、ForeignKey関連のモデルを読み込むことができます。

load()は、他のプロセスによって変更された場合、データベースからモデルを更新するために使用できます。

!!!ヒントモデルメソッドのload()メソッドの詳細を読む

QuerysetProxyメソッド

関連するManyTomanyフィールドとReverseForeignKeyに直接アクセスする場合は、関連するモデルのリストを返します。

ただし、QuerySet APIのサブセットを公開しているため、親モデルから直接フィルタリング、作成、関連モデルの選択などを行うことができます。

関連を選択

上記のselect_ated関数と全く同じですが、関係の反対側から関連オブジェクトを取得することができます。

!!!ヒントQuerysetProxyの詳細については、querysetproxyセクションにアクセスしてください

すべて選択

上記のselect_all関数と全く同じですが、関係の反対側から関連オブジェクトを取得することができます。

!!!ヒントQuerysetProxyの詳細については、querysetproxyセクションにアクセスしてください

プリフェッチ関連

上記のprefetch_ated関数と全く同じですが、関係の向こう側から関連するオブジェクトを取得することができます。

!!!ヒントQuerysetProxyの詳細については、querysetproxyセクションにアクセスしてください