2012-07-27 55 views
3

繼子關係,我也有類似如下的多表繼承模式:與select_related

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

由於性能原因,我使用select_related(深度= 2)每當我取一個大的選擇NODEDATA元素。然而,select_related不遵循關係到子類,所以下面的代碼*結果到ConceptNode執行myFunction的與該函數用於ConceptNode對象沒有預取的數據呼叫:

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

這使得select_related不是很有用,因爲有很多這樣調用的函數,並且它們沒有獲得select_related緩存的數據的好處。

我的問題是:我才能select_related獲取對於​​我這個信息,讓每個NODEDATA對象,我得到了這兩個節點和ConceptNode/DerivedNode例如緩存*

注:這實際上是我試圖做的是類似於this,但有一點不同,因爲我有多個子類:使用訪問函數模型就像我問here

注完成。

編輯:感謝來自克里斯 - 普拉特的文章中,我發現下面做什麼,我需要:

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

第一行預取的DerivedNode或ConceptNode實例到相關緩存對象。

回答

2

如果您執行的操作類似ConceptNode.objects.get(...),則會同時提取Node實例,以便您擁有實例的完整數據。但是,當您執行Node.objects.get(...)之類的操作時,ConceptNode等子類的數據爲而不包括。要選擇這些,你需要使用select_related(<related_name>),並獲得多個子類,你只是不斷添加related_name S作爲一個逗號分隔的列表,如:

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

謝謝克里斯 - 你的迴應讓我朝着正確的方向前進。提供爲多表繼承定義的隱式定義的related_name字段。 – turtlemonvh 2012-07-29 16:11:28

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通常看看它是否得到你想要的。

+0

謝謝克里斯。我想我的問題可能會讓你感到困惑,因爲你和@ chris-platt都以同樣的方式解釋它。我不需要獲取特定節點的所有子節點,如果節點也是ConceptNode或DerivedNode,我只想預取DerivedNode或ConceptNode實例。 – turtlemonvh 2012-07-29 16:18:09

+0

問題是沒有辦法知道'Node'的子類是什麼,直到它已經從數據庫中獲取並且所有的類都被實例化之後。這就是爲什麼'select_related'的所有可能性,然後當你需要時,適當的一個將會在那裏。 – 2012-07-30 14:30:28

+0

@ chris-platt,你完全正確。這就是我在解決方案中引用derivednode和conceptnode的原因。 – turtlemonvh 2012-07-31 14:43:31