04 April 2020

Dynamic Page Titles in Django

Often in a web application/website, you want each page or view to have a title that semantically describes what the current page is showing. In a web application, these may be dynamic titles, based on some attribute of the primary model being displayed on the page. I recently encountered this problem while working on the new version of Seeker, but I did not find that many appealing solutions through my usual Google/StackOverflow search. After a little bit of thought and trial and error, I ended up coming with a pretty good mixin-based solution.

First, you should make a base.html file that all your other templates inherit from and add the following to the <head></head> section.

<!-- Assuming this is in a template called `base.html` that gets inherited by other templates -->
<title>
  {{page_title}}
</title>

This is saying that whatever exists at the page_title attribute in the context object will be the title of the page. Now, let’s create a mixin for our views that will set this page_title attribute.

class PageTitleMixin(object):
  def get_page_title(self, context):
    return getattr(self, "page_title", "Default Page Title")

  def get_context_data(self, **kwargs):
    context = super().get_context_data(**kwargs)
    context["page_title"] = self.get_page_title(context)

    return context

This gives us two ways of defining a page title for our views that use this mixin.

  1. Define a page_title attribute on the class.
  2. Override get_page_title to dynamically set the page title.

Now that we got this mixin defined, let’s actually use it.

# Renders "Static Page Title" as the page title
class StaticPageTitleView(PageTitleMixin, View):
  template_name = "index.html"
  page_title = "Static Page Title"

# Renders "Default Page Title" as the page title
class DefaultPageTitleView(PageTitleMixin, View):
  template_name = "index.html"

# Renders "Some Model Attr" as the page title
class DynamicPageTitleView(PageTitleMixin, DetailView)
  template_name = "index.html"
  model = OurModel # assume that it has an attribute called `title`
  context_object_name = "model"

  def get_page_title(self, context):
    return context["model"].title # Assume this returns "Some Model Attr"

Lastly, let’s say you have another mixin that dynamically populates the context and you want to use that as the default value. That’s possible too, we just need to make some minor tweaks to our mixin code. Check this out.

class PageTitleMixin(object):
  def get_page_title(self, context):
    return getattr(self, "page_title", context["user_settings"].site_title)

  def get_context_data(self, **kwargs):
    context = super().get_context_data(**kwargs)
    context["page_title"] = self.get_page_title(context)

    return context

# We want to inject the `user_settings`object into every single context.
class UserSettingsContextMixin(object):
  def get_context_data(self, **kwargs):
    context = super().get_context_data(**kwargs)
    user = self.request.subdomain_user

    # assume the user has a settings object with a title attribute
    context["user_settings"] = user.settings

    return context

# Use another mixin to encapsulate all mixins for our page.
class PageMixin(PageTitleMixin, UserSettingsContextMixin):
  pass

# Renders "Some User's Title" as the page title
class DefaultPageTitleView(PageMixin, View):
  template_name = "index.html"

And that’s it! The nice thing about this mixin pattern is that you can extend this to other meta attributes in the page as necessary as well. I hope this was helpful! Do you have a different way of dynamically handling page titles in Django or have any questions? Feel free to let me know on Twitter!

Did you enjoy reading this?

Feel free to let me know on Twitter. You can also subscribe to my newsletter for more of my writing.