指南:第三方模型(seoobject)

当需要添加 SEO 的模型来自第三方包(例如 django-oscar 的 Product 模型、某个 CMS 包的 Post 模型,或者任何无法添加 mixin 的模型)时,可以使用 seoobject contrib 应用。它借助 Django 的通用关系(generic relations),把 SEO 数据存放在一张独立的关联表中,供编辑人员管理。


何时使用 seoobject

以下情况适合使用 seoobject:

  • 模型位于第三方包中,你无法修改。
  • 你希望在不修改模型的前提下,让编辑人员在 Admin 中逐对象编辑 SEO。
  • 你希望采用同样的 Admin 可编辑方式,但自己的模型已经用了 SeoModelFieldsMixin。这种情况下,请直接使用内联字段。

对于你自己拥有的模型,优先选择 SeoModelMixin(自动生成,不增加任何字段)或 SeoModelFieldsMixin(供编辑人员编辑的字段)。seoobject 会在每次解析允许列表中的对象时额外执行一次 ContentTypes 查询。


安装

把两个应用都加入 INSTALLED_APPS:

INSTALLED_APPS = [
    "django.contrib.contenttypes",  # usually already present
    "seo_suite",
    "seo_suite.contrib.seoobject",
]

执行迁移:

python manage.py migrate

把模型加入允许列表

seoobject 提供者只处理你明确列入允许列表的模型。这样可以避免在每次对象解析时都执行 ContentTypes 查询;未列入的模型会直接跳过,不访问数据库。

# settings.py

SEO_SUITE = {
    "OBJECT_MODELS": [
        "catalogue.product",   # app_label.model_name, lowercase
        "blog.post",
    ],
}

格式为 "app_label.model_name"(不区分大小写)。


在 Django Admin 中管理 SEO

应用安装完成并把模型加入允许列表后,编辑人员就可以在 Django Admin 的 SEO Suite — Object rules → SEO object rules 中创建和编辑 SeoObject 记录。

每条记录包含:

字段 用途
Content type 这条记录适用于哪个模型
Object ID 具体对象的主键
Site ID 可选:仅限某个站点
Language 可选:仅限某种语言
Title 覆盖页面标题
Description 覆盖 meta description
Keywords 覆盖 meta keywords
Robots 覆盖 robots 指令
Canonical 覆盖 canonical URL
OG image 覆盖社交分享图片
Extra JSON-LD 附加的 JSON-LD 对象(累加,不替换)

字段留空即沿用更低优先级的默认值。


解析过程

解析器处理允许列表中某个对象的请求时,会在 SeoObject 表中查询与该对象的 content type 和主键匹配的记录。匹配的精确度规则如下:

  1. site_id 和 language 都匹配的记录优先。
  2. 其次是 site_id 匹配但未指定语言的记录。
  3. site_id 和 language 都为空的记录适用于所有站点和所有语言。

最终选中的记录作为对象层(优先级 40)合并。如果对象本身也定义了 get_seo_metadata()(即使用了 SeoModelMixin),其输出也会合并到同一层。由于 SeoObject 记录在 mixin 调用之后才应用,因此它的值优先。

Note

如果对象不在 OBJECT_MODELS 中,就不会查询 SeoObject。模型自身的 get_seo_metadata()(如果有)仍会照常调用。


示例:django-oscar 的 Product

# settings.py
SEO_SUITE = {
    "OBJECT_MODELS": ["catalogue.product"],
}
# yourapp/views.py
from django.views.generic import DetailView
from seo_suite.mixins import SeoViewMixin
from oscar.apps.catalogue.models import Product


class ProductDetailView(SeoViewMixin, DetailView):
    model = Product
    template_name = "catalogue/product_detail.html"

视图把 Product 实例作为上下文对象传入。解析器检查允许列表,找到 catalogue.product,查询 SeoObject,并合并匹配的记录。如果没有记录,则回退到全局默认值。