Django for Designers/Basic views
Part 2: URLs, basic views, templates, static files
Note for tutorial attendees
At the end of each section of the tutorial, you will switch to a new branch based on the work from the previous part. This will make it so that everyone starts out each section with the same working code, so even if you get stuck or confused in one session, you can still keep up. If you don't have a lot of git experience, but you want to return to your work from a previous part, a TA can show you how to return to the code from a previous part and ask questions about it.
Remember that *you* shouldn't be putting your modifications inside the master branch, though! Instead, make a new branch for section 2 based off the master code you just downloaded, and let's get going!
# in django-for-designers/myproject
$ git branch my-branch-2 origin/pre-part-2
$ git checkout my-branch-2
M-V-C separation concept
When you write a Django app, your code is organized in a fashion reminiscent of a well-known design pattern known as MVC, short for model, view, controller. Django's precise organization methodology is based on models, views, and templates.
A view in Django handles a request from the website visitor for a web page. What you do with this request is up to you, and is the domain of the view code that you write. For example, when a request comes in, you could start a background process to send a fax to Tokyo indicating how happy you are that someone came to your website. Or you could provide a HTTP response to the website visitor with a list of recently bookmarked web pages if you are providing a bookmarking app.
The templates in Django control how data is presented. Typically, a view provides a collection of data to the template. The template then might loop over that information, wrap it in HTML bulleted lists, wrap that in a standard layout for all pages across your site, and serve that out to the site visitor. The templates usually refer to some static content, such as CSS, that makes the site actually seem designed!
Keeping this separation in mind is essential to feeling at home when using Django. (And since most web programming frameworks work via a similar separation, this mindset is good to keep in mind generally.)Writing our URLs
The first step to writing your application 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.
When a user requests a Django-powered page, the system looks at the ROOT_URLCONF setting, which contains a string in Python dotted syntax. Django loads that module and looks for a module-level variable called urlpatterns, which is a sequence of Python tuples in the following format: (regular expression, Python callback function [, optional dictionary])
Django starts at the first regular expression and makes its way down the list, comparing the requested URL against each regular expression until it finds one that matches.
(If you’d like to learn more about regular expressions, read the Dive into Python guide to regular expressions sometime. Or you can look at this xkcd. Regexpal is also a helpful tool for writing a regex.)
When we ran
django-admin.py startproject myproject
to create our project way back when, Django created a default URLconf file called 'urls.py' inside myproject/.
To write our app's URLs, edit the file myproject/urls.py so it looks like this:
urlpatterns = patterns('',
url(r'^$', 'bookmarks.views.index', name='home'),
url(r'^bookmarks/$', 'bookmarks.views.index', name='bookmarks_view'),
url(r'^tags/([\w-]+)/$', 'bookmarks.views.tag'),
)
Suppose a visitor to your site goes to (if you view the page, don't worry about the ViewDoesNotExist message, we'll get to that in a minute) http://localhost:8000/tags/awesome/.
- which regex pattern is tripped?
- what function is then called?
- what arguments is that function called with?
Save and commit your work.
Handling those URLs with some basic views
Start the development server:
# in django-for-designers/myproject
$ python manage.py runserver
Fetch “http://localhost:8000/bookmarks/” in your browser. You should get a pleasantly-colored error page with the following message:
ViewDoesNotExist at /bookmarks/
What's going on? Recall this line:
url(r'^$', 'bookmarks.views.index', name='home')
If we look in the bookmarks folder, we can see there's a views.py file... but if we look in that file, it's empty!
Let's write some views. Open bookmarks/views.py and put the following Python code in it:
from django.http import HttpResponse
def index(request):
return HttpResponse("Hello, world. You're at the bookmarks index.")
This is a very simple view.
Save the views.py file, then go to http://localhost:8000/bookmarks/ in your browser, and you should see that text.
Add a tag view by adding to the views.py file. This view is slightly different, because it takes an argument (which, remember, is passed in from whatever was captured by the regular expression in the URLconf):
def tag(request, tag_name):
return HttpResponse("This is a tag: %s" % (tag_name,))
Save views.py.
Navigate to http://localhost:8000/tags/awesome/ . It’ll run the tag() function and display whatever tag name you provide in the URL.
Add a little html to the ‘question’ view. For example, wrap the question_id in strong tags and verify that the view is indeed bold!
return HttpResponse("This is a tag: <strong>%s</strong>" % (tag_name,))
Add and commit your code. Remember to write a good commit message that explains what changed. (For examples of how not to write git commits, see http://www.commitlogsfromlastnight.com/.)
Handling URLs with templates
These placeholder responses are okay, but you wouldn't want to make a real webpage in a giant Python string in the middle of a view function! Fortunately, Django comes with a built-in template renderer! Let's edit views.py. First, at the top, edit the imports to add one fresh import:
from django.shortcuts import render
Then, modify the index function so it looks like this:
def index(request):
context = {}
return render(request, 'index.html', context)
render is a shortcut function that takes a request, a relative path to a template file, and an optional dictionary of contextual data and, instead of returning "Hello world" like before, returns a rendered web page.
Let's spin up our development web server and see our new view in action!
# in django-for-designers/myproject
$ python manage.py runserver
TemplateDoesNotExist at /
Oops. We get an error because we haven't actually made our index.html template file yet. Let's do that.
First, we'll make a templates folder inside our bookmarks app:
# in django-for-designers/myproject
$ mkdir bookmarks/templates
You can put your templates anywhere and tell Django where to look for them in settings.py. By default, though, Django will look inside each of your apps for a templates folder.
Now let's make a file called index.html inside our templates folder:
<!doctype html>
<html>
<head>
<title>My bookmarking app</title>
</head>
<body>
<div id="header">
<h1>My bookmarking app</h1>
</div>
<div id="content">
<h2>All bookmarks</h2>
<ul>
<li>Bookmark</li>
<li>Another bookmark</li>
<li>A third bookmark</li>
</ul>
</div>
<div id="footer">
Brought to you by PyCon US 2013
</div>
</body>
</html>
Now if you go to http://localhost:8000, you should see the webpage we made in our template file!
If you do not, you may need to restart your development server before it "sees" your template.
Save and commit your new template and template-based view.
Django templates 101
Let's rewrite the tags view to use a template as well.
def tag(request, tag_name):
context = {
'tag': tag_name,
}
return render(request, 'tag.html', context)
Our tag view gets fed in a variable we can use -- the name of the tag, so we add that to the context. That makes it available to the template to render.
Let's make our tag template. To do that, create a new file called tag.html in the same directory as index.html that you just created.
<!doctype html>
<html>
<head>
<title>My bookmarking app</title>
</head>
<body>
<div id="header">
<h1>My bookmarking app</h1>
</div>
<div id="content">
<h2>Bookmarks tagged {{ tag }}</h2>
<ul>
<li>Bookmark</li>
<li>Another bookmark</li>
<li>A third bookmark</li>
</ul>
</div>
<div id="footer">
Brought to you by PyCon US 2013
</div>
</body>
</html>
In Django's templating language, to put a variable into the template, put it inside double {{ brackets }}. If you look at http://localhost:8000/tags/awesome/, you'll now see "awesome" (or whatever tag name you choose) in the space occupied by {{ tag }}.
Let's make a base.html template. This template isn't rendered by any view, but all (or most) of our templates can inherit from it:
<!doctype html>
<html>
<head>
<title>My bookmarking app</title>
</head>
<body>
<div id="header">
<h1>My bookmarking app</h1>
</div>
<div id="content">
<h2>{% block subheader %}{% endblock %}</h2>
{% block content %}
Sample content -- you should never see this, unless an inheriting template fails to have any content block!
{% endblock %}
</div>
<div id="footer">
Brought to you by PyCon US 2013
</div>
</body>
</html>
This file contains the generic html layout we want for our site. It also includes two {% block %} tags, which are places where templates that inherit from this template can insert their own special HTML. Here we have a {% block %} for the subheader, and a block for the page content.
Let's make index.html inherit from this template:
{% extends 'base.html' %}
{% block subheader %}All bookmarks{% endblock %}
{% block content %}
<ul>
<li>Bookmark</li>
<li>Another bookmark</li>
<li>A third bookmark</li>
</ul>
{% endblock %}
And tag.html:
{% extends 'base.html' %}
{% block subheader %}Bookmarks tagged {{ tag }}{% endblock %}
{% block content %}
<ul>
<li>Bookmark</li>
<li>Another bookmark</li>
<li>A third bookmark</li>
</ul>
{% endblock %}
The {% extends %} tag tells the template renderer which template this template file inherits from. Each block tag then specifies the content that ought to go in each block.
Reload your webserver (if necessary) and take a look at http://localhost:8000 and http://localhost:8000/tags/awesome/ . They're both based on templates now -- and based on the same base template, so any changes we make to base.html will appear in both automatically!
As a rule, in Django, things in {% %} control the "logic" of the template, while things in {{ }} actually stick data into the template in particular places.
Static files
Now we know how to create HTML within Django's templating language. For a good web application, though, you will probably want to embed stylesheets, scripts, images, and other files.
Move outside your myproject/ directory and move the contents of the sample-static-files/ folder that came with your git repo into myproject/bookmarks/.
# in django-for-designers/myproject
$ cd ..
# in django-for-designers
$ mkdir myproject/bookmarks/static
$ git mv sample-static-files/* myproject/bookmarks/static/
Django automatically looks for a folder named 'static' inside any of your Django apps. You can also put static files in other places, such as inside myproject/ directly. To do that, you would need to add the absolute path of the folder to STATICFILES_DIRS in settings.py.
For consistency, now is a good time to return to the directory we used to be in:
# in django-for-designers
$ cd myproject
# in django-for-designers/myproject
$ ls
Make sure you see manage.py, and then let's continue.
Now let's add some more structure to our HTML templates to make them easier to style. First, base.html:
<!doctype html>
<html>
<head>
<title>My bookmarking app</title>
<link href='http://fonts.googleapis.com/css?family=Source+Sans+Pro:200,400,700,900' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="/static/css/style.css" type="text/css" media="screen" charset="utf-8">
</head>
<body>
<div id="container">
<div id="header">
{% block bookmark_widget %}
{% endblock %}
<h1><a href="/">My bookmarking app</a></h1>
</div>
<div id="content">
<h2>{% block subheader %}{% endblock %}</h2>
{% block content %}
Sample content -- you should never see this, unless an inheriting template fails to have any content block!
{% endblock %}
</div>
<div id="footer">
Brought to you by PyCon US 2013
</div>
</div>
</body>
</html>
And also index.html:
{% block content %}
<ul class="bookmarks">
<li>
<a class="bookmark-link" href="">Bookmark</a>
<div class="metadata"><span class="author">Posted by Jane Smith</span> | <span class="timestamp">2012-2-29</span> | <span class="tags"><a href="">funny</a> <a href="">haha</a></span></div>
</li>
<li>
<a class="bookmark-link" href="">Another bookmark</a>
<div class="metadata"><span class="author">Posted by Jeanne Doe</span> | <span class="timestamp">2012-2-28</span></div>
</li>
<li>
<a class="bookmark-link" href="">A third bookmark</a>
<div class="metadata"><span class="author">Posted by Jayne Cobb</span> | <span class="timestamp">2012-2-28</span> | <span class="tags"><a href="">funny</a></span></div>
</li>
<li>
<a class="bookmark-link" href="">La la la bookmark</a>
<div class="metadata"><span class="author">Posted by Jeanne Doe</span> | <span class="timestamp">2012-2-28</span> | <span class="tags"><a href="">blah</a> <a href="">school</a></span></div>
</li>
<li>
<a class="bookmark-link" href="">Rhubarb rhubarb bookmark</a>
<div class="metadata"><span class="author">Posted by Jeanne Doe</span> | <span class="timestamp">2012-2-27</span></div>
</li>
</ul>
{% endblock %}
{% block bookmark_widget %}
<div id="new-bookmark-widget">
<form method="post">
<h3>Bookmark</h3>
<p><label>URL</label> <input></p>
<p><label>Title</label> <input></p>
<p><label>Tags (comma-separated)</label> <input></p>
<p><button id="new-bookmark-submit">Submit</button>Submit</button>
</form>
</div>
{% endblock %}
and tag.html:
{% block content %}
<ul class="bookmarks">
<li>
<a class="bookmark-link" href="">Bookmark</a>
<div class="metadata"><span class="author">Posted by Jane Smith</span> | <span class="timestamp">2012-2-29</span> | <span class="tags"><a href="">funny</a> <a href="">haha</a></span></div>
</li>
<li>
<a class="bookmark-link" href="">Another bookmark</a>
<div class="metadata"><span class="author">Posted by Jeanne Doe</span> | <span class="timestamp">2012-2-28</span></div>
</li>
<li>
<a class="bookmark-link" href="">A third bookmark</a>
<div class="metadata"><span class="author">Posted by Jayne Cobb</span> | <span class="timestamp">2012-2-28</span> | <span class="tags"><a href="">funny</a></span></div>
</li>
</ul>
{% endblock %}
Now if we reload our site at http://localhost:8000, we can see that the prewritten static files have styled it some, and it looks significantly nicer than it did before!
Save and commit your work!