跟随select_related的子类关系

时间:2012-07-27 20:37:34

标签: django django-models django-orm

我有一个类似于以下内容的多表继承架构:

class NodeData(models.Model):
    node = models.ForeignKey(Node, db_index = True)
    value = models.FloatField(default = 0)
    name = models.TextField(unique=True, blank=True)

class Node(models.Model):
    name = models.CharField(max_length=50, blank=True)
    description = models.TextField(blank=True)
    node_tree = models.ForeignKey(NodeTree, db_index = True)
    unique_name = models.TextField(unique=True, blank=True)
    last_updated_timestamp = models.DateTimeField('date last updated', blank=True)

class ConceptNode(Node):
    node_parent = models.ForeignKey(Node, related_name="nodeParent", null=True, blank=True)

class DerivedNode(Node):
    node_source = models.ForeignKey(Node, related_name="nodeSource")
    node_target = models.ForeignKey(Node, related_name="nodeTarget")

出于性能原因,每当我获取大量NodeData元素时,我都在使用select_related(depth = 2)。但是,select_related不遵循关系到子类,因此以下代码*导致调用myFunction的ConceptNode实现,而没有为该函数中使用的ConceptNode对象预取的数据:

nd = NodeData.objects.get(id = 1)
nd.node.conceptnode.myFunction()

这使得select_related不那么有用,因为有很多这样的函数被调用,并且它们没有从select_related缓存的数据中获益。

我的问题是:我可以获得select_related来为我获取此信息,以便我得到的每个NodeData对象都缓存了Node和ConceptNode / DerivedNode实例吗?*

注意:这实际上是使用我询问here

的访问函数模型完成的

注意:我尝试做的与this类似,但有点不同,因为我有多个子类。

编辑:感谢chris-platt的提示,我发现以下内容符合我的要求:

nd = NodeData.objects.select_related('conceptnode','derivednode').get(id = 1)
nd.node.conceptnode.myFunction()

第一行预先将DerivedNode或ConceptNode实例提取到相关的缓存对象中。

2 个答案:

答案 0 :(得分:2)

如果您执行ConceptNode.objects.get(...)之类的操作,则会同时获取Node实例,以便获得实例的完整数据。但是,当您执行Node.objects.get(...)之类的操作时,ConceptNode等子类中的数据不包含 。要选择这些,您需要使用select_related(<related_name>),并且要获得多个子类,您只需将related_name添加为逗号分隔列表,例如:

Node.objects.select_related('nodeParent', 'nodeSource', 'nodeTarget')

答案 1 :(得分:0)

我认为select_related的工作方式存在轻微的误解。它不适用于所有关系,它适用于外键或一对一关系 - 其中相关数据是单个记录,并且可以使用连接修改原始查询以获取它。所以这个例子不起作用:

nd = NodeData.objects.get(id = 1)
nd.node.conceptnode.myFunction()

...因为Node是ConceptNode的父级,所以它可以有许多相关的。事实上,我认为你的nd.node.conceptnode会出现属性错误。我想你可以通过以下方式访问这些孩子的ConceptNodes:

nd = NodeData.objects.get(id = 1)
nd.node.nodeParent.all() # Loop through this and do stuff with the children...

我在这里使用'nodeParent',因为你设置了related_name(你可能想把它改成更合适的东西,比如'nodeChildren'或其他东西),但默认情况下它会是'conceptnode_set ',例如:nd.node.conceptnode_set.all()

如果您正在使用Django 1.4,那么有一个名为[prefetch_related][1]的新QuerySet方法,我还没有使用过,但它应该是针对这种情况(减少对'many'类型关系的查询),你理论上应该能够做到这样的事情:

nd = NodeData.objects.get(id = 1).prefetch_related('conceptnode')

...虽然您必须使用'conceptnode'参数,并且通常使用prefetch_related来查看它是否能满足您的需求。

相关问题