简体   繁体   English

如何在Django中使递归的ManyToManyField关系具有对称的额外字段?

[英]How to make recursive ManyToManyField relationships that have extra fields symmetrical in Django?

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

I want to be able to get the relations between Food_Tags like: 我希望能够得到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()
[]

but related_tags is empty for meat. 但是related_tags对于肉是空的。 I realize this has to do with the 'symmetrical=False' argument, but how can I set up the model such that 'meat.related_tags.all()' returns all related Food_Tags? 我意识到这与'symmetrical = False'参数有关,但我怎样才能设置模型,使'meat.related_tags.all()'返回所有相关的Food_Tags?

As mentioned in the docs : 文档所述

Thus, it is not (yet?) possible to have a symmetrical, recursive many-to-many relationship with extra fields, in Django. 因此,在Django中,(还有?)可能与额外的字段具有对称的,递归的多对多关系。 It's a "pick two" sorta deal. 这是一个“挑选两个”的交易。

我发现Charles Leifer的这种方法似乎是克服这种Django限制的好方法。

Since you didn't explicitly say that they need to be asymmetrical, the first thing I'll suggest is setting symmetrical=True . 既然你没有明确说他们需要不对称,我建议的第一件事就是设置 symmetrical=True This will cause the relation to work both ways as you described. 这将导致关系以您描述的方式工作。 As eternicode pointed out, you can't do this when you're using a through model for the M2M relationship. 正如eternicode指出的那样,当您使用through模型进行M2M关系时,您无法做到这一点。 If you can afford to go without the through model, you can set symmetrical=True to get exactly the behavior you describe. 如果你能够没有through模型,你可以设置symmetrical=True来准确获得你描述的行为。

If they need to remain asymmetrical however, you can add the keyword argument related_name="sources" to the related_tags field (which you might want to consider renaming to targets to make things more clear) and then access the related tags using meat.sources.all() . 但是,如果它们需要保持不对称,则可以将关键字参数related_name="sources"related_tags字段(您可能需要考虑重命名为targets以使事情更加清晰),然后使用meat.sources.all()访问相关标记meat.sources.all()

To create a symmetrical relationship, you have two options: 要创建对称关系,您有两种选择:

1) Create two Tag_Relation objects - one with steak as the source, and another with steak as the target: 1)创建两个Tag_Relation对象 - 一个以steak为源,另一个以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]

2) Add another ManyToManyField to the Food_Tag model: 2)将另一个ManyToManyField添加到Food_Tag模型:

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')

As a note, I'd try to use something more descriptive than source and target for your through model fields. 作为一个注释,我会尝试使用比sourcetarget更具描述性的内容来完成您的直通模型字段。

The best solution of this problem (after many investigations) was to manually create symmetrical db record on save() call. 此问题的最佳解决方案(经过多次调查后)是在save()调用上手动创建对称db记录。 This results in DB data redundancy, of course, because you create 2 records instead of one. 当然,这会导致DB数据冗余,因为您创建了2条记录而不是1条记录。 In your example, after saving Tag_Relation(source=source, target=target, ...) you should save reverse relation Tag_Relation(source=target, target=source, ...) like this: 在您的示例中,在保存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)
            ])

The only disadvantage of this implementation is duplicating Tag_Relation entry, but except this everything works fine, you can even use Tag_Relation in InlineAdmin. 此实现的唯一缺点是重复Tag_Relation条目,但除此之外一切正常,您甚至可以在InlineAdmin中使用Tag_Relation。

UPDATE Do not forget to define delete method as well which will remove reverse relation. 更新不要忘记定义delete方法,这将删除反向关系。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何使递归多对多关系与 Django 对称 - How to make a recursive ManyToMany relationship symmetrical with Django 如何检查django模型ManyToManyField是否对称,如果对称=假? - How to check in django model ManyToManyField is symmetrical,if symmetrical=False? Django 1.7:如何使ManyToManyField成为必需? - Django 1.7: how to make ManyToManyField required? 如何根据字段的值设置额外的Django模型字段? - How to have extra django model fields depending on the value of a field? 在Django中,如何在没有显式查询的情况下从多对多关系中的额外字段中检索数据? - In Django, how do you retrieve data from extra fields on many-to-many relationships without an explicit query for it? django:在多对多关系中,额外字段上是否存在黑魔法? - django: is there a black magic on extra fields on many-to-many relationships? Django中ManyToManyField关系的主要关键问题 - Primary Key Issues with ManyToManyField Relationships in Django Django ManyToManyField没有保存M2M关系 - Django ManyToManyField not saving m2m relationships 如何在Django中创建涉及两个字段的ManyToManyField关系? - How do I create a ManyToManyField relation in Django that involves two fields? 如何在django中的manytomanyfield中添加字段? - How do I add together fields from a manytomanyfield in django?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM