2008-11-23 94 views
4

(Django的1.x中,巨蟒的2.6.x)Django管理界面不会使用子类的__unicode __()

我有模型的调整:我没有实例化动物

class Animal(models.Model): 
    pass 

class Cat(Animal): 
    def __unicode__(self): 
    return "This is a cat" 

class Dog(Animal): 
    def __unicode__(self): 
    return "This is a dog" 

class AnimalHome(models.Model): 
    animal = models.ForeignKey(Animal) 

,因为这应该是一个虚拟的类。我已经实例化猫和狗,但在AnimalHome的管理页面中,我选择的动物显示为“动物对象”(我猜是默认的__unicode __()),而不是我为这两个子类定义的__unicode__。帮帮我。


抽象基类问题是一个红鲱鱼WRT对这个问题,我想。即使Animal不应该是抽象的,但我仍然有这样的问题:出于某种原因,由于ForeignKey是在Animal上定义的,而不是它的一个子类,所以超类方法被调用而不是子类。在调用object.method()时,在OO编程中,您应该获得最低子类的实现,并且您必须做额外的工作才能获得任何超类的实现。那么为什么在子类上定义__unicode__是不够的 - 实际上问题可能是__unicode__根本没有被调用,因为内省对Animal类的反映显示它没有被定义。所以也许如果我为Animal定义__unicode__并让它调用子类'__unicode__我可以得到想要的效果。


好吧,我认为我理解ORM问题。这两个答案帮助我理解了这一点,谢谢。在实验中,我发现当Django保存一个子类模型时,它会做两件事:(1)它为超类表中的子类对象创建一行;(2)它使得子类表中的PK与在超类表中分配的PK。子类表中的这个PK被命名为superclass_ptr。基于此,我编制了以下内容。我会很感激反馈。

Class Animal(models.Model) 
    def __unicode__(self): 
    if Dog.objects.filter(pk=self.pk).count() > 0: 
     return unicode(Dog.objects.get(pk=self.pk)) 
    elif Cat.objects.filter(pk=self.pk).count() > 0: 
     return unicode(Cat.objects.get(pk=self.pk)) 
    else: 
     return "An Animal!" 

看来,劳伦斯是最上点WRT这个问题。猫和狗将拥有不相交的PK集(动物的任何子类都将拥有与其超类记录相同的PK),但不幸的是Django并没有在幕后执行任何工作:“我是一只动物,我知道动物有狗和猫的亚类,具体来说,我是动物3号,而且我只查了一下,也有猫3号,这意味着我实际上是猫3号。尽管这看起来完全可能并且非常合理(因为猫不会做任何动物本身无法做到的事情),因为它使用Python的内省。谢谢你们。

+0

我不认为你应该这样编码一个Django模型。尝试以这样一种方式定义数据模型,即可以为所有动物使用一个表(即一个模型)。不要把决定因素放在你的代码中。 如果你把你的对象腌成一个数据库行,那么你得到的东西像一个对象存储。 – Ber 2008-11-24 07:44:21

回答

5

ForeignKey(Animal)就是这样一个对Animal表中一行的外键引用。底层SQL模式中没有任何内容表明该表正被用作超类,因此您可以获取一个Animal对象。

要解决此问题:

首先,你要在基类是非抽象的。无论如何,这对于ForeignKey是必要的,并且还确保Dog和Cat将具有不相关的主键集合。

现在,Django使用OneToOneField实现继承。因此,具有子类实例的基类实例获取对该实例的引用,并进行适当命名。这意味着你可以这样做:

class Animal(models.Model): 
    def __unicode__(self): 
     if hasattr(self, 'dog'): 
      return self.dog.__unicode__() 
     elif hasattr(self, 'cat'): 
      return self.cat.__unicode__() 
     else: 
      return 'Animal' 

这也回答了你的问题的误码率约的Unicode()这是依赖于其他子类的属性。你现在实际上在子类实例上调用适当的方法。

现在,这的确表明,自从Django的已经在寻找幕后子类的实例,代码可能只是走一路,并返回一个猫或狗实例,而不是动物。你必须向开发者提出这个问题。 :)

3

Django(和一般的关系数据库)不能这样工作。即使使用像Django这样的ORM,你也不能像这样使用类层次结构。

有你的问题,有两种可能的解决方案:

(1)给予“名” attibute的动物模型,然后从[“狗”,“猫”]名称添加的实体。这将在外键选择框中显示动物的名字。

(2)如果你真的需要链接你的外键不同型号(这确实是不使用的RDBMS通常的方式),你应该在上CONTENTTYPES框架的文档阅读Generic Relations

不过,我的建议是(1)。

+0

如果我使用你的第一个建议,我该如何克服Django没有使用子类的__unicode __()的事实?说狗有财产is_german_shepherd和狗的__unicode __()返回:“狗:(是德国牧羊犬?:%s)”%(“是”,如果is_german_shepherd其他“否”) – 2008-11-24 00:28:16

6

你想要一个Abstract base class( “虚拟” 并不意味着在Python什么。)

从文档:

class CommonInfo(models.Model): 
    name = models.CharField(max_length=100) 
    age = models.PositiveIntegerField() 

    class Meta: 
     abstract = True 

编辑

