I remember when people were just getting excited about Rails. It was all about the demo. Build a blog (or whatever) in 30 minutes. It was so elegant that even designers were getting into it. I tried, but I hit a wall. I’m not a programmer, I probably never will be, and Rails is for programmers. Even the templates use Ruby. That’s great for some, but it’s not for me.
As a designer for World Online, I’ve been involved with the development of Django since before it was Django. I may have been the first person other than Adrian and Simon to use the template language in production. I told Simon halfway through that first project that I’d never used a template language that made so much sense to me. From the beginning, I’ve always held out hope that I would be able to take advantage of that on my personal projects, without bugging the programmers to write code for me.
I’ve finally been able to realize that dream (sniff), thanks to a little feature in Django called generic views. Jeff showed the world what a non-programmer can do with Django’s generic views (and a lot of pictures of drunk girls). So what’s so special about something so… generic?
The missing link
There are really three basic steps you need to build a basic web app in Django. You need to define models (what your data is), views (how requests for your data are returned) and templates (how your data is displayed). I know how to write templates (and any web designer who’s ever customized a Movable Type template or hacked up some PHP includes does too).
And I can get my head around a model. It’s just a definition of objects and their attributes. Conceptually, it’s a lot like writing CSS rules or Javascript objects. You’re just declaring properties, there’s no advanced logic required.
Views are another story. In a framework like Django, views are where what programmers like to call “business logic” happens, with functions and routines and queries and optimization and all that scary stuff. In a word (or two): actual programming.
But most of the time I don’t need anything that complicated. Most of the time—probably 80% of the time—I need the same simple views: a list view, a detail view and maybe some date-based archives.
That’s where generic views come in. They’re a set of simple, commonly-used views that are built in to Django for reuse in your apps. They’re designed as a utility for programmers to save time creating the same routine code so they can get on to more important things, but they’re pretty handy on their own too.
In fact, it’s possible to build an entire app or site using entirely generic views, without writing a single line of actual view code. I did it this weekend. Want to see? OK, let’s go.
How I built a portfolio app in 30 minutes with Django
I haven’t updated my portfolio in three years. To give you an idea of how long that is in Internet years, it was one of the first sites I coded entirely by hand, and one of my first all-CSS layouts. I’ve reluctantly added a few pieces here and there over the years, but I cringed every time I went back to that code. I considered recreating it with a hacked-up Movable Type blog, but that ship has sailed.
Now that I’ve got my site on Django, I figured I’d be able to put something together that would be simple enough that I couldn’t put it off any longer, and flexible enough that I might actually want to keep it updated.
Note: I’m just going to document the steps I took to put together a really simple portfolio app. I’m not writing it as a tutorial, so I’m not going to cover a lot of background or fill in the blanks step by step. If you do want to follow along with your own code, I’d suggest you check out Jeff’s intro to Django for non-programmers and run through the Django tutorials first. If you want more information at any point about how something works, you can find it in the official Django documentation.
Step 1. Write the model (10 minutes)
I already have a project for the apps I use on my site, so I started by creating a new application within my project.
manage.py startapp portfolio
I decided I needed three models to put it together: Project, Client (so I could group projects with the same client) and Medium (web, print, etc.—also for grouping projects). Since I wanted to keep it simple, I could have just used text fields for client and medium, but since those make sense as relationships and it’s easy enough to define them separately up front, I did.
Here’s what my models look like
(I’m linking these out to text files so my post formatting doesn’t crap on the Python indenting.)
Even if you don’t know Python, I bet you can figure out most of what’s going on just by reading the code. I’m defining the three objects and their fields, along with some extra information in the Meta and Admin sections, and some simple functions to tell Django how to refer to the objects by name (using Python’s “repr” object) and URL within the site.
Client has two fields (name and URL). Pretty simple. Medium just has one field (name), but I had to include some extra information so Django knows what the plural of medium. The db_table property gets used to name the database table, and verbose_name_plural is used when referring to groups of objects in the admin interface.
Project has a few more fields, but they’re all pretty simple. Django has a lot of different types of fields for different types of data.
- Name is a simple character field which will appear as an
inputin the admin interface. - Slug is a special type of field that can only contain lowercase letters and dashes. It’s used in URLs.
- Project URL is a URL field. Django will check that the URL is valid before saving.
- Description is a text field, which will appear as a
textareain the admin interface. - Client is a foreign key relationship to the Client object we defined earlier. It will appear as a
selectmenu in the admin interface, with a list of all the clients in the system. - Media is a “many to many” relationship to the Medium objects, which means we can select more than one medium for each project. (There are many projects related to many media, hence “many to many”.)
- Disciplines is a simple character field. It could just as well be a many to many relationship to a “discipline” object, but I felt like that was overkill for this project.
- Completion date is a date field. It will appear as an
inputin the admin interface, with a calendar widget for choosing a date. - In development and Is public are boolean fields (yes or no). They’ll appear as checkboxes in the admin interface.
- Overview image and Detail image are URLs where I will paste links to the project images. These could easily use Django’s
ImageFieldfield type, which would provide a widget for uploading images directly through the admin interface, but since I’m not hosting my media on the same server as my Django setup, so I’ll just use URLs instead.
The get_absolute_url function tells Django how to find each project by its URL on the site. I’ll define the URLs later, but I know now how I want them to work. In this case I want my portfolio URLS to start with /work/ and just stick the slug field for each project on the end as a unique identifier. So an example URL would look like: /work/project-slug/.
I’ve also added an extra field to the Meta section, which tells Django how I want to sort projects when it retrieves them from the database. In this case, I want to keep them ordered reverse chronologically by date published, with the most recent projects first.
That’s all I need, there’s models are done. I’ll even give you time to look up the field types in the docs.
All I have to do now is tell Django to activate the models and reload my web server and log in to the admin interface and voila!

