5

(ジャンゴ 1.x、パイソン 2.6.x)

私は次のようなモデルを持っています:

class Animal(models.Model):
  pass

class Cat(Animal):
  def __unicode__(self):
    return "This is a cat"

class Dog(Animal):
  def __unicode__(self):
    return "This is a dog"

class AnimalHome(models.Model):
  animal = models.ForeignKey(Animal)

これは仮想クラスであるはずなので、Animals をインスタンス化していません。私は Cats と Dogs をインスタンス化しましたが、AnimalHome の管理ページでは、動物の選択は、2 つのサブクラスに対して定義した __unicode__ ではなく、"Animal オブジェクト" (デフォルトの __unicode__() だと思います) として表示されます。ヘルプ。


抽象基本クラスの問題は、この質問に対する赤いニシンだと思います。Animal が抽象化されていなかったとしても、何らかの理由で、ForeignKey が Animal で定義されており、そのサブクラスの 1 つではなく、サブクラスの代わりにスーパークラス メソッドが呼び出されているという問題があります。OO プログラミングで object.method() を呼び出すと、最下位のサブクラスの実装を取得することになり、スーパークラスの実装を取得するには追加の作業を行う必要があります。では、サブクラスで __unicode__ を定義するだけでは十分ではないのはなぜですか --- 実際、問題は __unicode__ がまったく呼び出されないことかもしれません。だから多分アニマルに __unicode__ を定義してサブクラスを呼び出させると


わかりました、ORM の問題は理解できたと思います。これらの答えは両方とも、これを理解するのに役立ちました、ありがとう。これを試しているうちに、Django がサブクラス化されたモデルを保存するときに、(1) サブクラス化されたオブジェクトの行をスーパークラスのテーブルに作成し、(2) サブクラス テーブルの PK をスーパークラス テーブルで割り当てられた PK。サブクラス テーブル内のこの PK は、superclass_ptr という名前です。これに基づいて、私は以下を作成しました。フィードバックをいただければ幸いです。

Class Animal(models.Model)
  def __unicode__(self):
    if Dog.objects.filter(pk=self.pk).count() > 0:
      return unicode(Dog.objects.get(pk=self.pk))
    elif Cat.objects.filter(pk=self.pk).count() > 0:
      return unicode(Cat.objects.get(pk=self.pk))
    else:
      return "An Animal!"

ローレンスは、この質問に関して最も適切であるようです。Cat と Dog はばらばらの PK セットを持ちます (そして、Animal のサブクラスはスーパークラスのレコードと同じ PK を持ちます) が、残念ながら Django は舞台裏で何も実行しません。動物には犬と猫のサブクラスがあります。具体的には、私は動物番号 3 で、さらに確認したところ、猫番号 3 もあります。つまり、私は実際には猫番号 3 です」. Python のイントロスペクションを使用すると、これは完全に可能であり、非常に合理的であるように見えますが (猫は動物自身ができないことは何もしないため)。皆さん、ありがとうございました。

4

6 に答える 6

6

ForeignKey(Animal) は、Animal テーブル内の行への外部キー参照です。テーブルがスーパークラスとして使用されていることを示す基礎となる SQL スキーマには何もないため、Animal オブジェクトが返されます。

これを回避するには:

まず、基本クラスを非抽象にする必要があります。いずれにせよ、これは ForeignKey に必要であり、Dog と Cat が分離した主キー セットを持つことも保証します。

現在、Django は OneToOneField を使用して継承を実装しています。このため、サブクラス インスタンスを持つ基底クラスのインスタンスは、適切な名前のそのインスタンスへの参照を取得します。これは、次のことができることを意味します。

class Animal(models.Model):
    def __unicode__(self):
        if hasattr(self, 'dog'):
            return self.dog.__unicode__()
        elif hasattr(self, 'cat'):
            return self.cat.__unicode__()
        else:
            return 'Animal'

