Python Django-检查多表继承查询集的类型
我试图在我的数据库中保存一种目录结构。简化示例: models.pyPython Django-检查多表继承查询集的类型,python,django,database-design,Python,Django,Database Design,我试图在我的数据库中保存一种目录结构。简化示例: 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)
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类型,SectionHeading
将抛出DoesNotExist错误
我一直在寻找这类问题的替代方案,我正在浏览contenttypes包中的通用外键。然而,这似乎会在Django管理端引起更多的麻烦。有人能提出比上述更好的解决方案吗
编辑:由于
顺序
字段,我避免了抽象继承。我必须将两个查询集连接在一起,并按顺序对它们进行排序。您可以检查类型:
if isinstance(section, SectionHeading)
但一般都是首选
编辑:
事实上,这可能行不通。对象将是一个节
。但您可以查找该属性:
if hasattr(section, 'sectionheading')
或
你可以检查一下类型:
if isinstance(section, SectionHeading)
但一般都是首选
编辑:
事实上,这可能行不通。对象将是一个节
。但您可以查找该属性:
if hasattr(section, 'sectionheading')
或
我一直在使用类似于他编辑中建议的内容:
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
现在,这不是很好,但是它从一个中心位置返回子类实例,所以我不需要用
try
包装其他语句。也许可以避免对子类reverse manager名称进行硬编码,但这种方法已经满足了我的需要。我一直在使用类似于他编辑中建议的内容:
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
现在,这不是很好,但是它从一个中心位置返回子类实例,所以我不需要用
try
包装其他语句。也许可以避免对子类reverse manager名称进行硬编码,但这种方法足以满足我的需要。我提出的解决方案涉及一个额外字段,指向(相当有用的)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)
如果您使用的是“base”对象,我认为这个解决方案非常好用。我提出的解决方案涉及一个额外的字段,指向(非常有用的)
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)
如果您使用的是“基本”对象,我认为这个解决方案非常好,使用起来也很舒服。OP here
虽然second
的答案对这个问题是正确的,但我想补充一点,我认为多表继承对于这个场景来说是一种低效的方法。访问子类模型的属性将导致发生查询-因此需要对返回的每一行进行查询。哎哟据我所知,select\u 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)
把两个Queryset连在一起
#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.')
因此,在我的模板()中,我可以这样做:
- if s|is_instance:"SectionClickable"
%span {{s.title}}
- if s|is_instance:"SectionHeading"
%span{'style':'color: #{{s.color}};'}
{{s.title}}
结果是,我只使用了两个查询,一个用于获取可单击的节对象,另一个用于此处的节标题对象
虽然second
的答案对这个问题是正确的,但我想补充一点,我认为多表继承对于这个场景来说是一种低效的方法。访问子类模型的属性将导致发生查询-因此需要对返回的每一行进行查询。哎哟据我所知,select\u 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)
把两个Queryset连在一起
#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.')
因此,在我的模板()中,我可以这样做:
- if s|is_instance:"SectionClickable"
%span {{s.title}}
- if s|is_instance:"SectionHeading"
%span{'style':'color: #{{s.color}};'}
{{s.title}}
结果是我只使用了两个查询,一个用于获取可点击的节对象,另一个用于节标题对象如何关联节和节标题
s?节标题是节的子类如何关联节和节标题
s?节标题是节的子类