Django 继承与永久链接

2 投票
3 回答
1150 浏览
提问于 2025-04-16 00:49

我正在用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 个回答

0

FooPage.objects.all() 这个命令会返回所有类型为 FooPage 的对象,这些对象实际上是数据库中 FooPageFooBlogFooGallery 表格的混合结果。为了获取正确的网址,你应该获取 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 成为一个真实的表格,你可以考虑把它设为一个抽象类。

1

你可以通过使用来自 django-model-utils 的 InheritanceManager 来避免添加内容类型字段。

然后,如果你在一个查询集上调用 .select_subclasses,它会将所有对象转换为子类对象,比如:

FooPage.objects.select_subclasses().all()
2

解决这个问题的经典方法是给父类添加一个内容类型(ContentType),用来存储这个实例对应的子类类型。这样一来,你就可以依赖一个一致的接口,它会返回正确类型的相关子类对象。

撰写回答