これは、他のサブクラス属性に依存するunicode ()に関する Ber への質問にも答えます。実際には、サブクラス インスタンスで適切なメソッドを呼び出しています。

さて、これは、Django が既に裏でサブクラスのインスタンスを探しているため、コードが最後まで進んで、Animal の代わりに Cat または Dog インスタンスを返す可能性があることを示唆しています。開発者とその質問を取り上げる必要があります。:)

于 2008-11-24T16:40:56.197 に答える
6

Abstract 基本クラスが必要です(「仮想」は Python では何の意味もありません)。

ドキュメントから:

class CommonInfo(models.Model):
    name = models.CharField(max_length=100)
    age = models.PositiveIntegerField()

    class Meta:
        abstract = True

編集

「オブジェクト指向プログラミングでは、object.method() を呼び出すと、最下位のサブクラスの実装を取得することになっています。」

真実。しかし、すべてではありません。

これは OO の問題ではありません。あるいは、Python や Django の問題ですらあります。これは ORM の問題です。

問題は、「FK 参照の最後に再構築されるオブジェクトは何ですか?」ということです。その答えは、FK 値からオブジェクトへの変換を処理する方法について、標準的で明白な答えがないということです。

値が 42の行がAnimalHomeあります。動物のどのサブクラス? ネコ?犬?ORM レイヤーは、実行すべきかどうかをどのように認識しますか?animalsAnimal.objects.get(pk=42)Dog.objects.get(pk=42)Cat.objects.get(pk=42)

「でも待って」とあなたは言います。「犬や猫のオブジェクトではなく、動物のオブジェクトを取得する必要があります。」それは期待できますが、Django ORM の仕組みはそうではありません。各クラスは別個のテーブルです。Cat と Dog は、定義上、別個のクエリを持つ別個のテーブルです。オブジェクト ストアを使用していません。リレーショナル テーブルに ORM を使用しています。


編集

まず、クエリは、Dog と Cat が共通のキー ジェネレーターを共有し、PK のセットが重複していない場合にのみ機能します。

PK 42 の犬と PK 42 の猫を飼っている場合、問題が発生します。また、鍵の生成を簡単に制御できないため、ソリューションは機能しません。

実行時の型識別が正しくありません。多くの点でオブジェクト指向ではありません。RTTI を回避するためにできるほとんどのことは、サブクラスを区別するための if ステートメントのシーケンスを拡大し続けるよりも優れています。

ただし、構築しようとしているモデルは、具体的には ORM システムの病理学的問題です。確かに、非常に具体的に病的なので、私はそれが宿題であることに賭けても構わないと思っています. [純粋な SQL システムにも病的な問題があります。彼らはしばしば宿題として現れます。]

問題は、ORM が実行すべきと考えていることを実行できないことです。したがって、2 つの選択肢があります。

  • Django の使用を停止します。
  • Django が直接行うことを行います。
  • オブジェクト指向の設計ガイドラインを破り、RTTI のようなもろいものに頼ります。これにより、動物の別のサブクラスを追加することが非常に困難になります。

RTTI を行うには、この方法を検討してください。これには、クラス名と PK が含まれます。

KIND_CHOICES = (
   ( "DOG", "Dog" ),
   ( "CAT", "Cat" ),
)

class Animal( models.Model ):
    kind = models.CharField( max_length= 1, choices=KIND_CHOICES )
    fk = models.IntegerField()
    def get_kind( self ):
        if kind == "DOG":
            return Dog.objects.get( pk = fk )
        elif kind == "CAT":
            return Cat.objects.get( pk = fk )
于 2008-11-23T23:23:44.277 に答える
3

Django (および一般的なリレーショナル データベース) は、この方法では機能しません。Django のような ORM を使用している場合でも、このようなクラス階層を操作することはありません。

この問題には、次の 2 つの解決策があります。

(1) Animal モデルに「name」属性を与え、['Dog', 'Cat'] の名前を持つエンティティを追加します。これにより、動物の名前が外部キー選択ボックスに表示されます。

