Flatpages in Django

Flatpages are simple static pages whose content do not change frequently. For example, Flatpages are commonly used to create pages like About Us, EULA (End User License Agreement), Terms of Use, etc. Many beginners mistakenly believes that flatpages are stored as static HTML pages. That is far from true, just like everything flatpages are also stored in the database.

Installing flatpages app

By default Flatpages app or django.contrib.flatpages is not enabled by default. To install flatpage app open settings.py file and add django.contrib.flatpages to the INSTALLED_APPS list as follows:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django.contrib.flatpages',
    'blog',
    'cadmin',
]

The flatpages app depends upon another framework called Sites framework (django.contrib.sites) which is also not enabled by default. To enable sites framework add django.contrib.sites to the INSTALLED_APPS setting and define a SITE_ID variable with a value of 1 at the end of the settings.py file.

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django.contrib.flatpages',
    'django.contrib.sites',
    'blog',
    'cadmin',
]

...

SITE_ID = 1

Now run migrate command as usual to create the neccessary tables.

(env) C:\Users\Q\TGDB\django_project\django_project>python manage.py migrate
Operations to perform:
  Synchronize unmigrated apps: messages, staticfiles
  Apply all migrations: admin, sessions, sites, auth, contenttypes, flatpages
Synchronizing apps without migrations:
  Creating tables...
    Running deferred SQL...
  Installing custom SQL...
Running migrations:
  Rendering model states... DONE
  Applying sites.0001_initial... OK
  Applying flatpages.0001_initial... OK

(env) C:\Users\Q\TGDB\django_project\django_project>

Flatpages Model

The flatpages uses FloatPage model to save data into the database. The Flatpage Model looks something like this:

class FlatPage(models.Model):
    url = models.CharField(maxlength=100)
    title = models.CharField(maxlength=200)
    content = models.TextField(blank=True)
    enable_comments = models.BooleanField(default=False)
    template_name = models.CharField(maxlength=70, blank=True)
    registration_required = models.BooleanField(default=False)
    sites = models.ManyToManyField(Site)

Lets examine each field one by one.

1) url (required): This field represents the URL where flatpage will appear. It can only take URL which begins and end with leading and trailing slash. For example, /about/, /eula/, /about/us/ etc. However, don't pass host name to this field for example:

example.com/about/
http://example.com/about/

2) title (required): Title of the flatpage.

3) content (optional): Content of the flatpage in HTML.

4) enable_comments (optional): A Boolean field which designates whether you want to enable comments for the flatpage or not. Setting this field to True will not automatically create a comment form in flatpage. If you want a comment form you have create it yourself. We only use this field in templates to check whether comments are enabled for the flatpage or not.

5) template_name (optional):  template to use for rendering flatpages. If not given it will fall back to default template flatpages/default.html.

6) registration_required (optional): A Boolean field, if set to True then only logged in users will be able to view the flatpage.

7) sites (required): This field is very important. Every flatpage is associated with a site using many-to-many relationship. In other words a FlatPage object can belong to one or more Site object.

Sites framework

Sites framework (django.contrib.sites) allows us to run multiple websites from same database and Django project. Sites framework provides a model called Site which contains the following two fields:

  1. domain
  2. name

domain - It is used to specify the is the address of the website. For example, example.com or http://example.com or https://example.com .

name - This field is commonly used to specify a human readable easy to remember name of the site.

While enabling sites framework we have defined SITE_ID variable with value of 1 because this is our first site.

Django automatically adds a site with domain and name field set to example.com the when we run migrate command after adding django.contrib.sites to INSTALLED_APPS in settings.py file.

To learn more about it check Sites framework in Django documentation.

Having learned the anatomy of flatpages, let's create some flatpages. At this point, there are two ways to create flatpages.

  1. Using Django Admin.
  2. Using Django ORM.

Lets try the Django Admin first.

Creating flatpages using Django Admin

Visit http://127.0.0.1:8000/admin/ and login to Django admin.

You should see FlatPage application listed with all the other installed apps as follows:

[]

