2011-11-30 97 views
0

我试图在数据库中保存一种内容表结构。简单的例子:Django - 检查多表继承查询集的类型

models.py

class Section (models.Model): 
    title = models.CharField(max_length=80) 
    order = models.IntegerField() 

class SectionClickable(Section): 
    link = models.CharField(max_length=80) 

class SectionHeading(Section): 
    background_color = models.CharField(max_length=6) 

views.py

sections = Section.objects.filter(title="Hello!") 
for section in sections: 
     if(section.sectionheading): 
      logger.debug("It's a heading") 

我需要做一些处理操作,如果它是一个SectionHeading实例,但(如Django的手册),访问如果对象不是类型SectionHeading,则section.sectionheading将引发DoesNotExist错误。

我一直在寻找这类问题的替代品,并且我正在浏览contenttypes包中的通用外键。但是,这似乎会在Django Admin方面造成更多麻烦。任何人都可以提供比上述更好的解决方案吗?

编辑:由于order字段,我避免了抽象继承。我将不得不在两个查询集连接在一起,并通过订单

+0

你怎么关联'Section's和'SectionHeading's? – second

+0

SectionHeading是Section – bcoughlan

+0

的子类啊,道歉,没注意 – second

回答

2

以及对它们进行排序,你可以检查类型:

if isinstance(section, SectionHeading) 

duck typing一般最好

编辑:

实际,那可能不起作用。该对象将是一个Section。但你可以看看属性:

if hasattr(section, 'sectionheading') 

try: 
    do_something_with(section.sectionheading) 
except AttributeError: 
    pass # i guess it wasn't one of those 
0

我一直在使用类似于second在他编辑提示了一句:

class SomeBaseModel(models.Model): 
    reverse_name_cache = models.CharField(_('relation cache'), max_length=10, 
              null=True, editable=False) 

    def get_reverse_instance(self): 
     try: 
      return getattr(self, self.reverse_name_cache) 
     except AttributeError: 
      for name in ['sectionclickable', 'sectionheading']: 
       try: 
        i = getattr(self, name) 
        self.reverse_name_cache = name 
        return i 
       except ObjectDoesNotExist: 
        pass 

现在,这”不是个t非常漂亮,但它从中心位置返回子类实例,所以我不需要用try来包装其他语句。也许可以避免子类逆向管理器名称的硬编码,但这种方法足以满足我的需求。

1

我想出了利用该解决方案涉及指向(相当有用)的额外字段ContentType类:

class Section(models.Model): 
    name = models.CharField(max_length=50) 
    content_type = models.ForeignKey(ContentType,editable=False,null=True) 

    def __unicode__(self): 
     try: 
      return self.as_leaf_class().__unicode__() 
     except: 
      return self.name 

    def save(self, *args, **kwargs): 
     if(not self.content_type): 
      self.content_type = ContentType.objects.get_for_model(self.__class__) 
     super(Section, self).save(*args, **kwargs) 

    def as_leaf_class(self): 
     content_type = self.content_type 
     model = content_type.model_class() 
     if(model == Section): 
      return self 
     return model.objects.get(id=self.id) 

如果你想通过“基地”对象,我认为这个解决方案是相当不错的,并舒适的工作。

0

OP在这里。

尽管second的回答对于这个问题是正确的,但我想补充一点,我认为对于这种情况,多表继承是一种效率低下的方法。访问子类模型的属性将导致查询发生 - 因此需要对返回的每一行进行查询。哎哟。据我所知,select_related不适用于多表继承。

我也排除了ContentTypes,因为它不够高雅,而且似乎也需要很多查询。

我定居在使用抽象类:

class Section (models.Model): 
    title = models.CharField(max_length=80) 
    order = models.IntegerField() 

    class Meta: 
     abstract=True 
     ordering=['order'] 

所查询两个表:

section_clickables = SectionClickable.objects.filter(video=video) 
section_headings= SectionHeading.objects.filter(video=video) 

,并加入了两个查询集在一起

#Join querysets http://stackoverflow.com/questions/431628/how-to-combine-2-or-more-querysets-in-a-django-view 
s = sorted(chain(section_headings, section_clickables), key=attrgetter('order')) 

最后我做了一个模板标签检查实例:

from my.models import SectionHeading, SectionClickable 

@register.filter() 
def is_instance(obj, c): 
    try: 
     return isinstance(obj, eval(c)) 
    except: 
     raise ObjectDoesNotExist('Class supplied to is_instance could not be found. Import it in the template tag file.') 

,这样在我的模板(HamlPy)我可以这样做:

- if s|is_instance:"SectionClickable" 
    %span {{s.title}} 
- if s|is_instance:"SectionHeading" 
    %span{'style':'color: #{{s.color}};'} 
     {{s.title}} 

的结果是,我只用了两个疑问,一是得到了SectionHeading对象SectionClickable对象,一个