解析流程¶
每个页面请求都会走同一条管线:从多个 提供者(provider) 收集元数据,按优先级顺序合并各层,再把结果定型为可渲染的对象。理解这个模型后,其他所有功能都会更容易理解。
优先级阶梯¶
提供者按优先级从低到高依次合并。对每个字段而言,优先级高的提供者胜出——但 JSON-LD 和额外的 head 片段例外,它们会从所有层 累加。
| 优先级 | 来源 | 典型用途 |
|---|---|---|
| 10 | SEO_SUITE["DEFAULTS"] |
全站兜底的标题后缀、robots、og:type |
| 20 | SEO_SUITE["SITE_DEFAULTS"][site_id] |
多站点部署中按站点设置的默认值 |
| 30 | 匹配请求路径的 SeoPath 记录 |
针对特定 URL、可在后台编辑的覆盖项 |
| 40 | 对象的 get_seo_metadata() 或 SeoObject 记录 |
模型字段,或为第三方模型设置的后台覆盖项 |
| 50 | 视图的 get_seo_metadata() / seo_* 属性 |
视图级的覆盖与增强 |
三态字段:UNSET、None 与具体值¶
每个元数据字段都处于以下三种状态之一:
- UNSET:表示“这个提供者不表态”。解析器会跳过这一层,沿用更低优先级提供者给出的值。
None或"":表示“明确为空”。它会覆盖更低优先级的值,且不渲染任何内容。当你想抑制某个低层本来会输出的字段时(例如某个页面不应有meta description),就用这种方式。- 字符串值:原样输出。
实际使用中你很少需要直接考虑这些。Mixin 和基于数据库列的提供者在模型字段为空时会把字段设为 UNSET,因此低优先级的值自然会得到保留。
字段合并规则¶
| 字段类型 | 合并行为 |
|---|---|
标量(title、meta_description、robots、canonical_url 等) |
高优先级胜出;UNSET 则让位 |
og 字典、twitter 字典 |
逐键深度合并;每个键由高优先级胜出 |
hreflang 列表 |
高层提供列表时整体替换 |
jsonld 列表、extra_head 列表 |
累加:所有层都参与贡献 |
JSON-LD 的累加规则是有意为之:视图添加的 BreadcrumbList 结构可以与模型添加的 Article 结构并存,页面会把两个 <script type="application/ld+json"> 块都输出。
og / twitter 字段的自动填充¶
合并完成后,定型器会用解析出的页面级元数据补齐 og 和 twitter 字典中的空缺:
og:title←title(若未设置og:title)og:description←meta_description(若未设置)og:url←canonical_url(若未设置)og:image←og_image(若未设置)twitter:*字段镜像对应的og:*字段twitter:card在有图片时默认为"summary_large_image",否则为"summary"
在任意一层显式设置 og 或 twitter 字典,即可覆盖上述任何一项。
一个具体例子¶
假设 settings 中定义了标题后缀和全局 robots,一条 SeoPath 记录为 /about/ 设置了自定义标题,视图又设置了描述:
Layer 10 (DEFAULTS): title_suffix=" | Acme", robots="index,follow"
Layer 30 (SeoPath): title="About Acme"
Layer 50 (View): meta_description="We build great things."
合并结果:
title = "About Acme" (from layer 30)
title_suffix = " | Acme" (from layer 10)
meta_description = "We build great things." (from layer 50)
robots = "index,follow" (from layer 10)
渲染出的 title 标签:<title>About Acme | Acme</title>
解析器做了什么¶
解析器每个请求只调用一次(结果缓存在 request 对象上)。它会:
- 按优先级顺序收集所有层。
- 用
SeoMetadata.merge_all合并。 - 根据请求的 host 或
CANONICAL_DOMAIN把 canonical URL 转为绝对地址。 - 返回一个
FinalizedSeoMetadata对象。其中不再有 UNSET 哨兵值,所有字段都可直接渲染。
如果 SEO_SUITE["CACHE_TTL"] 大于 0,定型后的结果会存入 Django 缓存,键为 site:language:app.model:pk:generation。模型保存或删除时 generation 计数器会自动递增,因此永远不会返回过期的缓存条目。
在代码中读取解析结果¶
如果你需要在模板之外查看解析后的元数据(例如在 REST 接口或管理命令中),直接使用 resolve_seo:
from seo_suite.context import resolve_seo
def my_view(request):
seo = resolve_seo(request)
print(seo.title)
print(seo.canonical_url)
print(seo.jsonld)
对于最常见的情况——已经使用 SeoViewMixin 的类视图——在 get_context_data 运行后,元数据已经存在于 context["seo"] 和 request.seo 中。