次のような抽象基本クラスがあるとしましょう。
class StellarObject(BaseModel):
title = models.CharField(max_length=255)
description = models.TextField()
slug = models.SlugField(blank=True, null=True)
class Meta:
abstract = True
ここで、StellarObjectから継承する2つの実際のデータベースクラスがあるとしましょう。
class Planet(StellarObject):
type = models.CharField(max_length=50)
size = models.IntegerField(max_length=10)
class Star(StellarObject):
mass = models.IntegerField(max_length=10)
ここまでは順調ですね。惑星や星を取得したい場合、私がすることはこれだけです:
Thing.objects.all() #or
Thing.objects.filter() #or count(), etc...
しかし、すべてのStellarObjectsを取得したい場合はどうなりますか?私が行った場合:
StellarObject.objects.all()
もちろん、抽象クラスは実際のデータベースオブジェクトではないため、クエリを実行できないため、エラーが返されます。私が読んだことはすべて、惑星と星にそれぞれ1つずつ、合計2つのクエリを実行してからそれらをマージする必要があると言っています。それはひどく非効率的なようです。それが唯一の方法ですか?
根本的に、これはオブジェクトとリレーショナルデータベース間の不一致の一部です。 ORMは違いを抽象化するのに素晴らしい仕事をしますが、とにかくそれらに直面することもあります。
基本的に、2つのクラス間にデータベース関係がない抽象継承、またはクエリごとに効率(追加のデータベース結合)を犠牲にしてデータベース関係を維持するマルチテーブル継承のいずれかを選択する必要があります。
抽象基本クラスを照会することはできません。マルチテーブル継承の場合、_Django-model-utils
_を使用できます。これはInheritanceManager
であり、標準のQuerySet
をselect_subclasses()
メソッドで拡張します。これにより、必要に応じて正しく実行されます。 -継承されたすべてのテーブルを結合し、各行に適切な型インスタンスを返します。
ベースでクエリを実行する必要がある場合は、抽象基本クラスを使用しないでください。代わりに、具象基本クラスを使用してください。
これは、モデルの多形の例です(多形-1つの多くの形式)。
1つまたは2つの場所にあるif-elseコードを少し使用するために、手動で処理するだけです-開発/メンテナンスの点でおそらくはるかに迅速で明確になります(つまり、これらのクエリが深刻な打撃を与えない限り、おそらく価値があります)あなたのデータベース-それはあなたの判断の呼びかけであり、状況に依存します)。
幸いなことに、ジャンゴにはポリモーフィズムを処理するためのライブラリがあります Django-polymorphic -これらのドキュメントはこれを正確に行う方法を示しています。これはおそらく、あなたが説明したように簡単にクエリを実行するための「正しい答え」です。特に、多くの場所でモデルの継承を行いたい場合はそうです。
この種には上記の両方の欠点がありますが、過去にこれを使用して、両方のタイプのモデルを含む1つのクエリセットオブジェクトを持つという利点を維持しながら、複数のクエリセットからすべての圧縮を自動的に実行しました。
チェックアウト Django-querysetsequence 複数のクエリセットのマージを管理します。
Django-polymorphic
ほどサポートされておらず、安定していませんが、それでも言及する価値があります。
この場合、他に方法はないと思います。
最適化のために、抽象StellarObject
からの継承を回避し、FKを介してStar
およびPlanet
オブジェクトに接続された個別のテーブルとして使用できます。
そうすれば、両方ともieを持ちます。 star.stellar_info.description
。
他の方法は、情報を処理し、many2many関係でStellarObjectをthrough
として使用するためのモデルを追加することです。
個別のサブクラスの動作をそれぞれの子クラスに基づいてオブジェクトに結び付けることを検討している場合は、抽象継承パターンまたは具体的な基本パターンのいずれかから離れることを検討します。
親クラスを介してクエリを実行すると(実行したいように聞こえます)Djangoは、結果のオブジェクトを親クラスのオブジェクトとして扱うため、子クラスレベルのメソッドにアクセスするには、 -オブジェクトをその場で「適切な」子クラスにキャストして、それらのメソッドを表示できるようにします...その時点で、親クラスレベルのメソッドにぶら下がっている一連のifステートメントは間違いなくよりクリーンなアプローチになります。
上記のサブクラスの動作が問題にならない場合は、生のSQLを介してモデルをまとめる抽象基本クラスにアタッチされたカスタムマネージャーを検討できます。
主に同一のデータフィールドの個別のセットをオブジェクトの束に割り当てることに関心がある場合は、bx2が示唆するように、外部キーに沿って関連付けます。
それはひどく非効率的なようです。それが唯一の方法ですか?
私の知る限り、DjangoのORMを使用する唯一の方法です。現在実装されているように、抽象クラスは、クラスの共通属性をスーパークラスに抽象化するための便利なメカニズムです。 ORMは、クエリに対して同様の抽象化を提供しません。
データベースに階層を実装するには、別のメカニズムを使用することをお勧めします。これを行う1つの方法は、単一のテーブルを使用し、typeを使用して行に「タグ付け」することです。または、プロパティを保持する別のモデルに汎用外部キーを実装することもできます(後者は私にも正しく聞こえません)。