Expose content at a URL (app hooks)¶
Setting apphook = True on a content model’s CMSConfig generates a CMS app
hook with a ready-made detail view. This page documents exactly what gets
generated, the URL it produces, and the contract your template must follow.
Enabling the app hook¶
class ArticleContent(AbstractCustomContent):
article = models.ForeignKey(Article, on_delete=models.CASCADE)
slug = models.SlugField()
title = models.CharField(max_length=200)
body = models.TextField()
class CMSConfig:
apphook = True
Then attach it to a page: in the CMS page admin, open Advanced settings and select the app hook (named after the grouper, e.g. “Article”), and publish.
What gets generated¶
A detail view — a Django
DetailViewover the content model.The URL is
<slug>/when the content model has aslugfield, and<pk>/otherwise. Its route name isdetailwithin an application namespace named after the grouper, lowercased (e.g.article).A
get_absolute_url()method is injected onto the content model (unless it already defines one). Prefer it over hand-writing{% url %}tags:<a href="{{ articlecontent.get_absolute_url }}">{{ articlecontent.title }}</a>
The equivalent explicit reversal is
{% url 'article:detail' slug=obj.slug %}.
The template contract¶
Two render paths can use your template, and they expose the content object under its (lowercased) model name — so use that variable for portability:
Render path |
Context variables |
Template chosen |
|---|---|---|
App hook detail view |
|
|
Frontend-editing render |
|
|
For ArticleContent in app my_content that means the variable
articlecontent and the template
my_content/templates/my_content/articlecontent_detail.html:
{% extends "base.html" %}
{% load cms_tags %}
{% block content %}
{% cms_edit_on %}
<article>
<h1>{{ articlecontent.title }}</h1>
<div>{{ articlecontent.body|safe }}</div>
</article>
{% cms_edit_off %}
{% endblock %}
Note
Use articlecontent, not object — object is only present on the
app-hook detail view, so a template that relies on it renders blank during
frontend editing.
Overriding the template name¶
AbstractCustomContent.get_template() returns
<app_label>/<modelname>_detail.html by default. Overriding it changes the
template used by the frontend-editing render only; the app-hook detail view
still resolves its template from the model’s template_name_suffix
(_detail). To keep both paths on one template, override get_template and
name the file to match, or simply use the default convention.
See Also¶
Article with APPhooks and CMS Plugins - App hook in a worked example
Register the grouper admin - Editing the content behind these URLs