子クラスの名前が分からない場合、django でオブジェクトの子クラスにアクセスするにはどうすればよいですか?
-
06-09-2019 - |
質問
Django では、親クラスとそれを継承する複数の子クラスがある場合、通常はparentclass.childclass1_set またはparentclass.childclass2_set を通じて子にアクセスしますが、必要な特定の子クラスの名前がわからない場合はどうすればよいでしょうか?
子クラス名が分からなくても、親→子の方向で関連オブジェクトを取得する方法はありますか?
解決
(アップデート:Django 1.2 以降では、OneToOneField リレーションの逆方向 (つまり、継承階層の下位) にわたって select_関連クエリを追跡できるため、追加を必要としない、より優れた手法が利用可能です。 real_type
親モデルのフィールド。として利用可能です 相続マネージャー の中に ジャンゴモデルユーティリティ プロジェクト。)
これを行う通常の方法は、適切な「リーフ」クラスのコンテンツ タイプを格納する親モデルの ContentType に、ForeignKey を追加することです。これを行わないと、継承ツリーの大きさに応じて、インスタンスを見つけるために子テーブルに対してかなりの数のクエリを実行する必要がある場合があります。あるプロジェクトで私がそれを行った方法は次のとおりです。
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 not 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__
を呼び出す必要があります)。
あなたが何らかの形で関心の子クラスを特定したら(多分すべてのそれらのすべての子のサブクラスなどのインスタンスをしたい場合は、)子クラスの名前がgetattr(parentclass,'%s_set' % childclass.__name__)
ある場合は、、'foo'
が、これは単にparentclass.foo_set
にアクセスするようなもので、(助けるべきです - - これ以上でもそれ以下)ありません。ここでも、あなたが明確化や例が必要な場合は、お問い合わせください!
カールのソリューションはここに関連する複数の子クラスが存在する場合、手動でそれを行うための一つの方法です、良いものです。
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)]
これは、Djangoの進化として安定であることが保証されていない_metaのアウト機能を使用していますが、それはトリック行い、オンザフライで使用することができます場合は必要とされます。
これは私が本当に必要なものにこれをしたことが判明します:
それは私のために完璧に取り組んできました。みんなのおかげで、しかし。私はちょうどあなたの答えを読んで多くのことを学びました!
あなたはそのためにジャンゴ多型に使用することができます。
これは、自動的に戻って自分の実際の型の派生クラスをキャストすることができます。また、Djangoの管理サポート、より効率的なSQLクエリの処理、およびプロキシモデル、インラインとフォームセットのサポートを提供しています。
基本的な原理は、(セキレイの.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のインスタンスである親のすべてのフィールドを探して達成することができます。あなたの例から、あなたが話している子クラスはサブクラスではないようです。右?
プロキシを使用して別のアプローチは、このブログで見つけることができますを投稿してください。他のソリューションと同様に、それは非常によく記事の最後に置かれている、その利点と負債を持っています。