指南:JSON-LD 结构化数据¶
django-seo-suite 内置了一套现成的 schema.org 配置档(profile)。只需在模型上加一个类属性即可启用,无需改动模板。
在模型上启用配置档¶
from django.db import models
from seo_suite.mixins import SeoModelMixin
class Article(SeoModelMixin, models.Model):
headline = models.CharField(max_length=200)
blurb = models.TextField(blank=True)
cover = models.ImageField(upload_to="covers/", blank=True)
published_at = models.DateTimeField()
updated_at = models.DateTimeField(auto_now=True)
slug = models.SlugField(unique=True)
SEO_FIELD_MAP = {"title": "headline", "meta_description": "blurb", "og_image": "cover"}
SEO_SCHEMA_PROFILES = ["Article", "BreadcrumbList"]
def get_absolute_url(self):
return f"/articles/{self.slug}/"
def get_breadcrumbs(self):
return [
("Home", "/"),
("Articles", "/articles/"),
(self.headline, self.get_absolute_url()),
]
{% seo_head %} 会把每个配置档渲染成一个独立的 <script
type="application/ld+json"> 块。
免费配置档库¶
下面所有配置档默认都已注册。在模型的 SEO_SCHEMA_PROFILES 或视图的 seo_schema_profiles 中按键名引用即可。
WebPage¶
键名: "WebPage"
读取 title/name/headline → name,description/summary/excerpt →
description,get_absolute_url() → url。始终输出(即使没有匹配到任何字段)。
WebSite¶
键名: "WebSite"
读取 SEO_SUITE["DEFAULTS"]["og"]["site_name"] → name,请求根地址 →
url。一般加在首页视图上。
Organization¶
键名: "Organization"
读取 SEO_SUITE["DEFAULTS"]["og"]["site_name"] → name,请求根地址 →
url。在首页上与 WebSite 搭配使用。
Person¶
键名: "Person"
读取 full_name/name/get_full_name() → name,get_absolute_url() →
url。用于作者个人主页。
BreadcrumbList¶
键名: "BreadcrumbList"
读取 get_breadcrumbs()(或 breadcrumbs 属性)。该方法必须返回一个由 (name, url) 元组组成的列表,或一个由带 "name" 和 "url" 键的字典组成的列表:
def get_breadcrumbs(self):
# Tuple form:
return [
("Home", "/"),
("Articles", "/articles/"),
(self.headline, self.get_absolute_url()),
]
# Dict form (equivalent):
return [
{"name": "Home", "url": "/"},
{"name": "Articles", "url": "/articles/"},
{"name": self.headline, "url": self.get_absolute_url()},
]
只有至少存在一个有效项时才会输出。
Article¶
键名: "Article"
| JSON-LD 字段 | 依次尝试的来源属性 |
|---|---|
headline |
headline、title、name |
description |
meta_description、description、summary、excerpt |
image |
og_image、image、cover_image、thumbnail |
datePublished |
date_published、published_at、published、pub_date、created |
dateModified |
date_modified、modified、updated_at、updated |
author |
author_name、author(包装为 {"@type":"Person","name":...}) |
mainEntityOfPage |
get_absolute_url() |
FAQPage¶
键名: "FAQPage"
读取 get_faqs()(或 faqs 属性)。该方法必须返回一个列表,元素为 (question, answer) 元组,或带 "question"/"answer" 键的字典(也接受 "q"/"a" 简写):
def get_faqs(self):
return self.faq_items.values_list("question", "answer")
# or:
return [
{"question": "What is …?", "answer": "It is …"},
{"q": "How do I …?", "a": "You can …"},
]
只有至少存在一组完整的问答时才会输出。
Product¶
键名: "Product"
| JSON-LD 字段 | 依次尝试的来源属性 |
|---|---|
name |
name、title |
description |
meta_description、description、summary |
image |
og_image、image、cover_image |
sku |
sku |
brand |
brand(包装为 {"@type":"Brand","name":...}) |
offers |
由 price、currency/price_currency、availability 组装而成 |
用 get_schema_data 覆盖配置档字段¶
任何模型都可以为某个配置档补充或覆盖特定字段,无需继承该配置档:
class Article(SeoModelMixin, models.Model):
headline = models.CharField(max_length=200)
SEO_SCHEMA_PROFILES = ["Article"]
def get_schema_data(self, key, context=None):
if key == "Article":
return {
"inLanguage": "en-US",
"publisher": {
"@type": "Organization",
"name": "My Blog",
"logo": {"@type": "ImageObject", "url": "https://example.com/logo.png"},
},
}
return {}
get_schema_data 返回的字典会合并到配置档的自动输出之上(你的键优先于配置档的键)。不想修改的配置档,返回 {} 或 None 即可。
用 get_schema_image 输出更丰富的图片信息¶
默认情况下,配置档的 image 字段只是一个纯 URL 字符串(从 og_image、image、cover_image、thumbnail 解析而来)。如果你还有原始大图及其尺寸,可以改为输出 schema.org 的 ImageObject:Google 的富媒体结果更青睐带明确尺寸的图片。
实现可选的 get_schema_image(key, context) 钩子即可。它返回什么,就原样用作配置档的 image 值:可以是字符串、ImageObject 字典,或由二者组成的列表。django-seo-suite 不会处理你的图片;你可以自由对接自己的缩略图库(easy-thumbnails、sorl、imagekit 等):
class Article(SeoModelMixin, models.Model):
cover = models.ImageField(upload_to="covers/")
SEO_SCHEMA_PROFILES = ["Article", "BreadcrumbList"]
def get_schema_image(self, key, context=None):
# The `key` argument is a per-profile switch: return a value only
# for the profiles you want, and None to fall back / skip.
if key != "Article":
return None
thumb = get_thumbnailer(self.cover)["card"] # your thumbnail backend
return {
"@type": "ImageObject",
"contentUrl": self.cover.url,
"thumbnailUrl": thumb.url,
"width": self.cover.width,
"height": self.cover.height,
}
image 字段的解析顺序:先按约定设置纯字符串,再由 get_schema_image 覆盖;如果 get_schema_data(见上文)也返回了 image 键,则以它为最终结果。
这个钩子完全可选,不实现时行为不变。借助 key 参数,一个方法就能服务所有配置档,由你决定哪些配置档带图片(返回 None 即跳过)。
高流量站点的缓存
get_schema_image 在解析时运行,所以即使钩子很重(例如调用缩略图后端或探测图片尺寸),每次请求也只会调用一次。启用对象解析缓存(SEO_SUITE["CACHE_TTL"] > 0,需要已配置 Django 缓存后端)后,钩子的输出会作为缓存元数据的一部分保存下来,在多次请求间复用,并在对象保存或删除时自动失效。缓存关闭(默认)时,钩子在每次解析时都会运行,因此请保持它足够轻量,或在钩子内部自行缓存。
在视图上启用配置档¶
在视图上使用 seo_schema_profiles(注意:没有 SEO_ 前缀,且为小写):
class HomepageView(SeoViewMixin, TemplateView):
template_name = "home.html"
seo_title = "My Site"
seo_schema_profiles = ["WebSite", "Organization"]
全站默认配置档¶
要给站点的每个页面都加上某个配置档,使用 DEFAULT_SCHEMA_PROFILES:
DEFAULT_SCHEMA_PROFILES 中的配置档会与模型级和视图级配置档一并合并;所有来源的 JSON-LD 块都会累加输出。
JSON-LD 的累加规则¶
标量字段遵循“高优先级覆盖低优先级”,而 JSON-LD 块会在所有层级间累加。模型声明了 ["Article"],视图声明了 ["WebPage"],同一页面就会输出这两个块。这是有意设计的:一个页面包含多种结构化数据类型是合法的,而且往往正是你想要的。
自定义序列化器¶
默认的序列化器会用 Unicode 转义处理 <、> 和 & 字符,防止通过 JSON-LD 注入代码。如果需要不同的序列化策略(例如调试时美化输出、自定义转义),可以替换序列化器: