Jeff Croft

I’m a product designer in Seattle, WA. I lead Design at a stealthy startup. I recently worked at Simply Measured, and previously co-founded Lendle.

Some of my past clients include Facebook, Microsoft, Yahoo!, and the University of Washington.

I’ve authored two books on web and interactive design and spoken at dozens of conferences around the world.

But seriously, who gives a shit?

Blog entry // 08.05.2008 // 12:07 PM // 39 Comments

Default” templates in Django

Django’s templating system is one of its strongest points, in my opinion, and one of it’s coolest features it the “fallback” system used for locating templates. When used wisely, it can allow for a situation in which you can literally design an entire site by creating only one HTML file.

In Django’s settings.py file, you configure your TEMPLATE_DIRS variable. This is simply a Python tuple representing filesystem paths to the locations your templates reside. For example:

TEMPLATE_DIRS = (
  '/home/jcroft/webapps/django/jeffcroft.com/templates2',
  '/home/jcroft/webapps/django/jeffcroft.com/templates1',
)

Here, we’ve got two directories containing templates: templates1, and templates2. Whenever a Django app attempts to load a template, it will look in these two paths to find it. For example, if your blog application needs a template called blog/entry_detail.html, it will first look for it in .../templates2/blog/entry_detail.html. If it’s not found, it will “fallback” to .../templates1/blog/entry_detail.html (if it’s still not found, Django will return an error.)

Most Django sites are made up of several plug-and-play “apps”. For example, jeffcroft.com includes a blog app, a bookmark app, a tumblelog app, an events app, and more. Each of these apps are collected into a “project”, which often represents a single site (jeffcroft.com is a Django project, for example). As I build new Django apps, I store them in a single directory (which is a Python module). In my case, this directory is called “savoy” (a clever name we’ve come up with for our suite of Django apps). So, we’ve got savoy/blogs, savoy/events, and so forth. But we also have savoy/templates. This is what I like to call “default” templates. This directory contains simple templates for all the apps.

All of the templates in this directory “extend” base.html. If you aren’t familiar with Django’s template inheritance model, this basically means that base.html will act as a parent template to, for example, blog/entry_detail.html, and the contents of entry_detail.html will get interjected into base.html. If this isn’t making sense, you might check out a post I wrote on Django template inheritance back in 2006.

When I set up a new Django project (again, such as jeffcroft.com), I include the Savoy default template path in TEMPLATE_DIRS, but precede it with a “site-specific” template path. Like this:

TEMPLATE_DIRS = (
  '/home/jcroft/webapps/django/jeffcroft.com/templates',
  '/home/jcroft/webapps/django/savoy/templates',
)

We know the savoy/templates directory is full of default templates. Since we’ve just set it up, let’s assume the jeffcroft.com/templates directory is empty. So what happens now when our blog app needs blog/entry_detail.html? It looks in jeffcroft.com/templates/blog/entry_detail.html, but doesn’t find it, so it falls back to ...savoy/templates/blog/entry_detail.html. Remember that entry_detail.html, along with all the Savoy default templates, extend base.html. So the same thing happens again: the system looks in jeffcroft.com/templates/base.html, doesn’t find it, so it uses savoy/templates/base.html, instead.

Remember I said this could lead to a situation where all you have to do is create one HTML file to design a site? base.html is that one file. If you create a new base.html in jeffcroft.com/templates, it will be used instead of the one in ‘savoy/templates. That base.html file will contain links to your CSS files, your header, footer, sidebar, and so forth — the bulk of your design. Suddenly, by creating one file, you’ve got a full working site in your look and feel.

We used this model extensively at World Online when I was there. We sold a Django-based CMS product called Ellington, which came with a set of default templates. These defaults weren’t much to look at, but they did output nice, clean, semantic HTML for every app and every page in the system (and there were a lot). When newspaper or magazine would purchase Ellington from us, all they had to do was create a base.html file encapsulating their site’s look and feel. Suddenly, they have a working site that functions for their brand.

After that, they may dig in and find that individual templates, such as our entry_detail.html example, weren’t quite to their liking. All they needed to do was make a copy of ellington/blogs/entry_detail.html into their site-specific directory (say, washingtonpost/blogs/entry_detail.html) and tweak to their heart’s content.

It’s really an elegant system. If you’re creating Django apps, I suggest that you create a set of default templates that output clean, simple HTML and extend the Django convention of base.html. Doing so lets you (or others, if your apps are publicly available) get started using them very quickly by simply creating a replacement base.html file. Nathan Borror’s Django Basic Apps are a great example of a suite of apps that work exactly like this.

