Django 继承与永久链接
我正在用Django创建一个简单的内容管理系统(CMS),里面有多个“模块”(每个模块都是一个Django应用)。我设置了以下模型:
class FooObject(models.Model):
id = models.SlugField(primary_key=True)
name = models.CharField(max_length=255)
creator = models.ForeignKey(auth.models.User, editable=False, related_name="createdby")
class FooPage(FooObject):
content = models.TextField(blank=True, null=True)
@models.permalink
def get_absolute_url(self):
return ('page', (), {'page_id':self.id}
class FooSubitem(FooObject):
parent = models.ForeignKey(FooPage, related_name='subitems')
在每个模块中,我创建了一个FooPage的子类,并且至少有一个FooSubitem的子类,比如:
# in FooBlog.models
class FooBlog(FooPage):
owner = models.ForeignKey(auth.models.User, editable=False)
@models.permalink
def get_absolute_url(self):
return ('blog', (), {'blog_id':self.id})
class FooPost(FooSubitem):
post_time = models.DateTimeField(auto_now_add=True)
还有
# in FooGallery.models
class FooGallery(FooPage):
location = models.CharField(max_length=255)
@models.permalink
def get_absolute_url(self):
return ('gallery', (), {'gallery_id':self.id})
class FooImage(FooSubitem):
image_file = models.ImageField(upload_to='foogallery')
这些只是简化的例子,但应该能让你大致明白我想做的事情。在FooPost和FooImage的管理界面中,我限制了父级选择列表,只能选择对应的父页面。
我的问题出现在我尝试在模板中使用这些内容时。在每个视图中,我有以下代码:
page_list = FooPage.objects.all()
这段代码返回了所有FooPages的列表,包括FooBlog和FooGallery类型的页面。但是,当我遍历这个列表时:
{% for page in page_list %}{{ page.get_absolute_url }}{% endfor %}
它返回的是'page'的URL模式,而不是'blog'或'gallery'的URL模式。
我该如何做到这一点,以便在以后想要添加FooCalendar模块时,不需要重写代码?我希望确保这能适用于任何可能的模块。
谢谢,
- Lexo
3 个回答
FooPage.objects.all()
这个命令会返回所有类型为 FooPage
的对象,这些对象实际上是数据库中 FooPage
、FooBlog
和 FooGallery
表格的混合结果。为了获取正确的网址,你应该获取 FooBlog
或者 FooGallery
的对象,比如:
page.fooblog.get_absolute_url()
如果页面只是一个简单的页面对象,也就是通过 FooPage
创建的,那么可能会出现 FooBlog.DoesNotExist
的错误。因此,为了获取正确的网址,你可以尝试这样做:
urls = []
for page in FooPage.objects.all():
try:
page = page.fooblog
except FooBlog.DoesNotExist:
pass
urls.append(page.get_absolute_url())
另外,如果你不想让 FooPage
成为一个真实的表格,你可以考虑把它设为一个抽象类。
你可以通过使用来自 django-model-utils 的 InheritanceManager 来避免添加内容类型字段。
然后,如果你在一个查询集上调用 .select_subclasses
,它会将所有对象转换为子类对象,比如:
FooPage.objects.select_subclasses().all()
解决这个问题的经典方法是给父类添加一个内容类型(ContentType),用来存储这个实例对应的子类类型。这样一来,你就可以依赖一个一致的接口,它会返回正确类型的相关子类对象。