Flatpages In Django Part 1: Basic Implementation

How to quickly set up Django flatpages for building generic, one-off pages like "about us," "privacy policy, "terms and conditions," and so forth.

Introduction

For constructing and maintaining generic, one-off pages, such as for your "privacy policy" or "terms and conditions" pages, where building a dedicated app would be overkill, Django has the native app "flatpages" that is capable of handling handling and storing html content. Flatpages is a complete solution that requires very little set up to get up and running. I'll explore how to do so in this article.

Before we jump in, here's the Django FlatPage model from Github

class FlatPage(models.Model):
    url = models.CharField(_("URL"), max_length=100, db_index=True)
    title = models.CharField(_("title"), max_length=200)
    content = models.TextField(_("content"), blank=True)
    enable_comments = models.BooleanField(_("enable comments"), default=False)
    template_name = models.CharField(
        _("template name"),
        max_length=70,
        blank=True,
        help_text=_(
            "Example: “flatpages/contact_page.html”. If this isn’t provided, "
            "the system will use “flatpages/default.html”."
        ),
    )
    registration_required = models.BooleanField(
        _("registration required"),
        help_text=_(
            "If this is checked, only logged-in users will be able to view the page."
        ),
        default=False,
    )
    sites = models.ManyToManyField(Site, verbose_name=_("sites"))

For my purposes, I'm primarily interested in the URL, title, and content fields. The URL field is particularly interesting. It specifies the location of the page and is also used for lookups by default. If you use the flatpages middleware, it will pass the URL to the default flatpages view function when a 404 response status code is encountered and see if a page with a matching URL exists. That way, you never need to statically code a single URL pattern to get your page working.

The other fields are interesting in that they make the model quite flexible. For example, the "registration_required" field allows for using flatpages behind password protection. In my opinion, that's too broad of an application for the projects I work with. But I appreciate the power that this adds to the model.

Let's get started with flatpages.

Set Up

Django flatpages can be integrated into your project with just a couple quick steps. For the following demonstration, I'll be using Django version 4.2.

Step 1: Add flatpages to installed apps.

Within your project settings, add the following to INSTALLED_APPS:

# settings.py

INSTALLED_APPS = [
    ...
    'django.contrib.sites'
    'django.contrib.flatpages',
]

The flatpages app uses the "sites" framework as a dependency, so make sure to add it to INSTALLED_APPS first. Furthermore, you'll need to specify the SITE_ID setting. If your project contains a single site, use the integer 1. A matching Site object needs to exist in the database so be sure to create it.

# settings.py

SITE_ID = 1

If your project handles multiple sites, make sure to point to the correct site id.

Step 2: Handle URLs

You can handle routing for flatpages in a number of different ways. If you're interesting in having pages exist at a particular location or with a certain prefix, then simply add the following to your root URL config:

# urls.py

from django.urls import path, include


urlpatterns = [
    path("pages/", include("django.contrib.flatpages.urls")),
]

Alternatively, you could use the provided middleware for a more dynamic solution. When the middleware is installed, it will listen for when "404" exceptions are raised and check for matching URLs in the flatpages table. Keep in mind this needs to be a full path if any nesting is needed. For example, with the first approach a url value of "/about-us/" would route to "/pages/about-us/." When using the middleware, you need to add the "/pages/" component explicitly if that's where you desire the resource to be located.

He's what the middleware looks like installed.

# settings.py

MIDDLEWARE = [
    ...
    'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware',
]

The order of the middleware matters. You should put the flatpages middleware at the end of the list.

Last, you can always explicitly define routes.

# urls.py

from django.contrib.flatpages import views


urlpatterns = [
    path("about-us/", views.flatpage, {"url": "/about-us/"}, name="about"),
]

Step 3: Migrate

Both the sites and flatpages apps have migrations and need to by applied to work properly. Be sure to run the following once both have been added to INSTALLED_APPS:

python manage.py migrate

Step 4: Build A Page (with Admin)

As mentioned above, flatpages can be used out of the box with the Django admin interface. You don't have to use it if you want to build your own admin interface, which is the approach I tend to follow. But this is the simplest and fastest way to get up and running. The admin framework is installed by default. But if you had disabled it from the beginning, make sure to add it back to INSTALLED_APPS and migrate.

Now, simply log in to your admin site (located at "/admin/login/" by default) and begin building pages.

The final step involves building a template. But first, let's look at what can go wrong using the Admin site.

Step 4(a): Error - Site Matching Query Does Not Exist

If you had disabled admin to begin with, a site object may not have been created by default. An exception will be raised when you try to access the admin site. To fix this manually, open the Django shell:

python manage.py shell
from django.contrib.sites.models import Site
Site.objects.create(name="example.com", domain="example.com")

Additionally, leaving out SITE_ID can cause the exception.

Step 5: Template

This is pretty much the one thing Django doesn't handle for you. Unfortunately, there's really no way it could handle building even a generic template for your flatpages because it wouldn't know what base template you're extending.

The docs instruct you to establish the default template at/as "flatpages/default.html." I like to place generic templates like this in a templates directory nested in the project root since it doesn't belong to any particular app within the project. Within the project root, I'll create the directories "templates/flatpages" and add the file "default.html."

Because this will be outside any installed app where Django will search for templates by default, the "templates" directory needs to be registered.

# settings.py

TEMPLATES = [
    {
        ...
        'DIRS': [
            os.path.join(BASE_DIR, 'templates'),
        ],
        ...
    }
]

Once you create your default.html file, build the html to extend your base template and reference the title and content attributes within your template. With your template built out, you're now ready to go. It's that easy.

Conclusion

Django flatpages provides a simple and efficient method for creating and managing basic pages. Using this approach can be a great way to save time. With the five basic steps outlined above, you can be up and running quickly without having to build any features. The FlatPage model is a powerful tool that can be used in a wide variety of situations and I recommend incorporating its use in your next Django project.

Further Reading

Check out my article Flatpages In Django Part 2: Extending Flatpages which looks at how to extend the functionality of the flatpages framework.

Details
Published
September 8, 2023
Next
September 9, 2023

Flatpages In Django Part 2: Extending Flatpages

How to extend the Django flatpages app including a custom view to handle lookups by ID instead of URL.