如何在 Django 中使具有额外字段对称的递归 ManyToManyField 关系?
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 对于肉来说是空的。我意识到这与 'symmetry=False' 参数有关,但是如何设置模型以使 'meat.lated_tags.all()' 返回所有相关的 Food_Tags?
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:
>>> 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. 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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
正如中提到的文档:
因此,在 Django 中,(还?)不可能与额外字段建立对称的、递归的多对多关系。这是一个“二选一”的交易。
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. It's a "pick two" sorta deal.
我发现 Charles Leifer 提出的这种方法似乎是一个很好的方法克服 Django 限制的方法。
I found this approach made by Charles Leifer which seems to be a good approach to overcome this Django limitation.
由于您没有明确表示它们需要不对称,因此我建议的第一件事是设置正如 eternicode 所指出的,当您使用symmetry=True
。这将导致关系按照您所描述的方式双向工作。through
模型来建立 M2M 关系时,您无法执行此操作。如果您可以不使用through
模型,则可以设置symmetry=True
来准确获得您所描述的行为。但是,如果它们需要保持不对称,您可以将关键字参数
lated_name="sources"
添加到lated_tags
字段(您可能需要考虑将其重命名为targets
让事情更清楚),然后使用meat.sources.all()
访问相关标签。Since you didn't explicitly say that they need to be asymmetrical, the first thing I'll suggest is settingAs eternicode pointed out, you can't do this when you're using asymmetrical=True
. This will cause the relation to work both ways as you described.through
model for the M2M relationship. If you can afford to go without thethrough
model, you can setsymmetrical=True
to get exactly the behavior you describe.If they need to remain asymmetrical however, you can add the keyword argument
related_name="sources"
to therelated_tags
field (which you might want to consider renaming totargets
to make things more clear) and then access the related tags usingmeat.sources.all()
.要创建对称关系,您有两种选择:
1) 创建两个
Tag_Relation
对象 - 一个以steak
作为源,另一个以steak
作为源作为目标:2)向
Food_Tag
模型添加另一个 ManyToManyField:作为注释,我会尝试使用比
source
和target
更具描述性的内容code> 用于您的模型字段。To create a symmetrical relationship, you have two options:
1) Create two
Tag_Relation
objects - one withsteak
as the source, and another withsteak
as the target:2) Add another ManyToManyField to the
Food_Tag
model:As a note, I'd try to use something more descriptive than
source
andtarget
for your through model fields.这个问题的最佳解决方案(经过多次调查)是在
save()
调用上手动创建对称数据库记录。当然,这会导致数据库数据冗余,因为您创建了 2 条记录而不是 1 条。在您的示例中,保存Tag_Relation(source=source, target=target, ...)
后,您应该保存反向关系Tag_Relation(source=target, target=source, ...)< /code> 像这样:
此实现的唯一缺点是重复
Tag_Relation
条目,但除此之外一切正常,您甚至可以在 InlineAdmin 中使用 Tag_Relation。更新
不要忘记定义删除反向关系的方法。
The best solution of this problem (after many investigations) was to manually create symmetrical db record on
save()
call. This results in DB data redundancy, of course, because you create 2 records instead of one. In your example, after savingTag_Relation(source=source, target=target, ...)
you should save reverse relationTag_Relation(source=target, target=source, ...)
like this: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.UPDATE
Do not forget to define
delete
method as well which will remove reverse relation.