Guide: Detail views¶
SeoViewMixin on a DetailView gives you object-derived metadata by default,
with an easy path to override specific fields at the view level.
Default behaviour¶
When SeoViewMixin is mixed into a DetailView, the resolver receives both the
view and self.object. The object's get_seo_metadata() runs at priority 40;
any seo_* attributes or get_seo_metadata() override on the view runs at
priority 50.
from django.views.generic import DetailView
from seo_suite.mixins import SeoViewMixin
from .models import Article
class ArticleDetail(SeoViewMixin, DetailView):
model = Article
slug_field = "slug"
slug_url_kwarg = "slug"
This is all that is required. The article's title, description, canonical
URL, and any other fields resolved by SeoModelMixin are automatically used.
Overriding individual fields¶
Set seo_* class attributes to override specific fields without touching the
model:
class ArticleDetail(SeoViewMixin, DetailView):
model = Article
slug_field = "slug"
seo_robots = "noindex" # drafts, paywalled content, etc.
Available class attributes (all default to None, meaning "no opinion"):
| Attribute | Metadata field |
|---|---|
seo_title |
title |
seo_title_suffix |
title_suffix |
seo_h1 |
h1 |
seo_description |
meta_description |
seo_keywords |
meta_keywords |
seo_robots |
robots |
seo_canonical |
canonical_url |
seo_og_image |
og_image |
Dynamic overrides with get_seo_metadata¶
For values that depend on request state, override get_seo_metadata:
class ArticleDetail(SeoViewMixin, DetailView):
model = Article
def get_seo_metadata(self, context=None):
from seo_suite.metadata import SeoMetadata
# Only suppress indexing for unpublished content.
if not self.object.published:
return SeoMetadata.partial(robots="noindex,nofollow")
# Return UNSET for all fields to defer entirely to the object layer.
return SeoMetadata.partial()
SeoMetadata.partial(**kwargs) constructs a metadata object with only the
named fields set; all others remain UNSET so lower-priority layers supply them.
View title overrides the object, but canonical survives¶
A common pattern: the view sets a branded title while keeping the object's canonical URL:
class ArticleDetail(SeoViewMixin, DetailView):
model = Article
seo_title = "Read Our Latest" # overrides article.title at priority 50
# canonical_url is not set here, so article.get_absolute_url() survives
Because seo_canonical is None on the view (no opinion), the object's
canonical_url from get_absolute_url() is used unchanged.
Adding JSON-LD at the view level¶
Use seo_schema_profiles to enable schema profiles at the view level. This is
useful when the profile depends on request context (e.g. adding a WebSite
profile only on the homepage):
When both the model and the view declare profiles, both sets of JSON-LD blocks are emitted (accumulation rule). See JSON-LD structured data.
Using SeoViewMixin without DetailView¶
SeoViewMixin works with any class-based view that has get_context_data.
The object attribute is optional; if present, it is passed to the resolver.
from django.views.generic.base import View
from django.shortcuts import get_object_or_404
from seo_suite.mixins import SeoViewMixin
from seo_suite.context import attach_seo
class MyCustomView(SeoViewMixin, View):
seo_title = "Custom Page"
def get(self, request, pk):
self.object = get_object_or_404(Article, pk=pk)
seo = attach_seo(request, view=self, obj=self.object)
# ... render response ...