在Django中,當你有一個父類和多個從它繼承的子類時,你通常會通過parentclass.childclass1_set或parentclass.childclass2_set訪問一個子類,但是如果我不知道具體子類的名稱I想?如何在不知道子類的名稱的情況下訪問django中的對象的子類?
有沒有辦法在不知道子類名的情況下在父 - >子方向獲取相關對象?
在Django中,當你有一個父類和多個從它繼承的子類時,你通常會通過parentclass.childclass1_set或parentclass.childclass2_set訪問一個子類,但是如果我不知道具體子類的名稱I想?如何在不知道子類的名稱的情況下訪問django中的對象的子類?
有沒有辦法在不知道子類名的情況下在父 - >子方向獲取相關對象?
(更新: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直接放入特定繼承層次結構中的父類。
如果您無法修改父級模型,此解決方案將不起作用。在這種情況下,你幾乎不能手動檢查所有的子類。
您可以實現此目的,查找父級中所有django.db.models.fields.related.RelatedManager實例的字段。從你的例子看來,你所講的子類不是子類。對?
不,不是子類..抱歉的混亂。 – debuggerpk 2012-03-14 18:44:25
在Python中,給定一個(「新風格」)類X,你可以得到它的(直接)子類X.__subclasses__()
,它返回一個類對象列表。 (如果你想要「更進一步的後代」,你還需要在每個直接子類等上調用__subclasses__
- 如果你需要關於如何在Python中有效地實現這一點的幫助,只需要問!)。
一旦你以某種方式確定了子類的利益(也許所有的人,如果你希望所有的孩子子類等的情況下),getattr(parentclass,'%s_set' % childclass.__name__)
應該幫助(如果該子類的名字是'foo'
,這就像訪問parentclass.foo_set
- 不多,不少)。再次,如果你需要澄清或例子,請問!
這是很棒的信息(我對__subclasses__不瞭解),但我相信這個問題實際上更多地體現在Django模型如何實現繼承。如果您查詢「父」表,您將返回父實例。它實際上可能是SomeChild的一個實例,但Django不會自動爲你提供(可能會很貴)。您可以通過Parent實例上的屬性來獲取SomeChild實例,但前提是您已經知道它是您想要的SomeChild,而不是Parent的某個其他子類。 – 2009-05-30 15:38:18
對不起,我說「可能_實際上是SomeChild的一個實例」並不清楚。您擁有的對象是Python中的父實例,但它可能在SomeChild表中有相關的條目,這意味着您可能更喜歡將它作爲SomeChild實例使用。 – 2009-05-30 15:42:55
卡爾的解決方案是一個很好的,這裏有一個方法做手工,如果有多個相關子類:
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的穩定不斷髮展,但它的確有訣竅,可以在需要時即時使用。
事實證明,我真正需要的是這樣的:
Model inheritance with content type and inheritance-aware manager
這已經完全爲我工作。不過感謝所有人。我讀了很多,只是看你的答案!
使用代理的替代方法可以在this blog post中找到。和其他解決方案一樣,它有它的好處和責任,這些都很好地結束了。
這是我的解決方案,它再次使用_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
您可以使用django-polymorphic。
它允許自動將派生類轉換回它們的實際類型。它還提供了Django管理支持,更高效的SQL查詢處理以及代理模型,內聯和formset支持。
基本原理似乎多次改造(包括W's的.specific
,或本文中概述的例子)。但是,需要更多的努力來確保它不會導致N-query問題,或者與管理員,表單集/內聯或第三方應用程序很好地集成。
@ S.Lott這類反應真的變老了。僅僅因爲你不能想到用例並不意味着提問者沒有。如果你使用任何類型的多態行爲的子類化(你知道,OOP的主要優點之一?),這個問題是一個非常自然而明顯的必要性。 – 2009-05-30 15:40:32
@ S.Lott在這種情況下,可以隨意練習一些非粗魯的版本,比如「我不確定我理解上下文,你能解釋一下你的用例嗎?」 – 2009-05-30 21:10:26