Django では、親クラスとそれを継承する複数の子クラスがある場合、通常は parentclass.childclass1_set または parentclass.childclass2_set を介して子にアクセスしますが、必要な特定の子クラスの名前がわからない場合はどうすればよいでしょうか?
子クラス名を知らなくても、親から子への関連オブジェクトを取得する方法はありますか?
Django では、親クラスとそれを継承する複数の子クラスがある場合、通常は parentclass.childclass1_set または parentclass.childclass2_set を介して子にアクセスしますが、必要な特定の子クラスの名前がわからない場合はどうすればよいでしょうか?
子クラス名を知らなくても、親から子への関連オブジェクトを取得する方法はありますか?
(更新: Django 1.2 以降では、逆の OneToOneField リレーション (したがってダウン継承階層) で select_related クエリをたどることができますreal_type
。親モデルに追加されたフィールドを必要としない、より優れた手法が利用可能です。 django-model-utilsプロジェクト。)
これを行う通常の方法は、適切な「リーフ」クラスのコンテンツ タイプを格納する Parent モデルの ContentType に ForeignKey を追加することです。これがないと、継承ツリーの大きさによっては、インスタンスを見つけるために子テーブルに対してかなりの数のクエリを実行する必要がある場合があります。1つのプロジェクトでそれを行った方法は次のとおりです。
from django.contrib.contenttypes.models import ContentType
from django.db import models
class InheritanceCastModel(models.Model):
"""
An abstract base class that provides a ``real_type`` FK to ContentType.
For use in trees of inherited models, to be able to downcast
parent instances to their child types.
"""
real_type = models.ForeignKey(ContentType, editable=False)
def save(self, *args, **kwargs):
if self._state.adding:
self.real_type = self._get_real_type()
super(InheritanceCastModel, self).save(*args, **kwargs)
def _get_real_type(self):
return ContentType.objects.get_for_model(type(self))
def cast(self):
return self.real_type.get_object_for_this_type(pk=self.pk)
class Meta:
abstract = True
これは、再利用可能にするために抽象基本クラスとして実装されます。これらのメソッドと FK を特定の継承階層の親クラスに直接配置することもできます。
親モデルを変更できない場合、このソリューションは機能しません。その場合、すべてのサブクラスを手動でチェックするのにかなり苦労します。
Python では、(「新しいスタイルの」) クラス X を指定するとX.__subclasses__()
、クラス オブジェクトのリストを返す を使用して、その (直接の) サブクラスを取得できます。(「さらに子孫」が__subclasses__
必要な場合は、直接の各サブクラスなども呼び出す必要があります。Python でそれを効果的に行う方法についてヘルプが必要な場合は、お問い合わせください!)。
関心のある子クラス (すべての子サブクラスのインスタンスが必要な場合など) を何らかの方法で特定したら、getattr(parentclass,'%s_set' % childclass.__name__)
役立つはずです (子クラスの名前が の場合、これはアクセスするの'foo'
と同じです。parentclass.foo_set
)。繰り返しますが、説明や例が必要な場合は、お尋ねください!
カールの解決策は良いものです。複数の関連する子クラスがある場合に手動で行う方法の 1 つを次に示します。
def get_children(self):
rel_objs = self._meta.get_all_related_objects()
return [getattr(self, x.get_accessor_name()) for x in rel_objs if x.model != type(self)]
これは _meta の関数を使用します。これは、django が進化するにつれて安定することが保証されていませんが、必要に応じてオンザフライで使用できます。
私が本当に必要としていたのはこれであることがわかりました:
それは私にとって完璧に機能しました。他の皆様のお陰ですが。あなたの回答を読んだだけでとても勉強になりました!
そのためにdjango-polymorphicを使用できます。
派生クラスを実際の型に自動的にキャストできます。また、Django 管理サポート、より効率的な SQL クエリ処理、およびプロキシ モデル、インラインおよびフォームセット サポートも提供します。
基本原則は何度も再発明されているようです (Wagtail の.specific
や、この投稿で概説されている例を含む)。ただし、N クエリの問題が発生しないことを確認したり、管理者、フォームセット/インライン、またはサードパーティのアプリとうまく統合したりするには、より多くの労力が必要です。
これが私の解決策です。これも使用する_meta
ため、安定しているとは限りません。
class Animal(models.model):
name = models.CharField()
number_legs = models.IntegerField()
...
def get_child_animal(self):
child_animal = None
for r in self._meta.get_all_related_objects():
if r.field.name == 'animal_ptr':
child_animal = getattr(self, r.get_accessor_name())
if not child_animal:
raise Exception("No subclass, you shouldn't create Animals directly")
return child_animal
class Dog(Animal):
...
for a in Animal.objects.all():
a.get_child_animal() # returns the dog (or whatever) instance
プロキシを使用した別のアプローチについては、このブログ投稿を参照してください。他のソリューションと同様に、それには利点と欠点があり、投稿の最後に非常にうまく記載されています.
これは、django.db.models.fields.related.RelatedManager のインスタンスである親のすべてのフィールドを探すことで実現できます。あなたの例から、あなたが話している子クラスはサブクラスではないようです。右?