Dynamic page titles in Plain and Django
Using class-based views or template blocks to dynamically generate page titles in a Plain or Django app.
It's easy to forget about the HTML <title>
element when building a Plain or Django app.
You put one in your base.html
when you start the project,
but forget to actually change the title when making your individual views and templates.
<!doctype html>
<html lang="en">
<head>
<title>Forge</title>
</head>
But good page titles are incredibly useful!
Forgetting them can hurt both SEO and user experience.
There are two ways we recommend implementing HTML page titles in Plain or Django:
- Using template blocks (simpler)
- Using class-based views (more powerful)
Page titles using template blocks
Template blocks can be used for small pieces of content just like they can be used for entire headers/bodies/footers.
In your base.html
, use a standard template block inside the <title>
tag:
<!doctype html>
<html lang="en">
<head>
<title>{% block title %}{% endblock %}</title>
</head>
When you extend your template,
simply use the {% block title %}
to set the page title:
{% extends "base.html" %}
{% block title %}Billing{% endblock %}
{% block content %}
...
{% endblock %}
You can use context variables just like any other template blocks:
{% extends "base.html" %}
{% block title %}{{ obj.name }} billing{% endblock %}
{% block content %}
...
{% endblock %}
That's all there is to it! If you don't know where to start then give this a try. But if you find yourself needing more control then consider moving the logic to your views instead...
Page titles using class-based views
Let's create a view mixin that injects a title
into the template context:
# (Plain)
class PageTitleViewMixin:
title = ""
def get_title(self):
"""
Return the class title attr by default,
but you can override this method to further customize
"""
return self.title
def get_template_context(self):
context = super().get_template_context()
context["title"] = self.get_title()
return context
# (Django)
class PageTitleViewMixin:
title = ""
def get_title(self):
"""
Return the class title attr by default,
but you can override this method to further customize
"""
return self.title
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["title"] = self.get_title()
return context
Then use {{ title }}
in your base.html
:
<!doctype html>
<html lang="en">
<head>
<title>{{ title }}</title>
</head>
By using both a title
class attribute and a get_title
method,
it will be easy to set a "static" title for a view:
class BillingView(PageTitleViewMixin, TemplateView):
template_name = "billing.html"
title = "Billing"
But also do something more dynamic:
class ProjectView(PageTitleViewMixin, DetailView):
def get_title(self):
return self.object.name
You could further extend this to add a suffix to the title automatically:
# (Plain)
class PageTitleViewMixin:
...
def get_template_context(self):
context = super().get_template_context()
context["title"] = self.get_title() + " - My App"
return context
# (Django)
class PageTitleViewMixin:
...
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["title"] = self.get_title() + " - My App"
return context
Bonus! You could also raise an exception on empty titles, so you don't forget to set the title for every view:
# (Plain)
class PageTitleViewMixin:
...
def get_template_context(self):
context = super().get_template_context()
title = self.get_title()
if not title:
raise ValueError("Page title should not be empty")
context["title"] = title + " - My App"
return context
# (Django)
class PageTitleViewMixin:
...
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
title = self.get_title()
if not title:
raise ValueError("Page title should not be empty")
context["title"] = title + " - My App"
return context