A Detailed Django Tutorial: Blog Basics Part III

 

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.).

Table of Contents



Public Facing

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.

Why have urls.py per application?

This allows the project developer to determine the level of delegation to the application. The project could use the urls.py provided by the application or implement it’s own urls.py for custom mapping. Furthermore, the project make have it’s own project-specific applications which provide alternative views for the application, but utilize the same models if the project requires more hand-tweaking. Although, application views should be flexible enough so that a project rewriting its view should be a rarity. The urls.py just provides the simplified implementation for the project and future projects with “default” url mappings.

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:

  1. A view that fetches all the recent posts to display.
  2. A view that displays a particular post and its comments

We’ll implement the comments later using Django’s built-in commenting system. But first we’ll need to display the blog posts.

Generic Views

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:
myqs = Post.objects.all()

doesn’t actually hit the database at all. Only something that requires the actual data from the results such as:

  • myqs.count() # uses a COUNT query
  • myqs.get(id=1) # uses WHERE id=1, but expects to only fetch 1 record
  • print myqs # executes the current query

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.

Url Function

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.

Order By

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.

Templates

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:

  • Variable output {{ variable }} with optional filters {{ variable|filter_name }}
  • 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).

Base.html

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/(? .*)$’, ‘django.views.static.serve’, {

’document_root’: settings.MEDIA_ROOT})
)

Warning about django.views.static.serve

It’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).

Listing page

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.

Detail page

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

Share and Enjoy:
  • Print
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • Add to favorites
  • Design Float
  • DZone
  • email
  • FriendFeed
  • PDF
  • Propeller
  • Reddit
  • RSS
  • StumbleUpon
  • Twitter

Related posts:

  1. A Detailed Django Tutorial: Blog Basics Part IV
  2. A Detailed Django Tutorial: Blog Basics Part II
  3. Django Tutorial: Blog Basics
  4. Django Python Framework
  5. Ruby on Rails To Do List Tutorial


Written by Brenley Dueck

 

6 Responses to “A Detailed Django Tutorial: Blog Basics Part III”

  1. Django Blog Tutorial | Blog of Jeff Says:

    April 3rd, 2009 at 2:50 am

    [...] Part 3 [...]

  2. qwertyt Says:

    April 6th, 2009 at 5:29 am

    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!

  3. compute32 Says:

    May 1st, 2009 at 7:57 pm

    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.

  4. darius Says:

    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 (.*?)

  5. Nick Says:

    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.

  6. Nolan Says:

    April 5th, 2010 at 7:13 pm

    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.

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

 
connect with me!