【问题标题】:Django Inheritance and PermalinksDjango 继承和永久链接
【发布时间】:2026-01-09 13:30:02
【问题描述】:

我正在 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 模块时,如何在不重写代码的情况下完成这项工作?我想确保这适用于任何可能的模块。

谢谢,

  • 莱克索

【问题讨论】:

  • 奇怪的是它不使用子类函数来返回 url(它确实在列表中返回 FooBlog 和 FooGallery,对).. 也许您可能需要设置 FooBlog 和 @ 987654328@ 类作为代理模型。当您为模型保留相同的数据库时使用代理模型,但您想调整诸如排序之类的东西,也许还有get_absolute_url 功能。查看它们:docs.djangoproject.com/en/1.2/topics/db/models/#id8 -- 如果这对你有用,请告诉我。
  • 感谢您的信息。我会研究代理模型。
  • 我不认为代理模型是我需要的。 FooImage 等各个子类需要附加额外的字段。抽象类是完美的,但我需要 FooPages 和 FooSubitems 之间的父/子关系存在,并且您不能在抽象类上指定外键。

标签: python django inheritance subclass permalinks


【解决方案1】:

这个问题的经典解决方案往往是adding a ContentType to the superclass which stores the type of subclass for that instance。通过这种方式,您可以依赖返回适当类型的相关子类对象的一致 API。

【讨论】:

  • 这正是我想要的。我最终修改了链接的 cmets 中描述的“QuerySet Hack”。谢谢!
【解决方案2】:

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

然后,如果您在查询集上调用.select_subclasses,它将向下转换所有对象,例如:

FooPage.objects.select_subclasses().all()

【讨论】:

    【解决方案3】:

    FooPage.objects.all() 返回所有FooPage 类型的对象,这些对象将混合 FooPage、FooBlog、FooGallery 的底层数据库表行。要获取正确的 URL,您应该获取 FooBlog 或 FooGallery 对象,例如

    page.fooblog.get_absolute_url()
    

    如果页面只是一个页面对象,即通过 FooPage 创建的,它可能会抛出 FooBlog.DoesNotExist 错误,所以要获得正确的 url,你可以这样做

       urls = []
       for page in FooPage.objects.all():
            try:
                page = page.fooblog
            except FooBlog.DoesNotExist:
                pass
    
                urls.append(page.get_absolute_url())
    

    如果您不希望 FooPage 成为真正的表格,您也可以尝试将 FooPage 设为抽象类。

    【讨论】:

    • 谢谢阿努拉格。我的问题是我不知道我将安装或将来可能安装哪些模块应用程序(例如 FooBlog、FooGallery)。如果我最终创建了 10 个或更多不同的应用程序(这是完全可能的),我不想重新编写我的代码来包含对每种类型的 FooPage 的检查。另外,这似乎非常低效,而且我应该能够以编程方式完成。有没有办法“深入”到最具体的子类?
    • 另外,我尝试使用抽象类,但由于 FooSubitems 需要有 FooPage 父级,并且您不能为抽象类指定外键,所以它不起作用。
    • 也许你可以编写一个通用函数来做同样的事情,即尽可能尝试获取派生类对象,你可以使用自定义模型管理器 (djangoproject.com/documentation/models/custom_managers)