Reference: SeoMetadata attributes

The seo template context variable is a FinalizedSeoMetadata instance. All UNSET sentinels have been resolved; every field is a Python value ready to render.


FinalizedSeoMetadata fields

Field Type Description
title str \| None Page title (without suffix).
title_suffix str Suffix appended to the title (empty string if not set).
full_title str title + title_suffix. Empty string when title is None. Property.
h1 str \| None Page heading text, separate from the <title>.
meta_description str \| None Content of <meta name="description">.
meta_keywords str \| None Content of <meta name="keywords">.
robots str \| None Content of <meta name="robots">.
canonical_url str \| None Absolute canonical URL.
hreflang list[HreflangAlt] List of hreflang alternates. Empty list when none set.
og dict[str, str] Open Graph values keyed by property suffix (e.g. "title", "image"). Auto-populated from page metadata.
twitter dict[str, str] Twitter card values keyed by tag name suffix. Auto-populated from og values.
jsonld list[dict] Accumulated JSON-LD objects from all layers. Empty list when none set.
extra_head list[str] Extra HTML fragments for the <head>. Empty list when none set.

HreflangAlt

Each entry in seo.hreflang is a HreflangAlt named tuple:

Field Type Description
lang str Language code or "x-default".
href str Absolute URL for this language.

SeoMetadata (partial / builder)

SeoMetadata is the immutable dataclass used by providers to express "partial" metadata — only the fields they have opinions about. It is distinct from FinalizedSeoMetadata (which is what templates see).

Constructing a partial

from seo_suite.metadata import SeoMetadata

# Only the named fields are set; all others are UNSET.
md = SeoMetadata.partial(
    title="My Page",
    meta_description="A description.",
    robots="noindex",
)

Fields

Same names as FinalizedSeoMetadata:

title, title_suffix, h1, meta_description, meta_keywords, robots, canonical_url, hreflang, og, twitter, og_image, jsonld, extra_head

Note

og_image exists on SeoMetadata (the builder) but not on FinalizedSeoMetadata. During finalisation, og_image is folded into og["image"] and twitter["image"]; it is not a separate field in the rendered output.


Accessing the seo object in Python

From a view or middleware:

from seo_suite.context import resolve_seo, attach_seo

# Resolve (not memoized):
seo = resolve_seo(request, view=view, obj=obj)

# Resolve and memoize on the request:
seo = attach_seo(request, view=view, obj=obj)

# Read back a memoized result without re-resolving:
from seo_suite.context import get_attached
seo = get_attached(request)  # None if not yet resolved

Checking fields safely

All scalar fields are None when not set. Use Django's default template filter or standard Python truthiness checks:

{{ seo.h1|default:seo.title }}
if seo.canonical_url:
    do_something(seo.canonical_url)

og and twitter are always dicts (never None). hreflang, jsonld, and extra_head are always lists (never None).


as_dict()

Convert the finalized metadata to a plain dictionary (useful for JSON responses or logging):

seo = resolve_seo(request)
data = seo.as_dict()