Django for Designers: Difference between revisions

imported>Paulproteus
imported>Paulproteus
(→‎Part 2: URLs, basic views, templates, static files: Removed, by moving it to a new page)
Line 32:
* [[/Starting_our_project|Part 1: Starting our project]]
* [[/Basic views|Part 2: URLs, basic views, templates, static files]]
 
=== Part 2: URLs, basic views, templates, static files ===
 
<!-- Instructor git note: You should do the above, and then do: git push origin HEAD:pre-part-2 -->
 
==== 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!
 
<source lang="bash">
# in django-for-designers/myproject
$ git branch my-branch-2 origin/pre-part-2
$ git checkout my-branch-2
</source>
 
====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 [http://diveintopython.net/regular_expressions/index.html Dive into Python guide to regular expressions] sometime. Or you can look at this [http://xkcd.com/208/ xkcd]. [http://regexpal.com/ 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
 
<source lang="bash">django-admin.py startproject myproject</source>
 
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:
 
<source lang="python">
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'),
)</source>
 
Suppose a visitor to your site goes to [http://localhost:8000/tags/awesome/ 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 <source lang="bash">
# in django-for-designers/myproject
$ python manage.py runserver</source> 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:
 
<source lang="bash">
# in django-for-designers/myproject
$ python manage.py runserver</source>
 
Fetch “[http://localhost:8000/bookmarks/ http://localhost:8000/bookmarks/]” in your browser. You should get a pleasantly-colored error page with the following message:
 
<source lang="bash">ViewDoesNotExist at /bookmarks/</source>
 
What's going on? Recall this line:
 
<source lang="python">url(r'^$', 'bookmarks.views.index', name='home')</source>
 
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:
 
<source lang="python">
from django.http import HttpResponse
def index(request):
return HttpResponse("Hello, world. You're at the bookmarks index.")
</source>
 
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):
 
<source lang="python">
def tag(request, tag_name):
return HttpResponse("This is a tag: %s" % (tag_name,))
</source>
 
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!
 
<source lang="python">return HttpResponse("This is a tag: <strong>%s</strong>" % (tag_name,))</source>
 
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/ 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:
 
<source lang="python">
from django.shortcuts import render
</source>
 
Then, modify the ''index'' function so it looks like this:
 
<source lang="python">
def index(request):
context = {}
return render(request, 'index.html', context)
</source>
 
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!
 
<source lang="bash">
# in django-for-designers/myproject
$ python manage.py runserver</source>
 
http://localhost:8000/
 
<source lang="bash">TemplateDoesNotExist at /</source>
 
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:
 
<source lang="bash">
# in django-for-designers/myproject
$ mkdir bookmarks/templates</source>
 
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:
 
<source lang="html4strict">
<!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>
</source>
 
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.
 
<source lang="python">
def tag(request, tag_name):
context = {
'tag': tag_name,
}
return render(request, 'tag.html', context)
</source>
 
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.
 
<source lang="html4strict">
<!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>
</source>
 
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:
 
<source lang="html4strict">
<!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>
</source>
 
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:
 
<source lang="html4strict">
{% 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 %}
</source>
 
And tag.html:
 
<source lang="html4strict">
{% 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 %}
</source>
 
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 [https://docs.djangoproject.com/en/dev/ref/templates/builtins/ 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/.
<source lang="bash">
# in django-for-designers/myproject
$ cd ..
# in django-for-designers
$ mkdir myproject/bookmarks/static
$ git mv sample-static-files/* myproject/bookmarks/static/
</source>
 
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:
 
<source lang="bash">
# in django-for-designers
$ cd myproject
# in django-for-designers/myproject
$ ls
</source>
 
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:
 
<source lang="html4strict">
<!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>
</source>
 
And also index.html:
 
<source lang="html4strict">
{% 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>New 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 %}
</source>
 
and tag.html:
 
<source lang="html4strict">
{% 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 %}
</source>
 
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!
 
<!-- Teacher git note: git push origin HEAD:pre-part-3 -->
 
=== Part 3: Models, our database, and making it visible ===
Anonymous user