Welcome!
My name is Jacob Kaplan-Moss; I’m one of the lead developers of Django, and I’m now the lead developer at the Lawrence Journal-World in Lawrence, KS, where Django originated.
I’m going to do my best in the next three hours to give you a solid introduction to Django. It’s an amazingly powerful framework, so we obviously won’t be able to touch on everything, but hopefully by the end we’ll have covered enough of the basics to get you rolling on a Django project of your own.
These are the basic points we’ll cover.
I’ll give a quick introduction to some of the philosophies behind Django, and talk a little bit about how to get Django up and running.
I won’t really spend a whole lot of time on either of those topics, though. If you want to know more about the history and philosophy of Django you can come to my session later at OSCON, and installation is quite well documented.
So the bulk of this tutorial will be spent on what I like to think of as the three main legs that Django stands on: Models, Views, and Templates (“MTV”). In other words, we’ll talk about how you define data, how you read and write data, and how you present that data to users.
Along the way we’ll cover two of the coolest advanced features of Django: the automatic interface, and something called “generic views” that will save you a boatload of time.
And if there’s time, I’ll talk about a bunch of little but cool features that might save you time.
Again, this is a quick introduction to a few bits of philosophy. I’ll spend quite a bit more time on this at my session.
This is the one sentence introduction to Django; the “mission statement” as it were. Let me break it down.
A Web framework is software that abstracts common problems of Web development and provides shortcuts for frequent programming tasks.
A good web framework finds the “pain points” of web developers and smoothes them over — but never gets in the way! It should let you work at a much higher level of abstraction, so you don’t need to worry about details of HTTP, SQL, or whatever. Again, it shouldn’t get in your way if you need to “step down” a level.
Python itself is another key “feature” of Django. It’s a beautiful, concise, powerful, high-level language with an amazing community; many things are easier in Django simply because of the tools you can build on top of.
Django also makes a point of not changing anything about how Python works; if you learn Django, you’re also learning Python. This helps down the road.
Regardless of how many powerful features it has, a Web framework is worthless if it doesn't save you time. Django's philosophy is to do all it can to facilitate hyper-fast development. With Django, you build Web sites in a matter of hours, not days; weeks, not years.
This comes directly out of real-world problems. We’re programmers, yes, but we work at a news organization. When a big story breaks, we don’t have the luxury of a long development cycle. Every convenience in Django is there because it makes you more productive.
Django strictly maintains a clean design throughout its own code and makes it easy to follow best Web-development practices in the applications you create.
The philosophy here is to make it easy to do things the "right" way.
More Django philosophy: http://www.djangoproject.com/documentation/design_philosophies/
One day, someone will finally win the OS wars and I’ll be able to quickly walk everyone through installing Django. Until then, the documentation linked here is pretty damn good.
- http://www.djangoproject.com/documentation/install/
- http://code.djangoproject.com/wiki/SetupOnTiger
- http://code.djangoproject.com/wiki/WindowsInstall
I actually think Django’s pretty easy to install — you only need Python and a database (which can by SQLite) — but multiplied by a thousand different environments it gets hard to “teach.”
If you’ve got a working install, however, you should be able to follow along with the rest of the talk pretty well...
Note that to follow along properly you should install Django from SVN; the releases are pretty out of date at this particular moment in time.
The first time you run Django, you need to create a “project”. A project is a collection of settings for an instance of Django — including database configuration, Django-specific options and application-specific settings.
Django comes with an internal development server. It’s a lightweight, pure-Python Web server that builds on the HTTP server included in Python's standard library. We've included this with Django so you can develop things rapidly, without having to deal with configuring Apache until you're ready for production. This development server watches your code for changes and automatically reloads, helping you make many rapid changes to your project without needing to restart anything.
It’s also a great way to make sure everything’s set up correctly.
The next step is to edit settings.py with your project settings. There’s quite a few options there (see http://www.djangoproject.com/documentation/settings/ for all of ‘em), but the DATABASE_* ones are the most important ones at first.
Keep in mind that that settings.py is just a normal Python module with variables, so you can include if statements, import statements, whatever.
Run syncdb to synchronize your database with what’s in your settings files. You’ll run this the first time you get a Django project up and running, and then again every time you add models to your project (more on that later).
Django notices you’ve got the authorization system installed, and creates a superuser account for you. That’ll come in handy later when we play with the admin interface.
For more one settings and the django-admin utility, see:
Now that we’ve got the skeleton of a project working, we’ll start on an app. An app is just a collection of data and code that makes up some sort of logical “unit”. Typically, projects are composed of a number of apps; the cool part is that apps can be reused in multiple projects.
Typically, an app exists to solve a single problem.
The app I’ll use for all the examples in the rest of this tutorial solves this problem: lots and lots of our semi-public data is stored in various third-party repositories around the web. I’ll bet everyone here uses at least one of these services on a daily basis; wouldn’t it be cool if you could aggregate all that information onto your own site?
Well, I wrote a Django app to do that. It’s called “jellyroll”, and it’s available at http://jacobian.org/projects/jellyroll/. I also use it to power my personal site (http://jacobian.org/), so if you want to see it in action you can check it out over there.
It actually ends up being a reasonable amount of code, so obviously I won’t cover all of it. However, if you want to download the code and following along (or browse it in my Trac installation), you’ll probably be able to follow along.
Although relational databases were first conceived of in the 1960s and have been around in some form or another for many years, the web has really taken database use to a whole new level. Almost every web site is backed by one database or another.
When done right, database-backed websites are extremely useful. Instead of the top-down style that dominates in print, databased websites let readers "choose their own adventures" as they move through the site.
The best sites take this to an extreme by providing tools that let readers sift through mountains of data they'd otherwise be unable to process. Helping developers build those tools is the primary purpose of Django. It makes sense, then, to start our discussion of Django development by focusing on how Django handles data modeling.
In a nutshell, a model describes your data. They're like a blueprint: they explain what types of objects you'll use, what fields those objects have, and how it all fits together. In Django, models definitions also contain metadata about your models that Django uses to build APIs -- more on that later.
Many web development platforms want you to define your models using SQL. After all, the data will eventually be stored in a database, so why not simply define your models there first? While this logic makes sense on the surface, we disagree:
- Writing SQL can be tough. Different databases require you to do things differently, and having to write three different versions of your data definitions is the opposite of elegant. Django models work with any database Django knows about.
- Having your data definitions stored in the database instead of in code means it's harder to keep your models under version control. Django does its best to support development best practices, which certainly includes the use of source control.
- Your application will need to work with the database design regardless of how you created it. If you created it using SQL, you'd have to repeat yourself. Don't Repeat Yourself.
- Writing Python is fun! Django does its best to keep you writing Python all the time.
So this is a Django model — the one we use to store the data from del.icio.us. So far there’s not much to distinguish it from the SQL version (besides syntax), but we’ll get there...
First, though, we need to put this code somewhere where Django expects it. This is how you create an app; this’ll create a package with a models.py and a views.py file. That code in the previous slide goes into models.py.
You've now created a model, but there's one more step to getting this model registered with Django. In your settings.py file, you'll see a list called INSTALLED_APPS. This list tells a given Django instance which apps should be considered "active" for that instance. This lets you have a large library of models and select which ones you want enabled on a site-by-site basis.
The next step is to use validate to make sure that everything about your model is set up right. This will check your syntax, but it will also warn you about common mistakes made when defining models.
Once you've created a model, Django automatically provides a high-level Python API for working with those models.
Although it's only a few lines of code, this actually does quite a bit. You can see how objects are created (Django objects are simply Python classes) and saved to the database (all Django objects have a save() method which saves the object to the database).
This is another point of philosophy in Django: it won’t hit the database unless you explicitly ask for it (hence requiring the save() call). This makes it always easy to read your code and figure out where the DB hits are.
Here we’ve got simple data retrieval. Every model gets an objects member which has functions for fetching content from the database. You can get a taste of how the lookup API works with the href__contains business; that’s a lookup for a link whose href attribute contains the string “example”.
Technically, this objects thing is a Manager. I won’t really go into them too much in this tutorial, but now you’ll recognize the term when you read the documentation.
More example code. This comes more or less directly from the Jellyroll delicious updater script. The delicious api (delicious.links.all()) returns an ElementTree instance; we iterate over <post> elements, creating links that don’t already exist.
Recall that earlier I claimed that metadata is one reason to define models in Python? So let’s do a bit of that.
One of the easiest and nicest things we can do is to add a better string representation to our models. Right now when we print models to the console, we don’t get anything very useful.
Adding a __str__ method fixes that. __str__ is used any time Django wants to spit out a “string version” of the model, and it’s very useful.
Here we’ve added a bunch of metadata. None of it is required, but it’s all optional. This is another theme of Django: provide sensible defaults, but allow you to override them easily.
Of course, what’s the point of using a relational database if we don’t use relationships? Let’s add some.
Here are — minus any metadata — the models for the google search history aggregator. Notice the ForeignKey, and notice it’s only defined in one place.
Notice the related_name attribute there; we’ll talk about it shortly.
A number of things to notice here:
- First, note that I can slice the results of DB calls. What you don’t notice is that the queries (filter() and friends) are lazy, so this doesn’t fetch all the objects and then just slice off the first but instead issues the correct limiting clause to the database. This means you can pass these querysets around and they don’t get evaluated until you need them.
- Second, see that the ForeignKey we defined earlier can be used quite naturally as an attribute of the instance just like you’d expect. This does makes the correct DB lookup in the background, but it’s cached so it only gets performed once.
- The third — and coolest — bit shows a query by a related object. I’m searching for all results to searches containing “thinkpad”. Django generates the proper joins behind the scenes. Even cooler is that this can follow as many relationships as you’ve got. I don’t know about you, but writing multi-level joins makes my head hurt, so this looks great to me.
For my money, this is the best feature of Django’s DB API. When we created the model, we only needed to specify the ForeignKey and Django automatically constructed accessors on the “other side” of the relationship. We Didn’t have to Repeat Ourselves.
That related_name business I told you to take note of? It’s used as the name of the accessor (results); Django will automatically choose one if you don’t give it (it would have been googlesearchresult_set in this case).
This lets us both do lookups of searches with results containing given queries (here I’m trying to remember how I found the Open Text Summarizer project), and also access all the results for a single search. In this second line, results is another Manager; that is, it works exactly the same as the “objects” on the result model.
Finally, here’s a many to many relationship (assuming a suitably defined Tag class). The lookup API works exactly like the results one did in the last example, so I won’t spend much time on it.
There’s a huge number of options and coolnesses that I can’t possibly cover in three hours, but these documents taken together cover every single possible thing the ORM layer is capable of:
- http://www.djangoproject.com/documentation/model_api/
- http://www.djangoproject.com/documentation/db_api/
- http://www.djangoproject.com/documentation/models/
The first two are references for the model definition API and the database lookup API; the third link is to a large set of examples.
There's one part of web development we've always hated: writing administration interfaces. Developing the parts of the site that the general public sees is always different and interesting, but the bits that the administrators use to modify the site always are the same. You've got to deal with authenticating users to make sure that they are allowed to edit the content, display and handle forms, deal with tricky validation issues... it's boring, and it's repetitive.
One of the oldest and most powerful parts of Django is the automatic admin interface. It hooks off of metadata in your model to provide a powerful and production-ready interface that content producers can immediately use to start adding content to the site.
Although Django can be — and these days is — used for anything, for us at the Journal-World the admin interface is the raison d'être of our love for Django. With it we can quickly define models and then turn them over to reporters and editors to fill out data.
We think the admin interface is the coolest part of Django -- and most Djangonauts agree -- but since not everyone actually needs it, it's an optional piece. That means there are three steps you'll need to follow to activate the admin interface.
Adding admin metadata to your models.
Not all models can (or should) be editable by admin users, so you need to "mark" models that should have an admin interface. You do that by adding an inner Admin class to your model.
The Admin declaration flags the class as having an admin interface. There are a number of options that you can put beneath Admin; for now we're sticking with all the defaults, so we put pass in there to signify to Python that the`` Admin`` class is empty.
Next, we need to install a few admin models (remember to run syncdb!)
This is also a good time to run the create_superuser.py script (in django/contrib/auth/bin) if you didn’t create a superuser the first time around.
Live demo! For those who don’t have the dubious pleasure of watching my demo, my rambling will probably involve some or all of these nifty features:
- The admin has a fairly powerful user/group/permission system. We don't see it here since we're working as a superuser, but other users can easily have their access restricted.
- Each object given an Admin declaration shows up on the main index page. Links to add and change objects lead to two pages we refer to as object "change lists" and "edit forms".
- Change lists are essentially index pages of objects in the system. There are a number of options that can control which fields appear on these lists and the appearance of extra features like date drill downs, search fields, and filter interfaces.
- Edit forms are used to edit existing objects and create new ones. Each field defined in your model appears here, and you'll notice that fields of different types get different widgets (i.e. date/time fields have calendar controls; foreign keys use a select box, etc.). You'll notice that the admin also handles input validation for you; try leaving a required field blank, or putting an invalid time into a time field and you'll see those errors when you try to save.
- When editing an existing object, you'll notice a "history" link in the upper-right. Every change made through the admin is logged, and you can examine this log by clicking the history.
- Deletions in the admin cascade. That is, when deleting an existing object, you'll see that the admin asks you to confirm the delete action to avoid costly mistakes. What might not be instantly obvious is that this page will show you all the related objects that will be deleted as well. For example, if you go to delete a GoogleSearch, you'll see that the related GoogleSearchResult objects are scheduled for deletion as well.
This is where it gets really interesting.
Up until this point, we've been dealing with how Django simplifies the boring, annoying, repetitive tasks every web developer faces. Now that we've gotten them out of the way, we can finally deal with the exciting, fun part of web development: creating the public facing views.
I refer to views your write yourself as "public views" to differentiate them from the built-in admin interface, but really these "public" views could be anywhere on the spectrum of private-only-for-a-single-user to completely public world-accessibly websites.
This is also the point where Django philosophy of "getting out of your way" comes into play: when writing views, you're pretty much left to your own devices as Django tries to be as unobtrusive as possible.
A view is a "type" of Web page in your Django application that generally serves a specific function and has a specific template. For example, in our delicious link example we’ll eventually have views for:
• the latest 15 links posted to del.icio.us
• an archive of links posted in a given year
• an archive of links posted in a given month
• an archive of links posted on a given day
In essence, a view is responsible for pulling together all the information accessible at a given URL. It's where you look up data from a database, read it in from a file, aggregate or summarize it, etc. Note that a view is not where you define how this data looks; that's up to a template, which we'll cover a bit later.
Like it or not, URLs are a part of your application design. Django forces you to think about them, because these are ugly.
Why do I (the reader) need to know that you’re using PHP? Or the ID of your story? In the bottom case, I could even be signaling a security problem just with my extension!
When a user requests a Django-powered page, Django looks at the ROOT_URLCONF setting, which contains a Python path to a module. Django loads that module and looks for some urlpatterns.
The first step of writing views is to design your URL structure. You do this by creating a Python module, called a URLconf. URLconfs are how Django associates a given URL with given Python code. Here, we’re adding the link index view at /links/ and a view for URLs like /links/2006/. We’ll leave out the rest of the views (month/day) until later, and you’ll soon see why.
A urlpattern is simply a list of regular expressions and function names. Django walks down the list until it finds a regex that matches; it then calls the provided function.
This is worth a review. When somebody requests a page from your Web site -- say, /links/2006/, this is what happens:
- Django will load myproject.urls Python module, because it's pointed to by the ROOT_URLCONF setting.
- Django then finds the variable named urlpatterns and traverses the regular expressions in order. When it finds a regular expression that matches it loads the associated Python package/module. In this case it’s jellyrolls.views
- Django then calls the function you’ve given as the last part of that callback string. Here it’s list_for_year.
- The view gets a "request" object as the first parameter. Here, the year param comes from the (?P<year>\d{4}) part in the regex. Using parenthesis around a pattern "captures" the text and sends it as an argument to the view function; the ?P<year> defines the name that will be used to identify the matched pattern; and \d{4} is a regular expression to match exactly four digits (i.e., a year). (Yes, this is not Y10K compatible.)
- The view returns a response, which is passed back to the browser (more on this next...)
The URL dispatch engine is pretty simple (I think), but there’s comprehensive documentation at http://www.djangoproject.com/documentation/url_dispatch/
So here’s perhaps the simplest possible view we could write for the link_index view. It shows one important thing, though: every single view takes a request (and possibly other options) and returns an HttpResponse object.
OK, a little more complex here; now we’re getting some data from the database and rendering it.
However, this should be pretty obviously bad: we’re building the page’s design right into the view, and setting ourselves up for disaster. If you want to change the HTML, you’d need to edit the Python, and you shouldn’t have to do that.
A little better. Now we’re loading a template and passing in some stuff to it (we’ll gloss over the template for now and come back to it later on).
However, in the interest of keeping code short, let’s modify this to use a shortcut instead.
If a view describes what data is displayed on a given page, the template describes how that data looks. This distinction is important since it will easily let you change how your site looks without having to change how it behaves. Most web development teams already have different people who develop the code who design the templates; this division in Django strives to match that natural division of labor.
Django's template language is designed to strike a balance between power and ease. It's designed to feel comfortable to those used to working with HTML. If you have any exposure to other text-based template languages, such as Smarty (http://smarty.php.net) or CheetahTemplate (http://www.cheetahtemplate.org/), you should feel right at home with Django's templates.
We have a deep love for Django's template language, but not everyone shares our passion. Luckily, like most of Django, there's nothing that binds you to using Django's template engine instead of the one of your choice. You can simply write views that render content using your favorite engine instead.
Django's template language has particularly been designed to be designer-friendly. We think most programmers -- us included -- have the design sense of a color-blind three-year-old, so we prefer to leave the designing to the professionals by giving them a template language they can understand.
Here’s a nice, complete, valid template we could use for the “recent links” view.
I’ll go over the salient features.
First thing to notice is that templates are simply text files. Much of the time you’ll be using them to generate HTML, but you could generate any text-based format (XML, CSV, YAML, whatever).
These are variables. When the template engine encounters a variable, it evaluates it and replaces it with the value of the variable.
The variables are evaluated against the context that we created in the view earlier.
Because template authors shouldn’t need to know Python, the dot in a template tries dictionary lookups, attribute looks, list-index lookups, and will call callables that take no arguments.
Finally, if none of those work, the contents of the setting TEMPLATE_STRING_IF_INVALID will be returned (which defaults to the empty string).
This is a filter.
Think of filters as a pipe: a variable goes in one end, and something else comes out the other.
Going with the pipe analogy, filters can be “chained” — the output of one passed onto the next. The above is a common idiom for dealing with user-submitted text; it escapes potentially dangerous HTML, and then turns linebreaks into <p> tags.
Some filters take arguments, which are given after a colon in double quotes. This example truncates the text to 30 words.
These are tags. Tags are more complex than variables: Some create text in the output, some control flow by performing loops or logic, and some load external information into the template to be used by later variables.
Some tags require beginning and ending tags; {% for %} is one of those tags. Ending tags are always of the form {% end<tag> %}.
The most powerful -- and thus the most complex -- part of Django's template engine is template inheritance. Template inheritance allows you to build a base "skeleton" template that contains all the common elements of your site and defines blocks that child templates can override.
Understanding inheritance begins with understanding the problem it solves. The template we designed previous looks just fine, but what happens when we go to create a template for the year archive? We'd start by duplicating all the DOCTYPE, <head>, etc. crap -- and that's a major pain. Now imagine you’ve got thousands of templates, and it’s clear you need a way of keeping all the repeated code in one place.
This template, which we'll call base.html, defines a simple HTML skeleton document that we'll use for all the pages on the site. It's the job of "child" templates to fill the empty blocks with content.
In this example, the {% block %} tag defines three blocks that child templates can fill in. All the block tag does is to tell the template engine that a child template may override those portions of the template.
So now we can go back to the link index template and “extend” the base template we just created.
The {% extends %} tag is the key here. It tells the template engine that this template "extends" another template. When the template system evaluates this template, first it locates the parent -- in this case, base.html.
At that point, the template engine will notice the three {% block %} tags in base.html and replace those blocks with the contents of the child template. So the title we've defined here will be used, as will all the template code to loop over and display the link list. The skeleton from the base template will be filled in with the content from the child one.
Note that since the child template didn't define the footer block, the value from the parent template is used instead. Content within a {% block %} tag in a parent template is always used as a fallback.
One common way of using inheritance is a three-level approach:
- Create a base.html template that holds the main look-and-feel of your site.
- Create a base_SECTION.html template for each "section" of your site. For example, base_news.html, base_sports.html. These templates all extend base.html and include section-specific styles/design.
- Create individual templates for each type of page, such as a news article or blog entry. These templates extend the appropriate section template.
This approach maximizes code reuse and makes it easy to add items to shared content areas, such as section-wide navigation.
Here are some tips for working with inheritance:
- If you use {% extends %} in a template, it must be the first template tag in that template. Template inheritance won't work, otherwise.
- More {% block %} tags in your base templates are better. Remember, child templates don't have to define all parent blocks, so you can fill in reasonable defaults in a number of blocks, then only define the ones you need later. It's better to have more hooks than fewer hooks.
- If you find yourself duplicating content in a number of templates, it probably means you should move that content to a {% block %} in a parent template.
- If you need to get the content of the block from the parent template, the {{ block.super }} variable will do the trick. This is useful if you want to add to the contents of a parent block instead of completely overriding it.
For more on templates from the point of view of a template author, see http://www.djangoproject.com/documentation/templates/. There’s also a guide to the templates for Python developers at http://www.djangoproject.com/documentation/templates_python/
Here again is a recurring theme of this book: at it worst web development is boring and monotonous. So far you've seen how Django tries to take away some of that monotony at the model and template layers, but web developers also experience this boredom at the view level.
Django's generic views were to developed to ease that pain. They take certain common idioms and patterns in view development and abstract them so that you can quickly write common views of onto data without having to write too much code.
Here’s a common way to do the “links for year” view we talked about earlier.
There’s no real problem here until you think about doing a similar view for searches by year, and for last.fm tracks by year, and... You can see how you’d end up writing a whole lot of boilerplate code; that’s not good. Generic views recognize these common tasks, and abstract them away.
Here’s a conversion of our links example to use generic views.
All generic views use the third urlpattern argument, an “info dict”. Here we’re telling these views what queryset to use and the date field we want to browse by.
Once we’ve done this, all we need to do is write some templates!
Django comes with quite a few generic views:
- The “simple” views take care of cases where you just want to render a simple template (with no context) or redirect to another page.
- The list/detail views handle doing (possibly paginated) lists of objects and individual detail pages.
- As you’ve seen, the date-based views take care of browsing by year, month, and day; there is also a week view, an object detail view, and a “today” view.
- The create/update/delete views handle creating, updating, and deleting objects.
Read all about generic views: http://www.djangoproject.com/documentation/generic_views/
There’s so much more to Django... I could easily fill another 3 hours with cool stuff. This is just an overview of all the cool extras that come with the framework.
- You can easily extend the template system with your own tags and filters.
- There’s a super-simple sessions framework for both anonymous and logged-in users.
- Speaking of logging in, nearly all of the work of auth/auth is done for you.
- The caching framework will save your butt when you get slashdotted. It’s got pluggable backends and can work with your database, memcached, a disk-based cache, etc.
- Django’s iternationalized and localized, and the built-in bits are translated into 29 languages.
- There’s a built-in comments framework that lets you easily add comments to any object.
- And a bunch more -- I can think of the paginator and generic relations right now, but there’s a lot of really cool bits.
I like to end all my talks with a bit of evangelism. Here’s what some notable and not-so-notable people had to say about Django.
Tempting to take this quote out of context, but I’ll refrain. Jeremy’s referring to Django’s RSS framework here.
http://www.oreillynet.com/onlamp/blog/2006/06/django_rss_made_stupidly_simpl.html