(2)外部キーを異なるモデルにリンクする必要がある場合 (これは実際には RDBMS を使用する通常の方法ではありません) 、contenttypes フレームワークのドキュメントでGeneric Relationsについて読む必要があります。

ただし、私のアドバイスは(1)です。

于 2008-11-23T23:22:06.480 に答える
2

これは、S.Lott が提案した内容に沿っていますが、サポートする必要があるサブクラスの数が増えるにつれて、ますます扱いにくくなり、保守が難しくなる可能性がある if/elif/... がありません。

class Cat(models.Model):
    def __unicode__(self):
        return u'A Cat!'

class Dog(models.Model):
    def __unicode__(self):
        return u'A Dog!'        

class Eel(models.Model):
    def __unicode__(self):
        return u'An Eel!'        

ANIMALS = {
    'CAT': {'model': Cat, 'name': 'Cat'},
    'DOG': {'model': Dog, 'name': 'Dog'},
    'EEL': {'model': Eel, 'name': 'Eel'},
}
KIND_CHOICES = tuple((key, ANIMALS[key]['name']) for key in ANIMALS)

class Animal(models.Model):
    kind = models.CharField(max_length=3, choices=KIND_CHOICES)
    fk = models.IntegerField()
    def get_kind(self):
        return ANIMALS[self.kind]['model'].objects.get(pk=self.fk)
    def __unicode__(self):
        return unicode(self.get_kind())

Django のマルチテーブル継承でも非常によく似たことができます (Django のドキュメントを検索してください)。例えば:

ANIMALS = {
    'CAT': {'model_name': 'Cat', 'name': 'Cat'},
    'DOG': {'model_name': 'Dog', 'name': 'Dog'},
    'EEL': {'model_name': 'Eel', 'name': 'Eel'},
}
KIND_CHOICES = tuple((key, ANIMALS[key]['name']) for key in ANIMALS)

class Animal(models.Model):
    kind = models.CharField(max_length=3, choices=KIND_CHOICES)
    def get_kind(self):
        return getattr(self, ANIMALS[self.kind]['model_name'].lower())
    def __unicode__(self):
        return unicode(self.get_kind())

class Cat(Animal):
    def __unicode__(self):
        return u'A Cat!'

class Dog(Animal):
    def __unicode__(self):
        return u'A Dog!'        

class Eel(Animal):
    def __unicode__(self):
        return u'An Eel!'        

サブクラスのインスタンスには、親クラスで定義されたすべてのフィールドが自動的に魔法のように保持され、より明確で簡潔なコードが可能になるため、私は個人的には 2 番目のオプションを好みます。(たとえば、Animal クラスに「gender」フィールドがある場合、Cat.objects.filter(gender='MALE') が機能します)。

于 2008-11-24T12:28:56.733 に答える
1

Generic Relations に関しては、通常の Django クエリは GenerecForeignKey 関係にまたがることができないことに注意してください。複数テーブルの継承を使用すると、一般的ではないソリューションになる代わりに、この問題を回避できます。

ドキュメントから:

GenericForeignKey の実装方法により、このようなフィールドをデータベース API を介してフィルター (filter() や exclude() など) で直接使用することはできません。それらは通常のフィールド オブジェクトではありません。これらの例は機能しません:

# This will fail
>>> TaggedItem.objects.filter(content_object=guido)
# This will also fail
>>> TaggedItem.objects.get(content_object=guido)
于 2008-11-25T08:44:04.147 に答える
1

django コンテンツ フレームワークを使用できます

ここでモデルを実装する方法の例を作成しました->

https://github.com/jmg/django_content_types_example/blob/master/generic_models/models.py

そして、ここで orm の使い方を見ることができます ->

https://github.com/jmg/django_content_types_example/blob/master/generic_models/tests.py

于 2013-11-14T20:41:18.933 に答える