Click the "Add" link in front of Flat Page to create a new FlatPage. You should see a form like this:

[]

Create a new "About" page by entering the following details:

[]

As discussed url, title and sites fields are required.

Notice Advanced options section at the bottom of the form. Click on the Show link to make it visible. The Advanced section allows you to set values to the following fields:

  • enable_comments
  • registration_required
  • template_name

[]

We don't need to enter any data into these fields for now.

Once you are done hit Save button at the bottom of the page.

Creating flatpages using Django ORM

Flatpages are implemented as FlatPage model. To use FlatPage model we have to first import it from django.contrib.flatpages.models as follows:

from django.contrib.flatpages.models import FlatPage

At this point, we should have the "About" flatpage object in our database.

>>>
>>> FlatPage.objects.values()
[{'id': 1, 'content': 'content of about\r\n\r\nFLATPAGE', 'url': '/about/', 'tem
plate_name': '', 'title': 'About', 'registration_required': False, 'enable_comme
nts': True}]
>>>

Let's create a EULA page.

>>>
>>> FlatPage.objects.create(
... url='/eula/',
... title = "EULA",
... content = "EULA for The Great Django Blog"
... )
<FlatPage: /eula/ -- EULA>
>>>
>>>

Now we have two flat pages "About" and "EULA". The important thing to note here is that our EULA flatpage is not usable at this point. We can't use flatpage in anyway until we associate it with at last one Site object.

>>>
>>> fp1 = FlatPage.objects.get(title="About")
>>> fp1
<FlatPage: /about/ -- About>
>>>
>>> fp1.sites.all()
[<Site: example.com>]

>>>
>>> fp2 = FlatPage.objects.get(title="EULA")
>>> fp2.sites.all()
<QuerySet []>
>>>

The above code demonstrates that our "About" page is associated with one site (i.e example.com), However, "EULA" page is associated with none.

To make EULA flatpage usable, connect it to a Site object as follows:

>>>
>>> from django.contrib.sites.models import Site  ## import Site model
>>>
>>> site = Site.objects.get(id=1)   ## get the site object
>>> site
<Site: example.com>
>>>
>>> fp2.sites.add(site)  ## connect it fp2 to site object using add() method
>>>
>>> fp2.sites.all()      
[<Site: example.com>]
>>>

Creating template for flatpages

We have two flatpages now but no way of displaying them. By default a FlatPage object uses flatpages/default.html template unless you specify a different one while creating/updating a FlatPage object.

Django doesn't supply flatpages/default.html template, so you have to create one yourself. In the blog app inside the templates directory, create a new directory named flatpages. Then inside the flatpages directory create a new file called default.html and add the following code to it.

{% extends "blog/base.html"  %}

{% block title %}
    {{ flatpage.title }} - {{ block.super }}
{% endblock %}

{% block content %}

    <div class="content">
        <div class="section-inner clearfix">

        <h1>{{ flatpage.title }}</h1>

        <p>
            {{ flatpage.content|linebreaks }}
        </p>

        </div>
    </div>

{% endblock %}

Notice the template variables flatpage.title and flatpage.content. All flatpages templates are passed a context variable named flatpage which is an FlatPage object. Once we have FlatPage object we can access any relevent information by typing a (.) dot followed by the attribute name. In our case we are only outputting the value of title (flatpage.title) and content (flatpage.title) attribute of the FlatPage object.

URL Pattern

Everything is in place. We just need url patterns to access our flatpages. Open urls.py in the blog app and add the following code to it.

from django.contrib.flatpages import views as flat_views
...

urlpatterns = [
    ...
    url(r'^about/$', flat_views.flatpage, {'url': '/about/'}, name='about'),
    url(r'^eula/$', flat_views.flatpage, {'url': '/eula/'}, name='eula'),
    ...
]

The flatpage app (django.contrib.flatpages) provides a flatpage() view whose job is to render the flatpages. It accepts a required argument called url which is the URL of the template we entered while creating the flatpage.

Now visit http://127.0.0.1:8000/about and http://127.0.0.1:8000/eula you will be greeted with about and eula flatpages respectively.