Comments

  1. 001 // Joshua Works // 08.05.2008 // 12:25 PM

    This is a brilliant tip. As our team builds more and more Django apps for use at K-State, we’re definitely going to need to take advantage of this. Thanks!

    Now where do I download Savoy? :)

  2. 002 // Jeff Croft // 08.05.2008 // 12:44 PM

    Now where do I download Savoy? :)

    Currently, Savoy is not for public consumption. One day, it may be. If/when that day comes, you’ll definitely hear about it here. :)

  3. 003 // Maura // 08.05.2008 // 1:10 PM

    Another great application of this is overriding admin templates.

  4. 004 // Daniel Lindsley // 08.05.2008 // 1:42 PM

    Interestingly enough, a topic along these lines has come up here this week. I wrote a small chunk of code was written to allow someone to take the “default” concept one step further, in that a specific template can load a default template with the same name and make further tweaks without having to duplicate the entire contents of the default template. Great for branding, per-site customizations, etc.

    Mr. Borrow is (maybe?) currently testing it for me. If it stands up to his needs, I’ll post the chunk someplace.

  5. 005 // Jeff Croft // 08.05.2008 // 1:47 PM

    @Maura: Absolutely! Good point.

    @Daniel: Yeah, Nathan and I were actually talking about that at dinner a few weeks ago, which may have been the spark that eventually led to you writing the tag. The idea of extending the default template is something I’ve always wished was available, so I’m happy to hear you’ve done something about it. Definitely let me know when it’s publicly available!

  6. 006 // Henrik Lied // 08.05.2008 // 3:01 PM

    This is a really great idea, Jeff! I’m in the process of building a commercial CMS myself (without the admin-app, believe it or not!), and this was of great help!

  7. 007 // Scott McCracken // 08.05.2008 // 3:04 PM

    When it comes to rapid site development, this is an absolute life-saver.

    Another great example involves creating a globally available 404 template (or other server errors, for that matter). Refinements to this template can be made on a project level if needed, but the power of having a fall-back is really quite powerful.

  8. 008 // MrX // 08.05.2008 // 4:24 PM

    I think that’s nothing unique, afaik ezpublish CMS (ez.no) uses similar system

  9. 009 // Jeff Triplett // 08.05.2008 // 4:47 PM

    @MRX regardless of how unique it is, it’s a damn nice feature that’s been in Django from day one.

    The idea of extending a base template is powerful but what @Daniel is trying to do is a programmatic fix for something that you can do already with the existing template engine. I’m looking forward to see his solution though.

    Here’s how you can do it with the existing template system:

    • In your default templates folder create a blog/entry_detail_base.html that has your template in it then create a blank blog/entry_detail.html that simply “{% extends “entry_detail_base.html” %}

    • The disadvantage is that you’ve created an extra file that is only a placeholder. The advantage is you can extend the _base file from your other template folders so that you can just override a few blocks of the _base template. This makes branding super easy. Food for thought…

  10. 010 // Simon Willison // 08.05.2008 // 4:52 PM

    The idea of extending the default template is something I’ve always wished was available

    There’s actually a relatively devious trick you can use to get that working in stock Django, without any modifications. You inspired me to write it up on the Django wiki:

    http://code.djangoproject.com/wi…

  11. 011 // Jeff Croft // 08.05.2008 // 5:42 PM

    @Jeff: If I’m understanding you correctly, you’re describing a very useful trick, but not one that solves the same problem Daniel (and Simon’s) solution(s) solves. But maybe I’m misunderstanding. :)

    @Simon: Woah. Clever. Never thought of that. Thanks! :)

  12. 012 // kevin // 08.05.2008 // 7:01 PM

    very cool post jeff. learned something new today. and simon’s wiki add is a great reference in addition.

  13. 013 // Skip Hire // 08.15.2008 // 8:21 AM

    This is quite similar to my custom PHP I plod away at from time to time.

    I use the URL to go to a method and each method has a mode. By default the mode is HTML, but I also have XML and AJAX. This allows me to run everything from index.php, where I generally just call classes. Anyway, if you are on page domain.ext/blog, it will run the html_blog method and if a template exists called _blog.xsl it will include it (if set to include templates). If no template is included it will die. However, if no method was called and additionally if no template could be found, it will run a fallback method where you can code custom responses.

    Using XSL you can also very easily code an entire site in one file, because one file can have multiple templates which can be called and you also have if-else ability in XSL.

    One thing I have been struggling with in my coding hobby is as follows Maybe some of the brains here can cast there eyes on it:

    As mentioned above, when you are on a page, it will call the corresponding method. So when I am on domain.ext/my_page, the html_my_page method is called. However, there is a problem that when I am on the page domain.ext/my-page, I am unable to call a method for it as the hyphen is not a valid character for a method name. Any ideas anyone?

  14. 014 // Jeff Croft // 08.15.2008 // 5:09 PM

    Any ideas anyone?

    Sure. Use a system like Django that doesn’t tie URLs to method names. :)

  15. 015 // Nata // 08.29.2008 // 7:14 AM

    Nice post :) Reading your posts always it is possible oneself of something interesting to learn :)

  16. 016 // David Cramer // 09.25.2008 // 12:30 AM

    This is the same way we’re handling it on our platform, which will potentially be on thousands of websites (all in-house). Override the CSS, and tweak any custom templates within your template dir, and you’re set.

  17. 017 // mzee@richo // 10.03.2008 // 9:34 AM

    Nice stuff , i like that ? just what i do , you know . keep it up bro

  18. 018 // chad // 10.27.2008 // 6:38 PM

    are you still thinking about providing a download? thanks, cc

  19. 019 // Daniel Lindsley // 11.05.2008 // 8:41 PM

    @The Esteemed Mr. Croft - I’ve finally gotten around to posting the code for the template tag I had mentioned. Full write up at http://toastdriven.com/fresh/ext… and the source at http://www.djangosnippets.org/sn….

  20. 020 // kiev // 11.11.2008 // 10 AM

    Fantastic! It’s really an elegant system.

  21. 021 // Jupiter Florida // 11.29.2008 // 2:05 AM

    What I really want is for somebody to complete the Velocity integration using Jython. I don’t want to use the version that’s made for Python.

  22. 022 // Landing Page Templates // 12.07.2008 // 4:09 AM

    I just love these template. They are so good.

  23. 023 // Landing Page Templates // 12.07.2008 // 4:10 AM

    I just love these template. They are so good.

  24. 024 // Micha Pietsch // 12.14.2008 // 7:09 AM

    I wonder that Nathan Borror put the default templates one level below the app. Seems that doesn’t meet your “…/savoy/templates”. It rather looks like “…/savoy/blog/templates”. Or am I wrong?

  25. 025 // Landing Page Templates // 12.17.2008 // 10:36 PM

    Thanks for the wiki, nice tip.

  26. 026 // Free Landing Page Templates // 12.17.2008 // 10:37 PM

    Forgot to mention, Django is awesome.

  27. 027 // Landing Pages // 12.17.2008 // 10:37 PM

    And the URL structure is different as well it’s not /savory/templates.

  28. 028 // Florida Web Design // 01.09.2009 // 3:39 PM

    From the previous post titled: Jupiter, Florida - I did finally find a template engine similar to velocity extension for Python. It’s called Cheetah. There is also AirSpeed. That’s actually Velocity, but it has a little bit of the functionality stripped. Very little is missing though.

  29. 029 // sohbet sitesi // 01.24.2009 // 5:43 AM

    I agree fully on Pandora. Good page and thank you for share

  30. 030 // sohbet sitesi // 01.24.2009 // 5:55 AM

    Cool post . .This means that there is no way for other people Thank you

  31. 031 // cheap prom dresses 2009 // 02.11.2009 // 5:44 AM

    It’s called Cheetah. There is also AirSpeed. That’s actually Velocity, but it has a little bit of the functionality stripped. Very little is missing though.

  32. 032 // log splitter // 03.11.2009 // 3:38 PM

    Most Django sites are made up of several plug-and-play “apps”. For example, jeffcroft.com includes a blog app, a bookmark app, a tumblelog app, an events app, and more. Each of these apps are collected into a “project”, which often represents a single site (jeffcroft.com is a Django project, for example).

    Well Well, honestly, thanks for this info

  33. 033 // conficker // 03.25.2009 // 4:34 PM

    I love Django’s default templating system for use in building an application, but for making an API that user’s can program in, Cheetah templates are much better.

  34. 034 // Conficker // 03.31.2009 // 9:21 PM

    keep em coming…i love these templates!

  35. 035 // dead sea salt // 04.24.2009 // 7:02 PM

    Dijango is awesum

  36. 036 // Caravan Porch // 08.21.2009 // 1:28 AM

    Template designing requires one creative mind in order to come up with appealing designs. I have nothing but respect to Django with these templates.

  37. 037 // ???? ?????? // 12.27.2009 // 12:15 PM

    ? ??? ?? ???????, ???? ? ?????, ??? ??? ???? ?????, ?? ????? ??? ??????? ???????

  38. 038 // cheilitis treatment // 01.07.2010 // 7:53 PM

    I dont think I have ever used a django template before? interesting

  39. 039 // cheilitis treatment // 01.07.2010 // 7:54 PM

    I dont think I have ever used a django template before? interesting

Tags for this entry