我正在尝试在django中建模树结构。这是我所得到的简化版本:
from django.db import models
class Node(models.Model):
parent = models.ForeignKey("Node", null=True)
name = models.CharField(max_length=20)
def child_cnt(self):
return self.node_set.count()
def __unicode__(self):
return self.name
到目前为止一切顺利。有用。但是,如果我现在开始创建这样的层次结构:
from ....models import Node
root = Node()
root.name = "ROOT"
root.parent = None
root.save()
n = Node()
print n.child_cnt()
>> 1
print n.node_set.all()
[<Node: ROOT>]
那么根节点在n
内作为子节点做了什么?我怎么能避免这种情况呢?
一旦我调用了n.save()
,问题就消失了,但是在管理站点内看到一个节点初始化为child_cnt
的情况有点令人讨厌。
答案 0 :(得分:1)
我知道这听起来很疯狂,但我花了几分钟试图解决你的问题而且它在我试过的任何尝试中都没有用。 无论如何,对于Tree ForeignKey,我找到了另一个解决方案Online,这是一个整个库来处理这种类型的密钥,https://github.com/django-mptt/django-mptt 你可以尝试一下,它可能适合你。我讨厌为这么小的东西添加一个库,但是我找不到更好的东西。
此外,如果你不想导入整个lib,你可以将TreeForeignKey带到你的代码中。
答案 1 :(得分:1)
好的,经过一些研究 - 结果如下:
首先,注意 - 当使用带有递归关系的ForeignKey时,你应该使用'self'而不是类名: https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.ForeignKey
然后 - ForeignKey是OneToOne关系。枚举其父节点时枚举Node子节点是不正确的... 回到问题 - 当你调用node_set.all()时 - 它似乎是作为Node.objects.all()执行的,这就是为什么它显示新创建的Node实例的项目(未保存在DB中)。实际上,此处不能使用node.node_set来获取Node的外键。为了让几个子节点到一个节点 - 你必须使用ManyToManyField,例如:
children = models.ManyToManyField('self', null=True)
然后使用:
调用它node.children.all() # for all children
或
node.children.count()
修改强>:
简要分析:
class A(model):
parent = ForeignKey(B)
然后
a = A()
b = B()
b.parent_set.all()
将返回所有将B作为父级的A,或者返回所有具有parent_id = b.id的A.在你的情况下,你实际上有:
a = A()
b = A()
b.parent_set.all()
将返回所有具有b(== a)作为父级的A,并且在这种情况下a.parent_id = b.id,但是b.id是None(未保存),因此您获得所有节点(b) has parent_id =无。
实际上,对于非递归关系,这种行为也是一样的。在第一个例子中,A()保存为parent = None,你将在b.parent_set.all()中获取所有未保存的B。