I’ve got a web interface for adding and editing the objects I just defined. It’s not magic and, just like any language, Django’s model syntax takes some learning and getting used to. It’s not English, but it’s not SQL either.
2. Map the URLs (5 minutes)
So now Django knows what all our objects are. Now it needs to know where to display them when they’re requested on the site. When a user requests the URL for a project on our site, we want them to get the right project or list of projects.
Every Django project has a set of rules that map URL patterns (defined using regular expressions) to views that retrieve objects from the database and display them to the browser through templates. Since we’re using Django’s built-in generic views, all we need to do is tell Django which generic view to use for each URL pattern.
Here’s what the URL patterns look like for the portfolio
Skip the first few lines for now and let’s look at urlpatterns part, specifically the first one.
The first part (before the first comma) is the regular expression to match the requested URL against. In this case the expression is easy, since all I want to match is /work/. I don’t want to match anything but /work/ though, so I do need some regex magic to start the match when it finds “work” and terminate the match after it hits the slash. That way I don’t match /work/stuff/ or /posts/work/ and serve up the wrong view in the wrong place.
The second part specifies the view I want to invoke with the URL I just matched. In this case, I’m using Django’s generic view for object lists. I found out how to specify this view from the generic views docs but it’s in part four of the tutorial as well.
The third part specifies what object I want to display and what template I want to use to display it. These extra arguments are contained in what’s called a dictionary, which in Python is basically just a list of variables and their definitions. In this case I’m referencing an external dictionary, info_dict, which I’ve defined earlier in the file, just above the urlpatterns.
All this dictionary does is get a list of all the projects in the database and assign it to a variable queryset. The object_list generic view looks for that queryset variable to find the objects to display. (The reason I’m defining this in a separate dictionary is because I’ll use the same info again for the next view.)
I’m also adding another variable just for this view, template_name, which specifies which template in our template directory to use to render this view.
The second URL pattern follows the same form. Regular expression to match the URL (/work/ followed by the project slug), the view I want to use (in this case the generic object detail view), and the info dict to pass to the view, with an extra argument telling the detail view which field to use for the slug in the URL.
That’s a lot of explanation for a few lines of code, but you don’t actually need to know any of it. If you’re good at copying and pasting, you can do it this part in three minutes instead of five.
Now, if I reload my web server and bring up one of the URLs I just defined, Django gives me an error page saying template does not exist.

Perfect!
Step 3. Make the templates (10 minutes)
Now I’m in the home stretch. This is familiar territory. I can start with a blank file, add my markup and some simple template logic and I’m good to go. If you haven’t already, spend some time with the Django template documentation for HTML authors to get the idea.
I start by creating a template called projects_list.html in a folder called portfolio in my templates directory.
Here’s what my template looks like for the project list
That’s it. This template extends another template called base_work (see the docs for a primer on template inheritance and extension) which includes all my surrounding page markup, so all I need to define here is the content specific to this page.
The object list generic view provides a list of the objects I passed to it (in this case projects) in a variable called object_list. I’m looping through each object in the list, checking if it’s marked as public and displaying the info for each one (but you knew that).
As an added bonus, I can use Django’s template filters to run my content through different transformations. In this case I’m loading a template tag library included with Django called markup ({% load markup %}) and using the filters it provides, in this case Textile.
Step 4. Add style (5 minutes)
OK, I cheated here. I already had most of the style done, so this didn’t take long. But at this point, we’re not even using Django anymore, so you’re kind of on your own. Mileage may vary.
Step 5. Gloat
You cheated again! That’s not an app, that’s just one page!
OK, so I kept it really simple. So simple that I’m not even using the detail views I created (yet). By the time I got this far I just couldn’t think of what else I would put on a detail page for each project, and I’m really fond of other people’s portfolios that are all on one page. How very Real™ of me.
But someday I probably will want detail views, and all I’ll have to do is add a template. Archives by date? Three lines of code and three templates. Archives by medium? One line, one template.
What else can you possibly do with generic views?
How about a blog? This entire site is powered by generic views (with some exceptions, including search and the home page, neither of which are too complicated on their own.)
But I can’t make money from a blog!
OK, how about ecommerce? I built this catalog site for a friend of mine with nothing but generic views and a PayPal account. No programmers in sight. Go ahead, buy something. I dare you.
On both of these projects, I spent more time designing and tweaking style and markup than I did writing application code. I’d be ashamed to call it programming.
Sure, it’s not revolutionary functionality, but it’s not something I could have done on my own a year ago with PHP or a hacked-up blog CMS. And the great thing is, I can hand off a project like this to a client (as I did with the jewelry site) with a fully-functioning backend interface with confidence that it’s actually going to work. And I don’t have to split the check with a programmer.
On a completely unrelated note, you might be interested to know that I’ve updated my portfolio for the first time in THREE YEARS.
If you’ve got the generic views bug and you want to know how to really use them to your advantage, check out James Bennett’s tips on how to get the most out of generic views.

Comments / Add yours…
Comments are closed.
Comments have been closed for this post.