2009-05-30 74 views
70

在Django中,當你有一個父類和多個從它繼承的子類時,你通常會通過parentclass.childclass1_set或parentclass.childclass2_set訪問一個子類,但是如果我不知道具體子類的名稱I想?如何在不知道子類的名稱的情況下訪問django中的對象的子類?

有沒有辦法在不知道子類名的情況下在父 - >子方向獲取相關對象?

+75

@ S.Lott這類反應真的變老了。僅僅因爲你不能想到用例並不意味着提問者沒有。如果你使用任何類型的多態行爲的子類化(你知道,OOP的主要優點之一?),這個問題是一個非常自然而明顯的必要性。 – 2009-05-30 15:40:32

+79

@ S.Lott在這種情況下,可以隨意練習一些非粗魯的版本,比如「我不確定我理解上下文,你能解釋一下你的用例嗎?」 – 2009-05-30 21:10:26

回答

77

更新:Django的1.2及更高版本,它可以按照跨越反向OneToOneField關係select_related查詢(並由此向下繼承層次),有可用更好的技術,它不需要對母模型添加real_type場。django-model-utils項目中有InheritanceManager。)

通常的做法是在Parent模型中添加一個ForeignKey到ContentType,該模型存儲正確的「leaf」類的內容類型。如果沒有這個,你可能需要在子表上進行大量的查詢來查找實例,具體取決於你的繼承樹有多大。下面是我在一個項目中做到的:

from django.contrib.contenttypes.models import ContentType 
from django.db import models 

class InheritanceCastModel(models.Model): 
    """ 
    An abstract base class that provides a ``real_type`` FK to ContentType. 

    For use in trees of inherited models, to be able to downcast 
    parent instances to their child types. 

    """ 
    real_type = models.ForeignKey(ContentType, editable=False) 

    def save(self, *args, **kwargs): 
     if not self._state.adding: 
      self.real_type = self._get_real_type() 
     super(InheritanceCastModel, self).save(*args, **kwargs) 

    def _get_real_type(self): 
     return ContentType.objects.get_for_model(type(self)) 

    def cast(self): 
     return self.real_type.get_object_for_this_type(pk=self.pk) 

    class Meta: 
     abstract = True 

這是作爲一個抽象基類實現的,使它可以重用;您也可以將這些方法和FK直接放入特定繼承層次結構中的父類。

如果您無法修改父級模型,此解決方案將不起作用。在這種情況下,你幾乎不能手動檢查所有的子類。

0

您可以實現此目的,查找父級中所有django.db.models.fields.related.RelatedManager實例的字段。從你的例子看來,你所講的子類不是子類。對?

+0

不,不是子類..抱歉的混亂。 – debuggerpk 2012-03-14 18:44:25

17

在Python中,給定一個(「新風格」)類X,你可以得到它的(直接)子類X.__subclasses__(),它返回一個類對象列表。 (如果你想要「更進一步的後代」,你還需要在每個直接子類等上調用__subclasses__ - 如果你需要關於如何在Python中有效地實現這一點的幫助,只需要問!)。

一旦你以某種方式確定了子類的利益(也許所有的人,如果你希望所有的孩子子類等的情況下),getattr(parentclass,'%s_set' % childclass.__name__)應該幫助(如果該子類的名字是'foo',這就像訪問parentclass.foo_set - 不多,不少)。再次,如果你需要澄清或例子,請問!

+0

這是很棒的信息(我對__subclasses__不瞭解),但我相信這個問題實際上更多地體現在Django模型如何實現繼承。如果您查詢「父」表,您將返回父實例。它實際上可能是SomeChild的一個實例,但Django不會自動爲你提供(可能會很貴)。您可以通過Parent實例上的屬性來獲取SomeChild實例,但前提是您已經知道它是您想要的SomeChild,而不是Parent的某個其他子類。 – 2009-05-30 15:38:18

+1

對不起,我說「可能_實際上是SomeChild的一個實例」並不清楚。您擁有的對象是Python中的父實例,但它可能在SomeChild表中有相關​​的條目,這意味着您可能更喜歡將它作爲SomeChild實例使用。 – 2009-05-30 15:42:55

4

卡爾的解決方案是一個很好的,這裏有一個方法做手工,如果有多個相關子類:

def get_children(self): 
    rel_objs = self._meta.get_all_related_objects() 
    return [getattr(self, x.get_accessor_name()) for x in rel_objs if x.model != type(self)] 

它採用了_meta的功能,這是不保證的Django的穩定不斷髮展,但它的確有訣竅,可以在需要時即時使用。

0

使用代理的替代方法可以在this blog post中找到。和其他解決方案一樣,它有它的好處和責任,這些都很好地結束了。

2

這是我的解決方案,它再次使用_meta,因此不能保證穩定。

class Animal(models.model): 
    name = models.CharField() 
    number_legs = models.IntegerField() 
    ... 

    def get_child_animal(self): 
     child_animal = None 
     for r in self._meta.get_all_related_objects(): 
      if r.field.name == 'animal_ptr': 
       child_animal = getattr(self, r.get_accessor_name()) 
     if not child_animal: 
      raise Exception("No subclass, you shouldn't create Animals directly") 
     return child_animal 

class Dog(Animal): 
    ... 

for a in Animal.objects.all(): 
    a.get_child_animal() # returns the dog (or whatever) instance 
2

您可以使用django-polymorphic

它允許自動將派生類轉換回它們的實際類型。它還提供了Django管理支持,更高效的SQL查詢處理以及代理模型,內聯和formset支持。

基本原理似乎多次改造(包括W's的.specific,或本文中概述的例子)。但是,需要更多的努力來確保它不會導致N-query問題,或者與管理員,表單集/內聯或第三方應用程序很好地集成。

相關問題