Django for Designers/Basic views

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!

M-V-C separation concept
TODO: draw a picture

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.

For Django, a model represents how the data is stored. This is the part of your app that ties your data to storage in a database. You write Python code, and Django converts it and out of into SQL, the language spoken by databases.

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

You might ask, “What’s a regular expression?” Regular expressions, or "regexes", are patterns for matching text. In this case, we’re matching the URLs people go to, and using regular expressions to match whole ‘groups’ of them at once. (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.)

In addition to matching text, regular expressions can capture text. Capturing means to remember that part of the string, for later use. Regexes use parentheses to wrap the parts they’re capturing.

For Django, when a regular expression matches the URL that a web surfer requests, Django extracts the captured values (if any) and passes them to a function of your choosing. This is the role of the callback function above. When a regular expression matches the url, Django calls the associated callback function with any captured parts as parameters. These callback functions typically live in one of your apps' views.py, so this will be much clearer after the next section.

When we ran

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:

Suppose a visitor to your site goes to http://localhost:8000/tags/awesome/.
 * which regex pattern is tripped?
 * what function is then called?
 * what arguments is that function called with?

Save urls.py. Start the dev server  and try that url out! What happens?

Save and commit your work.

FIXME: Right now, this is an error. Should it not be an error?

The idea that a URL doesn’t have to map onto a file or a folder, or some other sort of static resource, is quite powerful. The URL is just a way of giving instructions to some server, somewhere. Note that in this example, both / and /bookmarks/ go to the same place -- they both activate bookmarks.views.index!

(Rant: In Django, as in most modern frameworks, you have total control over the way your URLs look. People on the web won’t see cruft like .py or .php or even .html at the end of your URLs. There is no excuse for that kind of stuff in the modern era! (Though, putting .php on the end of all your Django URLs, while pointless, is kind of hilarious and super easy.))

Handling those URLs with some basic views
Start the development server:

Fetch “http://localhost:8000/bookmarks/” in your browser. You should get a pleasantly-colored error page with the following message:

What's going on? Recall this line:

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! So, our problem is that the URL parsing is going fine, but there is no one listening at the other end of the phone! This ViewDoesNotExist error happened because you haven’t written a function index in the module bookmarks/views.py.

Well, I guess we should do that!

Let's write some views. Open bookmarks/views.py and put the following Python code in it:

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

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!

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 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:

Then, modify the index function so it looks like this:

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!

http://localhost:8000/

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:

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:

Now if you go to http://localhost:8000, you should see the webpage we made in our template file!

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.

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.

In Django's templating language, to put a variable into the template, put it inside double {&#8288;{ 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 {&#8288;{ tag }}.

You'll notice--these two templates share an awful lot of code. Repeating yourself in this way is no fun, and a recipe for messy, inconsistent styling. Fortunately, Django templates support inheritance!

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:

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:

And tag.html:

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.

There are lots of built-in template tags and filters; we'll see a few more later. The full docs on them live here.

As a rule, in Django, things in {% %} control the "logic" of the template, while things in {&#8288;{ }} 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. How can we include static files such as these?

Move outside your myproject/ directory and move the contents of the sample-static-files/ folder that came with your git repo into myproject/bookmarks/.

TODO Actually provide sample static files here, and play-test the rest of this section.

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 this tutorial we'll just leave our static files inside bookmarks/, for the sake of simplicity.

For consistency, now is a good time to return to the directory we used to be in:

Make sure you see manage.py, and then let's continue.

TODO: see how the old HTML looks here

Now let's add some more structure to our HTML templates to make them easier to style. First, base.html:

And also index.html:

and tag.html:

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!