I’ve been working on a web site for a client that uses django flatpages. Flatpages is a built in component that allows users to create static content within the admin interface and publish it to the web front end. Think of it as a basic content management system component for django. Out of the box django’s flatpages have some limitations though – you cannot easily dictate a sort order or heirachy for the pages. Let me demonstrate…
The old way:
Typically one might show a list of your flatpages on your website you would do something like this
{% load flatpages %} {# Custom tag defined in lib/templatetags/ #}
{% get_flatpages as flatpages %}
<ul>
{% for page in flatpages %}
<li><a href="{{ page.url }}">{{ page.title }}</a></li>
{% endfor %}
</ul>
Which might generate some html rendered out like this:
Extending the FlatPages model:
We can use model inheritance to add a couple of fields to the flatpages model as explained here. To do that I simply add something like this to my models.py:
from django.db import models
from django.contrib.flatpages.models import FlatPage
class ExtendedFlatPage(FlatPage):
show_after = models.ForeignKey('ExtendedFlatPage', \
null=True, blank=True, default=None, \
related_name="flatpage_predecessor", \
help_text="Page that this one should appear after (if any)")
child_of = models.ForeignKey('ExtendedFlatPage', \
null=True, blank=True, default=None, \
related_name="flatpage_parent", \
help_text="Page that shis one should appear under (if any)")
So my new model adds two fields to the flatpages model that will allow me to define ordering and heirachy. The show_after field means that when I create a new flatpage, I can specify which other page the new page should be shown after. The child_of field can be used to state that the new page being added is a child of another page. A quick run of
python manage.py syncdb
Will go ahead and create the new table for your extended flat pages model. If we want to migrate existing flatpages into the new model, you can run a little query from the postgres command prompt like this:
insert into localsite_extendedflatpage (flatpage_ptr_id) select (id) from django_flatpage;
Where localsite_extendedflatpage is the table that was generated for your model (its name will vary depending on the name of your django app).
Registering the model in the admin interface:
To allow the user to administer the extended flat pages, you need to register your custom flatpages model with the admin interface and deregister the existing one (in admin.py):
from django.contrib import admin
from django.contrib.flatpages.admin import FlatpageForm, FlatPageAdmin
from django.contrib.flatpages.models import FlatPage
from models import ExtendedFlatPage
class ExtendedFlatPageForm(FlatpageForm):
class Meta:
model = ExtendedFlatPage
class ExtendedFlatPageAdmin(FlatPageAdmin):
form = ExtendedFlatPageForm
fieldsets = (
(None, {'fields': ('url', 'title', 'content', \
'sites', 'show_after', 'child_of' )}),
)
admin.site.unregister(FlatPage)
admin.site.register(ExtendedFlatPage, ExtendedFlatPageAdmin)
After restarting your web server, you should now see the ExtendedFlatPages model listed in your admin interface, and it should show you our two custom fields which we use to define order and heirachy.
Creating a custom template tag:
The next step in our journey is to create a custom templatetag that will render our listing of flatpages according to their heirachy and order. I added this to <myapp>/templatetags/local_tags.py :
from django import template
from localsite.models import ExtendedFlatPage
register = template.Library()
@register.simple_tag
def show_ordered_flatpages():
flatPages = ExtendedFlatPage.objects.filter(child_of__isnull=True).order_by('-show_after')
myString = ""
for myPage in flatPages:
myString += get_ul_for_page( myPage )
return myString
def get_ul_for_page( thePage ):
flatPages = ExtendedFlatPage.objects.filter(child_of=thePage).order_by('show_after')
if len(flatPages) < 1:
return """<li><a href="%s">%s</a></li>""" % ( thePage.url, thePage.title )
else:
myString = """<li><a href="%s">%s</a>""" % ( thePage.url, thePage.title )
myString += "<ul>\n"
for myPage in flatPages:
myString += get_ul_for_page( myPage ) #recursion
myString += "</ul>\n"
myString += "</li>\n"
return myString
So the template tag uses a simple recursive function to generate a heirachical nested collection of unordered lists (ul). The last thing I need to do is update my template that shows a listing of available flatpages to look something like this:
{% load local_tags %}
<div class = "flat-page-list">
{% show_ordered_flatpages %}
</div>
The result:
After making the above changes, my contrived list of flatpages now looks like this when rendered:
A couple of gotchas:
My examples above do not check to see where the logged in user has the rights to view a page. Also my ExtendedFlatPages model needs to have some checks added to ensure that show_after and child_of fields can not be populated with a reference back to themselves (which would probably cause infinite recursion in my templatetag code).



Barry Rowlingson
Don’t you think you are getting to the point where you’d be better off using a CMS? You’re hacking in hierarchies, menus… I’ve been looking at the Django-based LFC system recently and it does all that. I’ve even managed to hook URLs to non-LFC apps but retain the whole CMS structure… If that makes sense…
Also, what are you using for the wysiwyg text area in the Django admin?
Tim Sutton
Actually the main component of the site is satchmo (online store) but the client wants a few static pages on the site. I could have probably just hard coded links to the about etc in their template, but I wanted to give the client the ability to create new pages etc without needing me to hack templates each time. I’ll check out LFC but I’m guessing its overkill for their needs (and satchmo admin is already complex enough without adding more to it).
The wysiwig component is provided by the tinymce django app and I’ve overloaded the admin base_site.html to include a bit of js that gets added to every admin page to ensure that text areas are rendered as tinymce widgets.
Regards
Tim
Barry Rowlingson
Nice. django-tinymce is the next thing I want to hook onto my current django project.
robos85
Where are You extending all this things? Did You create a special app for this or what?
Thanks.
francis
Great tutorial, this gave me a solid start.
However, I’ve been trying to extend flatpages to integrate MPTT, and have been running into issues.
Would you mind taking a look at this: http://stackoverflow.com/questions/6985359/extending-django-flatpages-to-use-mptt
Thanks.
Tim Sutton
Hi
I had a look but I’m not familiar with MPTT unfortunately. Hopefully someone on stackoverflow can help you!
Regards
Tim
Lapin-Blanc
I really can’t figure out how you order the pages following the self refering foreignkey
flatPages = ExtendedFlatPage.objects.filter(child_of=thePage).order_by(‘show_after’)
I tried it and also modified the order, and it doesn’t seem to work. Am I missing something ?
Tim Sutton
Hi
The page ordering is done by entering an appropriate number and parent in the page’s admin interface.
Regards
Tim