Today we’ll be learning the gist of Django, a pythonic web framework. In the spirit of a stereotypical web framework tutorial, we’ll produce a primitive blog with the minimal amount of code possible. Yellow background color text indicates text to be entered into a command line/terminal and blue indicates code. If you’ve followed my django screencast, you’ll be familiar with the most of what this tutorial covers. However, I’ll explain the code in this tutorial in more depth than I did in the screencast. If you like to know what everything does, this article is for you.
If you’ve never dealt with (or even seen) python code before, I recommend skimming through python’s official tutorial. The most notable difference between python and most other languages is that tabs/spaces are used to indicate code blocks. So an if statement would look something like this:
if myvariable == True:
print “myvariable is True”
print “Always printed”
One other thing to note is that boolean variables are True and False (case-sensitive). It’s also a good idea to dust off your terminal / command line skills (cd, ls, etc.).
We have a nice back-end system, but regular users can’t see anything. After all, end-users are only able to see a 404 page and the admin login page (if they’re clever enough to find it). Let’s add more.
First, we need to edit urls.py to hook urls to our functions we’ll create after. In order to separate specific details about the urls, we’ll create a new urls.py for our posts so that if we wish to quickly insert an application to another project, we can simply add one line to urls.py to link it up. So in the project’s urls.py change urlpatterns to:
urlpatterns = patterns(”,
(r’^', include(‘posts.urls’)),
(r’^admin/’, include(admin.site.urls)),
)
Like the admin url system, we’ll make the project use urls.py to define (which we will shortly) url mapping. Since this is a pretty big feature for our blog, we’ll make it the base url.
Create a new file, urls.py, in your posts app directory. A urls.py for an application is exactly identical to one in a project. We’re only going to implement two views for the front end:
We’ll implement the comments later using Django’s built-in commenting system. But first we’ll need to display the blog posts.
Back to the new urls.py. The code before the explanation.
from django.conf.urls.defaults import *
from models import Post
queryset = {‘queryset’: Post.objects.all()}
urlpatterns = patterns(‘django.views.generic.list_detail’,
url(‘^$’, ‘object_list’, queryset, name=”posts”),
url(‘^(?P<object_id>\d+)/$’, ‘object_detail’, queryset, name=”post”),
)
We’re using Django-provided generic views, which removes the need to write view code for common functions: CRUD operations. So we’ll use it. The prefix helps makes each url mapping clean.
We imported our Post model so we can fetch data from our database and pass it to our view. Generic views accept an additional parameter named queryset which contains the object query the view can use to fetch database results. For models queries are done via objects property which allows various ways to fetch model instances. It’s worth noting that querysets are lazy evaluators, so the second url pattern’s view will automatically narrow it down to a particular id without polling the database for all the posts.
Querysets are Lazy Querysets avoid querying the database unless it can’t. This means something such as: doesn’t actually hit the database at all. Only something that requires the actual data from the results such as:
performs the query when it’s needed. Accessing an index automatically executes the queryset, but only once: myqs[0] # fetches all the query values, returns the first result myqs[1] # doesn’t query since above did, returns the second result You can build new querysets based on other ones without hitting the database: import datetime newqs = myqs.filter(pub_date__lte=datetime.datetime.now()) # no database call newqs = newqs.exclude(id=1) # no database call for post in newqs: # hits the database once and cached for iterating print post.title For more informtation see “Querysets are Lazy” and “When QuerySets are evaluated” in Django’s documentation. |
Another new thing is the url function over the tuple we normally use. It provides the same functionality as the 3-tuple we’ve used earlier with some added features. One most notable feature is the name keyword. This allows us to reference to a particular url by a specific name. Which will be helpful when we write our templates.
Finally if you looked at the regular expression for the post url pattern, you’ll see the regular expression named capture feature. If you’re not familiar with regular expressions too much, it captures to input value in parenthesis and passes that value to the view with the keyword named object_id. In this case, we match at least one digit (\d+) which the generic view use as the id.
If you looked out our urls.py code above, you’ll notice we don’t have any post ordering. This is simple to accomplished by simply replacing the Post.objects.all in our queryset with Post.objects.order_by(‘-pub_date’) which sorts all the posts by descending published date. You can also add more parameters to sort by other fields if one field is equal. But we’ll stick with order_by.
Now we can create the html templates for our two public view pages. Django has a custom templating language which can be broken down to two things:
Template Tags/Function calls {% if variable_exists_or_not %} {% endif %}, etc.
Although if statements may not seem like a function call, it is. Functions are broken down to a keyword that represents a function call, and all subsequent values are passed to that function. But I’m digressing…
This isn’t a tutorial about good html design, so we’re going to keep is stupid-short-and-simple. Create a folder in your projects directory named templates (or whatever you named it in settings.py).
The central idea about Django’s template system design is the used of extends. A parent template defines a series of blocks which can be overridden by child templates. This becomes clear as we work with templates. So lets create a template that will be a basis for all other templates which helps unify the look of the site and name it base.html. Here’s the base html template:
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML Transitional//EN”
“http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml” xml:lang=”en” lang=”en”>
<head>
<meta http-equiv=”Content-type” content=”text/html; charset=utf-8″ />
<title>My Blog</title>
<link rel=”stylesheet” href=”{{ MEDIA_URL}}css/style.css” type=”text/css” media=”screen,projection” />
</head>
<body>
<h1><a href=”/”>My Blog</a></h1>
{% block content %}{% endblock %}
</body>
</html>
In the stylesheet, we output the MEDIA_URL which is defined by settings.py and provided by the templating system to figure out the path to the stylesheet (its a bit out of the scope why we have access to MEDIA_URL, see RequestContext). We haven’t mapped the resources to the url on our development server, so it won’t work for now, but we’ll get to it.
Next we define a block for this template named content. Technically, we can input some default html if the block isn’t overridden, but since it’s the main content, it’s always going to be replaced.
To get MEDIA_URL to work correctly for we need to map urls for serving our media files. We can do this in urls.py. (Append to the bottom of your file)
from django.conf import settings
if settings.DEBUG:
urlpatterns += patterns(”,
(r’^rsrc/(?
’document_root’: settings.MEDIA_ROOT})
)
Warning about django.views.static.serveIt’s worth to mention that this view is not for production purposes. This is clearly stated in Djangos’ documentation, which is the reason why I’ve placed it in a settings.DEBUG if block. |
This imports the settings configuration and if it’s in DEBUG mode, adds the static serve content to the url patterns. You should change rsrc to whatever you specified as your MEDIA_URL excluding the prefixed slash. Everything else should be mostly explanatory (path named capture grabs everything else in the incoming url).
Now we’re going to implement templates that generic views use. In the generic views defined by urls.py look for template in certain locations by default (you can customize this behavior with the template_name parameter). But by default, it looks for the template in <app_name>/<model_name>_list.html for object_list and <app_name>/<model_name>_detail.html for object_detail. So for our particular application, we need templates for posts/post_list.html and posts/post_detail.html. Since I planned the actual html for a particular post to be the same, we’ll be using the include tag to include another extra html file located at posts/_post.html.
First, post_list.html:
{% extends ‘base.html’ %}
{% block content %}
{% for object in object_list %}
<hr/>
{% include ‘posts/_post.html’ %}
{% endfor %}
{% endblock %}
Pretty simple here. We tell Django that we’re extending base.html which inherits all the contents from that file. We’ll override the content block and loop through all the posts adding a horizontal rule before including the _post.html file which will display the data of the post. If you haven’t noticed, all template paths are relative to the base template directory (so we need to specify ‘posts’).
You may wonder where in the world we got the object_list variable. object_list is provided by the object_list generic view as stated by Django’s documentation, which contains a list of our posts. So in python fashion, we iterate over all the posts, and generate the html block for each object. _post.html will inherit all variables accessible in the template it was included in as if its contents were inserted into this template. We keep the post name as object as oppose to something like post since object_detail generic view uses the object variable to represent the single post that was fetched. Using the same variable allows use to reuse the include.
Our posts/post_detail.html is even simpler:
{% extends ‘base.html’ %}
{% block content %}
{% include ‘posts/_post.html’ %}
{% endblock %}
Nothing new to explain. But we have to also implement our _post.html template so lets create that now:
<div id=”object_{{ object.id }}”>
<h2>
<a href=”{% url post object.id %}”>{{ object.title }}</a>
<small>{{ object.pub_date|timesince }} ago</small>
</h2>
{{ object.body }}
</div>
The object variable represents a post model that we’re interacting with. So object.id, object.title, object.body are all post model fields we specified, although they could be fields, a function call (with no parameters) can be referenced the same way.
Here we’ve used the url template tag. This returns a url based on the url pattern name (see urls.py in posts app) and a set of parameter values separated by commas. The parameter values can specify the keyword name such as object_id=object.id, but that just makes it more verbose especially for one parameter.
Likewise we can add a link to return the user to all the posts. In post_detail.html add:
<a href=”{% url posts %}”>Back to posts</a>
Since the posts url pattern doesn’t require any parameters, we can leave that portion blank.
In this template, we also utilized a template filter, timesince, to transform the datetime field into a relative time from today. Template filters are only used in variable outputs (which are inside {{ }}) to modify the output in some fashion. Filters are functions that accept the variable from the left of itself and are stackable. So we can also do: {{ object.pub_date|timesince|upper }} to make the outputted timesince text display all in uppercase.
Part IV will most likely be published on Monday so subscribe to our RSS Feed
[...] Part 3 [...]
Hi
Great tutorial!!
A sugestion…
-can you add a little tutorial about forms in django?
-another tutorial, integration javascript(jquery) with django.
thanks for your work!
Hi, could you have a tutorial explaining how to pass a variable from views.py to a html file?
I am trying to make a ads rotation system. I am a php programmer and new to django and kinda new to python.
I would like to learn how I can get the ad graphic url from the database of google apps engine to the html file where the image tag is.
So their graphic ad shows. I am having trouble with this.
September 28th, 2009 at 2:13 am
works great until:
(r'^resources/(? .*)$', 'django.views.static.serve', {'document_root': settings.MEDIA_ROOT})
...
>>> re.compile('^resources/(?.*)$')
error: unexpected end of pattern
i placed the ‘?’ after: as in (.*?)
November 25th, 2009 at 12:10 pm
I’m getting the same problem as darius.
The correct expression is: resources/(?P<path>.*)$
It looks as though the browser is hiding some of it as it thinks it’s html mark up.
I found the solution here:
http://docs.djangoproject.com/en/dev/howto/static-files/
Note: Excuse the garbled message if that didn’t work but I was trying to put path within triangular brackets.
I would like more information regarding from django.conf import settings
if settings.DEBUG:
urlpatterns += patterns(”,
(r’^rsrc/(? .*)$’, ‘django.views.static.serve’, {
’document_root’: settings.MEDIA_ROOT})
)
You cannot just append this to the bottom of your file. could someone give the corrected one? It throws more than one error regarding these lines.
Twitter
Follow me on Twitter to keep up to date!
RSS Feed
Keep up with all of our updates by subscribing to our RSS feed!
FaceBook
Join our group on Facebook and become a fan of us!