“在面向对象编程的时候你调用object.method()你应该得到最低子类的实现。“

是的。但不是全部。

这不是OO问题。甚至是Python或Django的问题。这是一个ORM问题。

问题是“什么对象在FK参考结束时重建?”答案是,如何处理从FK值到对象的转换没有标准的明显答案。

我已经在AnimalHome有一排,animals的值为42.它指的是Animal.objects.get(pk=42)。动物的哪个子类?猫?狗? ORM层如何知道它是否应该做Dog.objects.get(pk=42)Cat.objects.get(pk=42)

“但是等等,”你说。 “它应该抓取Animal对象,而不是Dog或Cat对象。”你可以期待这一点,但这不是Django ORM的工作原理。每个班级都是一个不同的表格。猫和狗 - 根据定义 - 分开的表格,带有单独的查询。你没有使用对象存储。您正在将ORM用于关系表。


编辑

首先,你的查询,如果狗和猫共用一个密钥生成器,并且没有一组重叠的PK年代才有效。

如果你有42 PK一只狗和与42 PK猫,你已经有了一个问题。由于您无法轻松控制密钥生成,因此您的解决方案无法工作。

运行时间类型识别不好。它不是以多种方式面向对象的。为避免RTTI,几乎所有可以做的事都比不断扩展的if语句序列来区分子类更好。

但是,你要建立模型 - 专 - 对ORM系统的病理问题。事实上,如此具体的病态,我几乎愿意打赌它是作业。 [纯SQL系统也存在病理问题。他们往往显示为作业。]

的问题是,ORM不能做你认为它应该做的。所以你有两个选择。

  • 停止使用Django。
  • 做一些Django直接做的事。
  • 歇OO设计指南和诉诸RTTI一样易碎的东西,这使得它非常难以增加动物的一个小类。

考虑这样做RTTI - 它包含类的名称,以及在PK

KIND_CHOICES = (
    ("DOG", "Dog"), 
    ("CAT", "Cat"), 
) 

class Animal(models.Model): 
    kind = models.CharField(max_length= 1, choices=KIND_CHOICES) 
    fk = models.IntegerField() 
    def get_kind(self): 
     if kind == "DOG": 
      return Dog.objects.get(pk = fk) 
     elif kind == "CAT": 
      return Cat.objects.get(pk = fk) 
+0

谢谢。新的python。 – 2008-11-24 00:28:58

2

这是一起什么美国洛特提出的方针,但没有如果/ elif的/ ...,随着您需要支持的子类数量的增长,这会变得越来越尴尬和难以维护。

class Cat(models.Model): 
    def __unicode__(self): 
     return u'A Cat!' 

class Dog(models.Model): 
    def __unicode__(self): 
     return u'A Dog!'   

class Eel(models.Model): 
    def __unicode__(self): 
     return u'An Eel!'   

ANIMALS = { 
    'CAT': {'model': Cat, 'name': 'Cat'}, 
    'DOG': {'model': Dog, 'name': 'Dog'}, 
    'EEL': {'model': Eel, 'name': 'Eel'}, 
} 
KIND_CHOICES = tuple((key, ANIMALS[key]['name']) for key in ANIMALS) 

class Animal(models.Model): 
    kind = models.CharField(max_length=3, choices=KIND_CHOICES) 
    fk = models.IntegerField() 
    def get_kind(self): 
     return ANIMALS[self.kind]['model'].objects.get(pk=self.fk) 
    def __unicode__(self): 
     return unicode(self.get_kind()) 

也可以用Django的多表继承(搜索Django的文档)完成一些非常相似的事情。例如:

ANIMALS = { 
    'CAT': {'model_name': 'Cat', 'name': 'Cat'}, 
    'DOG': {'model_name': 'Dog', 'name': 'Dog'}, 
    'EEL': {'model_name': 'Eel', 'name': 'Eel'}, 
} 
KIND_CHOICES = tuple((key, ANIMALS[key]['name']) for key in ANIMALS) 

class Animal(models.Model): 
    kind = models.CharField(max_length=3, choices=KIND_CHOICES) 
    def get_kind(self): 
     return getattr(self, ANIMALS[self.kind]['model_name'].lower()) 
    def __unicode__(self): 
     return unicode(self.get_kind()) 

class Cat(Animal): 
    def __unicode__(self): 
     return u'A Cat!' 

class Dog(Animal): 
    def __unicode__(self): 
     return u'A Dog!'   

class Eel(Animal): 
    def __unicode__(self): 
     return u'An Eel!'   

我个人比较喜欢第二种选择,因为每个子类的实例将所有的父类自动神奇,它允许更清晰,更简洁的代码定义的字段。 (例如,如果动物类有'性别'字段,那么Cat.objects.filter(gender ='MALE')将起作用)。

1

对于一般的关系,注意,普通的Django查询不能跨越GenerecForeignKey关系。使用多表继承,由于它是一个不太通用的解决方案的成本避免了这个问题。

从文档:

由于GenericForeignKey的实现方式,你不能直接使用的过滤器等领域(过滤器(),不包括()等)通过数据库API。他们是不正常的领域对象。这些例子是行不通的:

# This will fail 
>>> TaggedItem.objects.filter(content_object=guido) 
# This will also fail 
>>> TaggedItem.objects.get(content_object=guido)