5
class Food_Tag(models.Model):
    name = models.CharField(max_length=200)
    related_tags = models.ManyToManyField('self', blank=True, symmetrical=False, through='Tag_Relation')

    def __unicode__(self):
     return self.name

class Tag_Relation(models.Model):
    source = models.ForeignKey(Food_Tag, related_name='source_set')
    target = models.ForeignKey(Food_Tag, related_name='target_set')
    is_a = models.BooleanField(default=False); # True if source is a target
    has_a = models.BooleanField(default=False); # True if source has a target

Food_Tags間の関係を次のように取得できるようにしたいと思います。

>>> steak = Food_Tag.objects.create(name="steak")
>>> meat = Food_Tag.objects.create(name="meat")
>>> r = Tag_Relation(source=steak, target=meat, is_a=True)
>>> r.save()
>>> steak.related_tags.all()
[<Food_Tag: meat>]
>>> meat.related_tags.all()
[]

しかし、related_tagsは肉の場合は空です。これは「symmetrical=False」引数と関係があることを理解していますが、「meat.related_tags.all()」が関連するすべてのFood_Tagを返すようにモデルを設定するにはどうすればよいですか?

4

5 に答える 5

6

ドキュメントで述べたように:

したがって、Djangoでは、(まだ?)余分なフィールドと対称的で再帰的な多対多の関係を持つことはできません。それは「2つ選ぶ」というちょっとした取引です。

于 2010-11-09T15:53:29.470 に答える
2

このDjangoの制限を克服するための良いアプローチのように思われる、CharlesLeiferによるこのアプローチを見つけました。

于 2015-04-13T16:25:19.070 に答える
1

非対称である必要があると明示的に言っていないので、最初に提案するのは設定symmetrical=Trueです。これにより、関係は説明したように双方向に機能します。eternicodeが指摘したようthroughに、M2M関係のモデルを使用している場合、これを行うことはできません。throughモデルなしで行く余裕がある場合はsymmetrical=True、説明した動作を正確に取得するように設定できます。

ただし、非対称のままにする必要がある場合は、キーワード引数related_name="sources"related_tagsフィールドに追加し(名前を変更しtargetsてわかりやすくすることを検討してください)、を使用して関連するタグにアクセスできますmeat.sources.all()

于 2010-11-09T07:48:44.180 に答える
0

対称関係を作成するには、次の2つのオプションがあります。

1)2つのTag_Relationオブジェクトを作成します。1つsteakはソースとして、もう1つsteakはターゲットとして作成します。

>>> steak = Food_Tag.objects.create(name="steak")
>>> meat = Food_Tag.objects.create(name="meat")
>>> r1 = Tag_Relation(source=steak, target=meat, is_a=True)
>>> r1.save()
>>> r2 = Tag_Relation(source=meat, target=steak, has_a=True)
>>> r2.save()
>>> steak.related_tags.all()
[<Food_Tag: meat>]
>>> meat.related_tags.all()
[<Food_Tag: steak]

Food_Tag2)モデルに別のManyToManyFieldを追加します。

class Food_Tag(models.Model):
    name = models.CharField(max_length=200)
    related_source_tags = models.ManyToManyField('self', blank=True, symmetrical=False, through='Tag_Relation', through_fields=('source', 'target'))
    related_target_tags = models.ManyToManyField('self', blank=True, symmetrical=False, through='Tag_Relation', through_fields=('target', 'source'))

class Tag_Relation(models.Model):
    source = models.ForeignKey(Food_Tag, related_name='source_set')
    target = models.ForeignKey(Food_Tag, related_name='target_set')

注意として、私はあなたのスルーモデルフィールドよりも説明的なものを使用しようとしsourceます。target

于 2015-05-04T17:32:09.317 に答える
0

この問題の最善の解決策は(多くの調査の後)、save()呼び出し時に対称のdbレコードを手動で作成することでした。もちろん、これにより、1つではなく2つのレコードを作成するため、DBデータの冗長性が生じます。あなたの例では、保存した後、次のようにTag_Relation(source=source, target=target, ...)逆の関係を保存する必要があります。Tag_Relation(source=target, target=source, ...)

class Tag_Relation(models.Model):
    source = models.ForeignKey(Food_Tag, related_name='source_set')
    target = models.ForeignKey(Food_Tag, related_name='target_set')
    is_a = models.BooleanField(default=False);
    has_a = models.BooleanField(default=False);

    class Meta:
        unique_together = ('source', 'target')

    def save(self, *args, **kwargs):
        super().save(*args, **kwargs)

        # create/update reverse relation using pure DB-level functions
        # we cannot just save() reverse relation because there will be a recursion
        reverse = Tag_Relation.objects.filter(source=self.target, target=self.source)
        if reverse.exists():
            reverse.update(is_a=self.is_a, has_a=self.has_a)
        else:
            Tag_Relation.objects.bulk_create([
                Tag_Relation(source=self.target, target=self.source, is_a=self.is_a, has_a=self.has_a)
            ])

この実装の唯一の欠点はTag_Relationエントリの複製ですが、これがすべて正常に機能することを除いて、InlineAdminでTag_Relationを使用することもできます。

UPDATEdelete逆の関係を削除するメソッドも 定義することを忘れないでください。

于 2016-10-14T11:37:31.670 に答える