Django for Designers: Difference between revisions

From OpenHatch wiki
Content added Content deleted
No edit summary
 
(45 intermediate revisions by 4 users not shown)
Line 4: Line 4:


This is a tutorial on web programming, so we will go beyond just Django and discuss third-party Django apps and other real-world web development tools. We'll also be emphasizing areas of Django that particularly affect designers, such as static files, template inheritance, and AJAX.
This is a tutorial on web programming, so we will go beyond just Django and discuss third-party Django apps and other real-world web development tools. We'll also be emphasizing areas of Django that particularly affect designers, such as static files, template inheritance, and AJAX.

<div style="font-size: 200%; text-align: center;">[http://www.youtube.com/watch?v=Z8FC8_2h5iM Watch the tutorial video!]</div>


===Things you should know already===
===Things you should know already===
Line 29: Line 31:


<!-- Teacher's note: Just before the tutorial, or maybe the night before, or maybe whenever you want, do a git push of the final git repo. It will have some branches. Make sure to: git push origin --all -->
<!-- Teacher's note: Just before the tutorial, or maybe the night before, or maybe whenever you want, do a git push of the final git repo. It will have some branches. Make sure to: git push origin --all -->
* [[/Laptop_setup|Part 0: Laptop setup (do this already!)]]
* [[/Starting_our_project|Part 1: Starting our project]]
* [[/Basic views|Part 2: URLs, basic views, templates, static files]]
* [[/Adding models|Part 3: Models, our database, and making it visible]]
* [[/CRUD|Part 4: CRUD]]
* [[/Styling|Part 5: Style time]]
* [[/Sharing|Part 6: Sharing with others]]
* [[/Whats_next|Part 7: Exercises for the reader]]


[https://dl.dropbox.com/u/84751227/cheatsheet.pdf A possibly-helpful cheatsheet for common command line operations (PDF)]
=== Part 1: Starting our project ===

This tutorial goes through the process of building a simple social bookmarking application, where users can save various URLs and tag those bookmarks to make them easier to find later. Our bookmarks application will be built on top of Django, a full-featured web framework written in Python.

==== Activate your env ====

The first step is to open your terminal window, navigate to your django-for-designers folder, and activate your env:

<!-- Teacher git note: There should be a pre-part-1 branch on Github that is to your liking. It should equal master on github. -->

<source lang="bash">
# in django-for-designers
$ source ./bin/activate</source>

You'll know it worked because afterwards, you'll see <source lang="bash">(django-for-designers) $</source> at the front of all your terminal lines in that terminal window!

(What this did is activate a special Python environment that knows about Django and other dependencies for this tutorial. By keeping our dependencies inside a separate environment, we can keep them from interfering with any other project's dependencies--for instance, if you had one project using Django 1.4 and another using Django 1.5 on the same computer.)

====Create a branch in your git repository====

If you're not familiar with git, git is a version control system that tracks changes you make to files within a git repository (the folder you just cloned). You make commits in git, which are like save points in video games -- the commit messages tell you what changes are included in this commit. If later on you make some changes and delete something or mess something up, you can always go back to a previous commit and try again.

Git also has "branches"--parallel timelines where you can go off from the main branch (called "master") and try something out. It's often a good idea to start a new branch when you're writing a new feature. You can make as many commits as you want inside the branch, and it won't affect the main master timeline. You can make as many branches as you want, and branches of branches. You can even share branches with others (though we won't be doing that today). Git's branching system (and its non-centralized architecture generally) make it easy to try out new ideas in code without having to ask permission of everyone else who might be working on the same code as you. Then, when your new feature is done, you can then merge (or rebase--the exact process would take a much longer explanation) the changes back into the master timeline.

We'll be using the master branch (the default name for the main branch of code, which you're in right now) of the git repository you cloned during laptop setup for syncing up throughout the class in case you get lost. You'll need to make your own branches for playing with the code on your computer, so that it doesn't interfere w. re-syncing later.

We've created some branches for you to base your work on. You will use snapshots these as starting points throughout the tutorial. At the end of a section, you'll have your own completed work for that section available, but you'll start fresh from a pristine snapshot of what it would have looked like if you did it our way.

To make sure you have the latest collection of those, run this command:

<source lang="bash">
# in django-for-designers
$ git fetch
</source>

To make your first branch, enter:

<source lang="bash">
# in django-for-designers
$ git branch my-branch-1 origin/pre-part-1
$ git checkout my-branch-1
</source>

Congratulations! You're now in a branch named my-branch-1! Notice that branches on your own computer have no prefix; branches that belong to use have ''origin/'' before their names.

You can see what branch you are in (and what branches are in your repository) at any time by typing:

<source lang="bash">
# in django-for-designers
$ git branch</source>

====Start your project====

Let's create your first Django project, which we'll call "myproject".

<source lang="bash">$ django-admin.py startproject myproject</source>

You'll see we made a folder called "myproject", with some files in it. Let's check them out!

<source lang="bash">
# in django-for-designers
$ cd myproject/
# in django-for-designers/myproject
$ ls
manage.py
myproject
$ ls myproject/
__init__.py
settings.py
urls.py
wsgi.py
</source>

These files are:

* manage.py: A command-line utility that lets you interact with this Django project in various ways.
* myproject/: Django auto-creates an folder within your project with the same name as your project that has a number of useful files in it. (This is a recent change; before Django 1.4, Django just put everything in the project folder instead of siloing it.)
* myproject/__init__.py: An empty file that tells Python that this directory should be considered a Python module. Because of the __init__.py file, you can use import to import myproject.
* myproject/settings.py: Settings/configuration for this Django project.
* myproject/urls.py: The URL declarations for this Django project; a “table of contents” of your Django-powered site.

Django comes with some local server software included. The Django development server is not hardy enough for actual production use (please, please don't use it for sites other people are supposed to see!) but it makes it easy to see the changes you make to your web app as you build it. Let's make sure our project works by verifying that the dev server will start:

<source lang="bash">
# in django-for-designers/myproject
$ python manage.py runserver</source>

(Note: If you see a ''python: can't open file'' error, make sure you have used ''cd'' to change into the directory with manage.py.)

You should see some output in your terminal that looks like:

<source lang="bash">Validating models...
0 errors found.
Django version 1.2, using settings 'myproject.settings'
Development server is running at http://127.0.0.1:8000/
Quit the server with CONTROL-C.</source>

Visit [http://localhost:8000/ http://localhost:8000] in your web browser, and you’ll see a “Welcome to Django” page, in pleasant, light-blue pastel. It worked! :)

Observe the logging that happens in the terminal where your server is running:

<source lang="bash">[24/Mar/2011 11:50:18] "GET / HTTP/1.1" 200 2057</source>

which has the format:

<source lang="bash">DATE METHOD URL PROTOCOL RESPONSE_CODE CONTENTSIZE</source>

Navigate to http://127.0.0.1:8000/some/url/. What changes in the terminal log?

Exit the server by returning to the terminal window where the development server is running and pressing CONTROL-C on your keyboard.

====Version control====

Before we do anything else, let’s commit our work in git.

As you recall, git lets you create checkpoints over the course of the time a program is being developed. Commits are those checkpoints. Programmers often have to go back into the history of a program to change things--whether it's to diagnose a bug or redesign how a feature works. Programmers also have to have an easy way of being able to edit the same application at the same time, and to share their edits with each other. Thus, besides '''saving''' their work the normal way, programmers '''commit''' their code using version control software.

Each commit contains the '''diff'''--the "difference", all of the changes you made to your code base -- between that commit and the commit before it. Different branches in git share the commits made prior to the branching-off point, then each have their own commit history.

To make a commit, first type <source lang="bash">
# in django-for-designers/myproject
$ git status</source> into your terminal. This will let you know what changes git has noticed in your code and which of those changes, if any, are staged and ready to be committed.

<source lang="bash">
# in django-for-designers/myproject
$ git status
# On branch my-branch-1
#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# ./
nothing added to commit but untracked files present (use "git add" to track)</source>

"Untracked files" means that git has noticed some new files inside its repository folder, but you haven't told git explicitly that you want it to "listen" for, and track, changes in those files. './' means that the folder we're in right now is currently untracked.

* Add the folder: <source lang="bash">
# in django-for-designers/myproject
git add ./
</source>
What does git status say now?

* git commit to commit those files. Adding the -m flag lets you say what the 'message' for the commit is in the same line:

<source lang="bash">
# in django-for-designers/myproject
$ git commit -m "Initial commit of Django project for the PyCon 2013 Django tutorial"
</source>

* Look at your changes with <source lang="bash">git log</source> to see your history. Is your commit message there?

Huzzah! We committed our changes so far. Now let's make some more changes!

====Set up your settings and database====

Now that we have a the scaffolding for our project in place, we can get to work! First, it needs to be configured.

Open myproject/myproject/settings.py in your editor. settings.py is a Python script that only contains variable definitions. Django looks at the values of these variables when it runs your project.

In settings.py, let's find DATABASES. The DATABASES variable is a dictionary (note the ‘{}’ characters) with one key: default.

<source lang="python">
DATABASES = {
'default': {
'ENGINE': 'django.db.backends', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
'NAME': '', # Or path to database file if using sqlite3.
'USER': '', # Not used with sqlite3.
'PASSWORD': '', # Not used with sqlite3.
'HOST': '', # Set to empty string for 127.0.0.1. Not used with sqlite3.
'PORT': '', # Set to empty string for default. Not used with sqlite3.
}
}</source>

Notice that the value of default is itself another dictionary with information about the site’s default database.

Set your app to use a sqlite database, in the ENGINE attribute. Sqlite is great for development because is stores its data in one normal file on your system and therefore is really simple to move around with your app. It's not sturdy enough to use for a website in production, though.

<source lang="python">'ENGINE': 'django.db.backends.sqlite3',</source>

Set your app to use a file called 'database.db' to store information for this project.

<source lang="python">'NAME': 'database.db',</source>

Does database.db exist right now? (No, but that's okay. It'll get created automatically when it's needed.)

Run git status, then git add and commit your change:

<source lang="bash">
# in django-for-designers/myproject
$ git add myproject/ # this is a shortcut that will add all changes inside the named folder
$ git commit -m "set up the database for Django to use"
</source>

Even though git knows to "listen" to settings.py now, it still won't add it to the 'staging area' for your commit unless you tell it to explicitly with git add.

Notice the INSTALLED_APPS setting towards the bottom of the settings.py. That variable (a python tuple... note the ‘()’ symbols) holds the names of all Django applications that are activated in this Django instance. Apps can be used in multiple projects, and you can package and distribute them for use by others in their projects. Some apps (as you can see) are installed by default!

Add South to our list of installed apps. (We'll need it later.)

<source lang="python">INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
# Uncomment the next line to enable the admin:
# 'django.contrib.admin',
# Uncomment the next line to enable admin documentation:
# 'django.contrib.admindocs',
'south',
)</source>

Each of our applications makes use of at least one database table, so we need to create the tables in the database before we can use them. To do that, run the following command in your terminal window:

<source lang="bash">
# in django-for-designers/myproject
$ python manage.py syncdb</source>

The syncdb command looks at the INSTALLED_APPS setting and creates any necessary database tables according to the database settings in your settings.py file. You’ll see a message for each database table it creates.

When prompted, you’ll get a prompt asking you if you’d like to create a superuser account for the authentication system. Say yes! Use 'super' as your password for now.

Does database.db exist right now? (Yes! Calling syncdb made Django realize it needed a sqlite database file, so it made one for us.)

Save and commit your work!

====Projects vs. apps distinction====

We’ve talked a little about Django apps and projects. You might be wondering what that nonsense is all about. Here are the things to know:
An app is component of a website that does something. South is a Django app. So is our bookmarks app (or it will be, anyway). An app is:
* single purpose - login, passwords, polls, forum, etc.
* orthogonal to / independent of other apps - Bookmarks shouldn’t have to know the inside details of authentication, for example.

A project corresponds roughly to a ‘website’: it has a settings.py file, and it may have corresponding databases or other data stores that the apps interact with. As you can remember, Django automatically made a folder for us called myproject, which you can think of as the app that coordinates all the other apps and other parts of the project.

Most projects--those for more complex websites--should use multiple apps, one for each core piece of functionality. Ideally, you should reuse existing free, open source Django apps that other people have written for things like user accounts, image galleries, and other common use cases. [http://www.djangopackages.com Django Packages] is a good resource for finding such apps.

You can find prewritten Django apps for things like bookmarks and tagging, but we'll be writing them ourselves for the sake of learning. :)

====Start your first app====

In your terminal, write:

<source lang="bash">
# in django-for-designers/myproject
$ python manage.py startapp bookmarks</source>

If you run

<source lang="bash">
# in django-for-designers/myproject
$ ls</source>

you'll see that now there is a folder called bookmarks/ in your project!

Inside that folder, you'll see:

<source lang="bash">
# in django-for-designers/myproject
$ ls bookmarks/
__init__.py
models.py
tests.py
views.py
</source>

Finally, we need to edit myproject/settings.py so that Django knows about our app. Open that file up, and look for INSTALLED_APPS. Add the following to the end of the sequence, on a line of its own:

<source lang="python">
'bookmarks',
</source>

It will then look like this:

<source lang="python">
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
# Uncomment the next line to enable the admin:
# 'django.contrib.admin',
# Uncomment the next line to enable admin documentation:
# 'django.contrib.admindocs',
'south',
'bookmarks',
)
</source>

Let's commit our new app.

<source lang="bash">
# in django-for-designers/myproject
$ git add bookmarks/
$ git commit -m "Made a bookmarks app"</source>

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

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

<source lang="python">
from django.http import HttpResponse
from django.shortcuts import render

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:

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

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

Remember to make a new branch for section 3 based off the official branch!

<source lang="bash">
# in django-for-designers/myproject
$ git branch my-branch-3 origin/pre-part-3
$ git checkout my-branch-3
</source>

====Introduction to databases and the ORM, or: finally, something we couldn't've done with plain HTML/CSS/JS!====

Okay, being able to design your URLs however you want, without them having to correspond to your actual file structure, is pretty neat. But besides that, we haven't done anything yet that you couldn't do with just HTML, CSS, and JavaScript. Time to fix that.

One key thing that separates web ''apps'' from web ''pages'' is that apps typically store data somewhere. The code that runs the app chooses what data seems most important to show at the moment. When writing an app using Django, we configure the storage through Django ''models''.

Every Django app comes with a ''models.py'' in which you list each kind of data you want to store. These are configured through Python classes (to name the kind of data) with a sequence of attributes (which control the pieces of data that make up the model). If you're familiar with, or interested in, SQL, these correspond to ''tables'' and ''columns'' respectively.

The models that you define here are built on top of Django's ''object-relational mapper'', or ORM. Because you define your models as Python ''objects'', you can effectively write queries against a ''relational'' database by instead coding in Python with reference to your objects. Django then ''maps'' your Python code into relational database queries in SQL.

By configuring your data access in Python, Django makes it easy to take advantage of your data layout in other places. For example, you'll see later how the ORM makes it easy to automatically generate forms that ask for exactly the information you need. It is also generally convenient to write your entire app in one language, rather than being required to switch to SQL to do queries. The ORM also handles "escaping," which makes your SQL queries handle Unicode and other strange characters properly. These conveniences let you avoid [http://en.wikipedia.org/wiki/Mojibake mojibake]-style data corruption and [http://en.wikipedia.org/wiki/SQL_injection SQL injection] attacks effortlessly.

TODO (models.py --> ORM --> DB chart)

====Creating a basic model====

Let's make it so we can store some real live data in our application! In our simple bookmarking app, we’ll create two models: Bookmarks and Tags. What information do we need to store about these objects?

A bookmark has:
* A URL
* A title (optionally)
* A timestamp for when the bookmark was made

A tag has:
* The tag slug/name

A tag also needs to know which bookmarks it applies to.

How do we represent this information in a Django model?

Open up bookmarks/models.py in your editor.

<source lang="python">
from django.db import models


class Bookmark(models.Model):
url = models.URLField()
timestamp = models.DateTimeField(auto_now_add=True)
title = models.CharField(max_length=200, blank=True, default="")
</source>

What's going on here?
* We've made a Bookmark class that inherits from Django's model class.
* We gave it a url, which is a URLField (a field that expects a string that parses as a valid URL).
* We gave it a timestamp, which is a DateTimeField (a field that expects a Python datetime object). auto_now_add means that the field will automatically set itself to the time when the model is created.
* Finally, we gave it a title, which is a CharField (a field that expects a string of some length or less, in this case less than 200 characters). "blank=True" means that Django will permit this field to be blank. We also set a default of the empty string, so that if someone fails to specify a title we permit that.

Now let's add a tag model! In the same file, below the Bookmark model:

<source lang="python">
class Tag(models.Model):
bookmark = models.ManyToManyField(Bookmark)
slug = models.CharField(max_length=50, unique=True)
</source>

The tag slug is stored in another CharField, which we've seen before. But what's this ManyToManyField? Well, it's one type of field for denoting a relationship between two models.

In relational databases, there's two basic types of relationships. In a ForeignKey relationship, one model relates to one and only one other model. You could imagine this like a Car model and a Wheel model. Each Wheel instance belongs to one and only one Car, so it would have a ForeignKey field for its car. The car, of course, can have multiple wheels.

With a ManyToManyField, on the other hand, the relationship isn't exclusive for either of the models involved. For instance, you could imagine having a Pizza model and a Topping model. Each Pizza can have multiple Toppings, and each Topping can be on multiple Pizzas.

(There are other variants of these fields, but this is the basic concept. See https://docs.djangoproject.com/en/dev/ref/models/fields/#module-django.db.models.fields.related for more details for how these relationship fields work in Django.)

In this case, each Bookmark can have multiple Tags, and each Tag can apply to multiple Bookmarks. So we use a ManyToManyField to store that information.

==== Creating database tables for your app (carefully, with South) ====

When you first create your models, you might not always know exactly what fields your models will need in advance. Maybe someday your bookmarks app will have preview thumbnail images! Then you would want to add another field to the model to store that information. Maybe someday you'll decide that tracking all the timestamps is silly, and want to delete that field.

Unfortunately, Django (and most database-using software) can’t figure out how to handle model changes very well on its own. Fortunately, a Django app called South that we installed earlier can handle these database changes--called ‘migrations’--for us.

Now that we’ve made our first version of our models file, let’s set up our bookmarks app to work with South so that we can make migrations with it in the future!

On the command line, write:

<source lang="bash">
# in django-for-designers/myproject
$ python manage.py schemamigration bookmarks --initial
</source>

As you can see, that’s created a migrations directory for us, and automatically made a new migration file inside it.

<source lang="bash">
# in django-for-designers/myproject
$ ls bookmarks/migrations/
0001_initial.py __init__.py
</source>

All we need to do now is apply our new migration:

<source lang="bash">
# in django-for-designers/myproject
$ python manage.py migrate bookmarks
</source>

Great! Now our database file knows about bookmarks and its new models, and if we need to change our models, South is set up to handle those changes. We’ll come back to South later.

IMPORTANT: You can't migrate an app if it's already been synced in the database using

<source lang="bash">
python manage.py syncdb
</source>

. But you do need to run syncdb at least once before you use south (since south itself uses syncdb to give itself space in your database). That's why it's super important that when you run syncdb, south should be listed under INSTALLED_APPS, but none of your own apps should be, and after you add your app to INSTALLED_APPS, you must not run syncdb again until after you've already set up migrations with that app.

Add and commit all your work, including the migrations folder that South generated for you!

==== Add some bookmarks via the command line ====

Now, let’s hop into the interactive Python shell and play around with the free API ("Application programming interface" -- APIs aren't just data doodads that hip web startups provide for you, they're an important concept in software architecture.) that Django gives you. To invoke the Python shell, use this command:

<source lang="bash">
# in django-for-designers/myproject
$ python manage.py shell</source>

We’re using this instead of simply typing “python”, because manage.py's shell sets up the project’s environment for you. “Setting up the environment” involves two things:
* Making sure bookmarks (and any other apps you might have) are on the right path to be imported.
* Setting the DJANGO_SETTINGS_MODULE environment variable, which gives Django the path to your settings.py file.

Once you’re in the shell, let's explore the database API. Let's import the model classes we just wrote:
<source lang="python">
>>> from bookmarks.models import Bookmark, Tag
</source>

List all the current Bookmarks:

<source lang="python">
>>> Bookmark.objects.all()
[]
</source>

How many bookmarks is this? It's an empty list, so zero!

Let's add a bookmark:

<source lang="python">
>>> b = Bookmark(url="http://www.bringinthecats.com/", title="Funny link")
</source>

We could specify the value for timestamp as well here, but we don't have to since when we wrote its model definition, we specified that timestamp has a default value (of when the bookmark is created). We did specify a title for the bookmark, even though we didn't have to since it's an optional field.

Try getting the ID number of our new bookmark 'b' by typing:

<source lang="python">>>> b.id</source>

What happens?

Save your bookmark to the database. In Django, you have to call save() explicitly.

<source lang="python">>>> b.save()</source>

Get the id of the Bookmark instance. Because it’s been saved, it has an ID in the database now! Even though we didn't specify one in our models.py, every saved model instance automatically has an id field.

<source lang="python">>>> b.id
1</source>

Access the database columns (Fields, in Django parlance) as Python attributes:

<source lang="python">
>>> b.title
"Funny link"
>>> b.timestamp
datetime.datetime(2011, 12, 1, 3, 3, 55, 841929)
</source>

We can change the bookmark title by changing its title attribute, then calling save().

<source lang="python">
>>> b.title = "WHEEEE"
>>> b.save()
>>> b.title
"WHEEEE"
</source>

If we now ask Django to show a list of all the Bookmark objects available, we can see it's no longer an empty list!

<source lang="python">
>>> Bookmark.objects.all()
[<Bookmark: Bookmark object>]
</source>

==== Fix the hideous default model representation====
Wait a minute! <Bookmark: Bookmark object> is an utterly unhelpful representation of this object. Let’s fix that by editing the Bookmark model. Use your text editor to open the bookmarks/models.py file and adding a __unicode__() method to both Bookmark and Tag:

<source lang="python">
class Bookmark(models.Model):
url = models.URLField()
timestamp = models.DateTimeField(auto_now_add=True)
title = models.CharField(max_length=200, blank=True, default="")

def __unicode__(self):
return self.url


class Tag(models.Model):
bookmark = models.ManyToManyField(Bookmark)
slug = models.CharField(max_length=50, unique=True)

def __unicode__(self):
return self.slug
</source>

Let's see our new shiny __unicode__ methods in action. Start a new Python interactive shell by running:

<source lang="bash">
# in django-for-designers/myproject
$ python manage.py shell</source>

<source lang="python">
>>> from bookmark.models import Bookmark, Tag
>>> Bookmark.objects.all()
[<Bookmark: http://www.bringinthecats.com/>]
</source>

Save and commit your changes.

==== Adding more data via the shell ====

<source lang="python">
>>> b = Bookmark(url="http://www.google.com")
>>> b.save()
>>> b = Bookmark(url="https://us.pycon.org/2013/", title="PyCon US website")
>>> b.save()
>>> b = Bookmark(url="https://www.djangoproject.com")
>>> b.save()
</source>

We've created a bunch of bookmarks, but no tags! Let's change that.

<source lang="python">
>>> b = Bookmark.objects.all()[0]
>>> b
<Bookmark: http://www.bringinthecats.com/>
>>> b.tag_set.create(slug="cats")
<Tag: cats>
>>> b.tag_set.create(slug="music")
<Tag: music>
>>> b.tag_set.create(slug="funny")
<Tag: funny>
>>> b.save()
</source>

==== Slicing and dicing Django data ====

There are many methods for searching across your Django models.

We can filter our bookmarks, for instance for ones with a particular URL:

<source lang="python">
>>> foo = Bookmark.objects.filter(url="http://www.bringinthecats.com/")
>>> foo
[<Bookmark: http://www.bringinthecats.com/>]
</source>

Or for bookmarks which have titles:

<source lang="python">
>>> titled_bookmarks = Bookmark.objects.exclude(title="")
>>> titled_bookmarks
[<Bookmark: http://www.bringinthecats.com/>, <Bookmark: https://us.pycon.org/2013/>]
</source>

If you try to use filter to search for a question that does not exist, filter will give you the empty list.

<source lang="python">
>>> Bookmark.objects.filter(title="Who framed Roger Rabbit?")
[]
</source>

The get method, on the other hand, returns exactly one hit. If it finds zero matches, or more than one match, it will raise an exception.

<source lang="python">
>>> Bookmark.objects.get(id=1)
<Bookmark: http://www.bringinthecats.com/>
>>> Bookmark.objects.get(id=4)
Traceback (most recent call last):
...
DoesNotExist: Bookmark matching query does not exist.
</source>

More information on making queries with Django's ORM can be found in the Django docs at https://docs.djangoproject.com/en/dev/topics/db/queries/#retrieving-objects.

=== Part 3.5: Changing our mind and adding users ===

D'oh! You know what every social bookmarking app has, that ours doesn't have? Users!

I don't mean like the number of people using it--I mean a way to store different users' accounts and keep track of who owns which bookmarks. So let's change our app to include this feature.

Lucky for us, Django comes with an app for user accounts and authentication from the get-go! In fact, it's already installed. If you look back at your settings.py file, you'll see that in INSTALLED_APPS, there is an entry for 'django.contrib.auth'.

<source lang="python">
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
# Uncomment the next line to enable the admin:
# 'django.contrib.admin',
# Uncomment the next line to enable admin documentation:
# 'django.contrib.admindocs',
'south',
'bookmarks',
)
</source>

That's our authentication app!

Let's open the Django shell and play with this app a bit.

<source lang="bash">
# in django-for-designers/myproject
$ python manage.py shell</source>

<source lang="python">
>>> from django.contrib.auth.models import User
>>> User.objects.all()
[<User: karen>]
</source>

Whaaaaat?? There's already a User here. How can that be?

You might recall making a 'superuser' account when you first set up your Django project. That superuser was, in fact, created using Django's built-in auth app.

What is our user account's id number?

<source lang="python">
>>> me = User.objects.all()[0]
>>> me.id
1
</source>

Neato!

==== Add user field to bookmark ====

Now we need to create a relationship between the built-in User model and our Bookmark model.

First, in our models.py file, we need to import django.contrib.auth's built-in User model so that we can refer to it in our models.

<source lang="python">
from django.db import models
from django.contrib.auth.models import User
</source>

Now we need to think--what kind of relationship do users and bookmarks have?

Well, a user can have multiple bookmarks. But (right now, anyway) a bookmark should only have one user. So that means that we should use a ForeignKey field to add the user to our Bookmark model.

<source lang="python">
class Bookmark(models.Model):
author = models.ForeignKey(User)
title = models.CharField(max_length=200, blank=True, default="")
url = models.URLField()
timestamp = models.DateTimeField(auto_now_add=True)

def __unicode__(self):
return self.url
</source>

While we're at it, let's update the __unicode__ method too to let us know to whom a bookmark belongs to.

<source lang="python">
class Bookmark(models.Model):
author = models.ForeignKey(User)
title = models.CharField(max_length=200, blank=True, default="")
url = models.URLField()
timestamp = models.DateTimeField(auto_now_add=True)

def __unicode__(self):
return "%s by %s" % (self.url, self.author.username)
</source>

==== Make a migration in South ====

Now that we've added a field to our model, we are going to need to create a database migration. South can help us do this!

To create our migration, run

<source lang="bash">
# in django-for-designers/myproject
$ python manage.py schemamigration bookmarks --auto</source>

Note that we're now using --auto instead of --initial (which we used back when we first wrote our models and set up our app to use South).

Eep! Before it will make our migration file, South wants some information from us:

<source lang="bash">
? The field 'Bookmark.author' does not have a default specified, yet is NOT NULL.
? Since you are adding this field, you MUST specify a default
? value to use for existing rows. Would you like to:
? 1. Quit now, and add a default to the field in models.py
? 2. Specify a one-off value to use for existing columns now
? Please select a choice: 2
</source>

We could in theory modify our models to specify a default value for author, or make it optional. But neither of those sound like good options--we *want* future bookmarks to be forced to have an author! So we'll choose 2, to set up a default value for our new author field just for the purposes of this migration.

<source lang="bash">
? Please enter Python code for your one-off default value.
? The datetime module is available, so you can do e.g. datetime.date.today()
>>>
</source>

We then need to come up with a default value. Well, right now there's only one User in our system who the sample bookmarks we'd entered so far could belong to--our superuser account, which (if you don't remember) had an ID number of 1.

So let's enter 1 for our default.

<source lang="bash">
? Please enter Python code for your one-off default value.
? The datetime module is available, so you can do e.g. datetime.date.today()
>>> 1
+ Added field author on bookmarks.Bookmark
Created 0002_auto__add_field_bookmark_author.py. You can now apply this migration with: ./manage.py migrate bookmarks
</source>

Remember, the first step creates the migration, but doesn't run it. So let's do what South says and run a command to apply our migration!

<source lang="bash">
# in django-for-designers/myproject
$ python manage.py migrate bookmarks
Running migrations for bookmarks:
- Migrating forwards to 0002_auto__add_field_bookmark_author.
> bookmarks:0002_auto__add_field_bookmark_author
- Loading initial data for bookmarks.
Installed 0 object(s) from 0 fixture(s)
</source>

Save and commit your work to add users to your bookmarks app!

==== Templates and links for login/logout/etc ====

Django's auth app comes with built-in views, which we can use to handle login and logout functionality for our users. Once we point some URLs at them, that is.

First, let's edit urls.py:

<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'),
url(r'^login/$', 'django.contrib.auth.views.login'),
)
</source>

Now if anyone goes to localhost:8000/login/, the built-in login view will get triggered.

Run your dev server and try that. What error do you see?

While there is a built-in login view, there is no built-in login template. We need to build one for it. While we could put it anywhere and tell login explicitly where to look, by default the login view expects the template to reside at templates/registration/login.html. So we may as well put it there:

<source lang="bash">
# in django-for-designers/myproject
$ cd bookmarks/templates/
# in django-for-designers/myproject/bookmarks/templates
$ mkdir registration
$ touch registration/login.html
</source>

Open up your new login template and put this inside:

<source lang="html4strict">
{% extends "base.html" %}

{% block subheader %}Login{% endblock %}

{% block content %}
{% if form.errors %}
<p>Your username and password didn't match. Please try again.</p>
{% endif %}

<form method="post" action="{% url 'django.contrib.auth.views.login' %}">
{% csrf_token %}
<table>
<tr>
<td>{{ form.username.label_tag }}</td>
<td>{{ form.username }}</td>
</tr>
<tr>
<td>{{ form.password.label_tag }}</td>
<td>{{ form.password }}</td>
</tr>
</table>

<input type="submit" value="login" />
<input type="hidden" name="next" value="/" />
</form>
{% endblock %}
</source>

Note the hidden input with the name "next". This input tells the login view what URL to send the user to after they successfully log in. We have it set to '/', so it'll just take them back to the home page.

We need to add a logout view too! Let's do that. Back in urls.py:

<source lang="python">
url(r'^login/$', 'django.contrib.auth.views.login'),
url(r'^logout/$', 'django.contrib.auth.views.logout', {'next_page': '/'})
</source>

The dictionary after the logout URL sends some extra arguments to the logout view. Specifically it tells the logout view where to send the user after they log out. We could make a special goodbye splash page or something, but nah, let's just send them back to the home page again.

Now we need to add a login/logout link to our site so people can actually use these views! We want this link to be at the top of every page, so we'll edit our base template:

<source lang="html4strict">
<body>
<div id="container">
<div id="header">
{% block bookmark_widget %}
{% endblock %}
<div id="authentication">
{% if user.is_authenticated %}
Hi {{user}}! <a href="{% url 'django.contrib.auth.views.logout' %}">Logout</a>
{% else %}
<a href="{% url 'django.contrib.auth.views.login' %}">Login</a>
{% endif %}
</div>
<h1><a href="/">My bookmarking app</a></h1>
</div>
[ ... ]
</source>

Our template checks to see if there's a logged-in user, and if so, shows a hello message and a logout link. If the user isn't logged in, it shows a login link instead.

Check http://localhost:8000 and try logging in with the superadmin username and password you created before! It should work. :)

You might be wondering--where did the 'user' variable in the template come from? If you look at views.py, you'll notice we never added a user variable to our context dictionary. So how did this happen?

The answer is in the function we are using to render our templates, render(). render() automatically uses the request we sent it to create a Django RequestContext, which contains a bunch of extra context variables that get sent along to every view that uses a RequestContext. The Django auth app adds the current user to the RequestContext automatically. This is handy, since you don't want to have to look up the current user to every single view you ever write separately, just so the nav section on your website will work everywhere!

There are other functions that return an HTMLResponse, like render(), but don't include a RequestContext. render_to_response() is one common shortcut function that doesn't include it by default; another was the HttpResponse() function we used earlier! Just something to remember--if you're trying to send a piece of data to almost every page in your web app, 1.) you probably want to use render() in your views, and 2.) you want to find a way to add your data to your app's RequestContext. (https://docs.djangoproject.com/en/dev/ref/templates/api/#django.template.RequestContext says more about this!)

Save and commit your changes in adding login/logout functionality.

==== Modify views and templates to use model data ====

Let's edit our views and templates so they use real bookmark data from our database!

=====Add more data=====

First, let's add a bit more bookmark and tag data to our database. We'll use a Python loop to add a lot of bookmarks at once.

<source lang="bash">
# in django-for-designers/myproject
$ python myproject/manage.py shell
</source>

That will bring us to the Django management shell. Within that special Python prompt, type the following:

(Remember, any time you see ">>>", you don't have to type those characters.)

<source lang="python">
>>> import django.contrib.auth.models
>>> me = django.contrib.auth.models.User.objects.all()[0]
</source>

This tells Python we'll be accessing the user model, and then grabs the first user, storing it in a variable called ''me''.

Keeping that Python prompt open, run the following commands:

<source lang="python">
>>> import bookmarks.models
>>> import random
>>> tag_names = ['kids', 'read_on_plane', 'send_to_mom']
>>> # Create the Tag objects
>>> for tag_name in tag_names:
... tag = bookmarks.models.Tag(slug=tag_name)
... tag.save()
...
>>> kids_urls = ['http://pbskids.org/', 'http://www.clubpenguin.com/', 'http://www.aplusmath.com/hh/index.html', 'http://kids.nationalgeographic.com/kids/activities/recipes/lucky-smoothie/', 'http://www.handwritingforkids.com/', 'http://pinterest.com/catfrilda/origami-for-kids/', 'http://richkidsofinstagram.tumblr.com/', 'http://www.dorkly.com/picture/50768/', 'http://www.whyzz.com/what-is-paint-made-of', 'http://yahooligans.com/']
>>> for kids_url in kids_urls:
... b = bookmarks.models.Bookmark(author=me, url=kids_url)
... b.save()
... how_many_tags = random.randrange(len(tag_names))
... for tag_name in random.sample(tag_names, how_many_tags):
... b.tag_set.add(bookmarks.models.Tag.objects.get(slug=tag_name))
...
>>>
</source>

This creates a number of bookmarks, tagged appropriately! You can exit the shell by typing:

<source lang="python">
>>> exit()
</source>

===== Get views.py to talk to our models =====

Then, we'll need to have our views.py file import the bookmark model and send data to the index view.

<source lang="python">
from django.shortcuts import render
from bookmarks.models import Bookmark


def index(request):
bookmarks = Bookmark.objects.all()
context = {
'bookmarks': bookmarks
}
return render(request, 'index.html', context)
</source>

Wait! We don't actually want to load every bookmark in our database when we go to the front page. If we have lots of bookmarks, that will get slow and unwieldy quickly.

Instead, let's show the 10 most recent bookmarks:

<source lang="python">
def index(request):
bookmarks = Bookmark.objects.all().order_by('-timestamp')[:10]
context = {
'bookmarks': bookmarks
}
return render(request, 'index.html', context)
</source>

We also want our tag view to show real bookmarks. We only want to show bookmarks that have been tagged with the given tag. So we can write:

<source lang="python">
from django.shortcuts import render
from bookmarks.models import Bookmark, Tag


[...]

def tag(request, tag_name):
tag = Tag.objects.get(slug=tag_name)
bookmarks = tag.bookmarks.all()
context = {
'tag': tag,
'bookmarks': bookmarks,
}
return render(request, 'tag.html', context)
</source>

===== Change templates to handle bookmark data =====

Now, let's modify our templates to use this data.

In index.html:

<source lang="html4strict">
{% block content %}
<ul class="bookmarks">
{% for bookmark in 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>
{% endfor %}
</ul>
{% endblock %}
</source>

Instead of writing out each bookmark list element individually ahead of time, we are using a Django template language for loop to create a list element for each bookmark. Thus, we only have to specify the HTML formatting of our list elements once!

Start your development server and look at http://localhost:8000 now. You'll see that instead of the fake HTML we had before, each link's text is the unicode representation of each bookmark in your database. Cool, huh?

That's okay, but the unicode representation isn't really what we want to go there. Let's fix that:

<source lang="html4strict">
<a class="bookmark-link" href="{{ bookmark.url }}">
{% if bookmark.title %}{{ bookmark.title }}{% else %}{{ bookmark.url }}{% endif %}
</a>
</source>

If the bookmark has a title, we'll make the title the link. Otherwise we'll show the URL. We also fill in the URL on the <a> tag, to make the link work.

We also want to fill in the other metadata, like tags and author info. To do that:

<source lang="html4strict">
<div class="metadata"><span class="author">Posted by {{ bookmark.author }}</span> | <span class="timestamp">{{ bookmark.timestamp }}</span>
{% if bookmark.tag_set.all %}| <span class="tags">
{% for tag in bookmark.tag_set.all %}
<a href="{% url 'bookmarks.views.tag' tag.slug %}">{{ tag.slug }}</a></span>
{% endfor %}
{% endif %}
</div>
</source>

We check if there are any tags for the bookmark, and if so loop over the tags to put each of them in.

Additionally, we use Django's {% url %} tag to generate the URL for our tag's link. The URL tag takes first an argument which is the path of a particular view function (bookmarks.views.tag), then additional arguments for any input variables that the function expects to glean from the URL. (As you may recall, the tag view takes an argument tag_name, which is the name of the tag in question).

Why use {% url %} instead of just writing "/tags/{&#8288;{ tag.slug }}"? Django principle of DRY--Don't Repeat Yourself--means that we want to avoid duplicating work as much as possible. If later down the line we decided to change our URL structure so that tag pages would appear at "/bookmarks/by_tag/<tag_name>" instead, we'd have to go in and fix all these hard-coded URL patterns by hand. Using the {% url %} tag makes Django generate our URL for us, based on our urls.py file, so any changes we make automatically get propagated outward!

Run

<source lang="bash">
# in django-for-designers/myproject
$ python manage.py runserver
</source>

and confirm that your tag links working and our other changes are visible. It's easy to typo or make a mistake with {% url %} tags, so we want to make sure everything is working.

The default timestamp formatting that Django gave us is pretty neat, but in our original mockup we just used the date--not this long timestamp. Fortunately, Django's built-in filters make it easy to format a date or time any way we want! Since we just want to show the date, we'll use the date filter:

<source lang="html4strict">
<span class="timestamp">{{ bookmark.timestamp|date:"Y-m-d" }}</span>
</source>

We tell Django that we're using a filter via the |filter_name syntax. The arguments that come after the colon are a standard Python code for describing different ways of formatting dates. You can read more about the date filter and all the different formatting codes at https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date. For our purposes, Y outputs the full four-digit year, while m and d outputs the month and the day as two digit numbers. The hyphens we put between them are included in the formatting, too -- if we wanted the date to use slashes instead, we'd simply write |date:"Y/m/d".

Spin up your dev server, if you haven't got it running already, and check out your changes! Then save and commit your precious work.

==== Dealing with errors ====

Let's update our tag.html template to use the same formatting as index.html. Since we're using the same formatting for the list of bookmarks, and there aren't any other content block differences between index.html and tag.html, we can simply modify our tag template to inherit from index.html. Then the only block we need to overwrite is the subheader:

<source lang="html4strict">
{% extends 'index.html' %}

{% block subheader %}Bookmarks tagged {{ tag }}{% endblock %}
</source>

Now we can go to http://localhost:8000/tags/funny and see a nicely styled list of bookmarks tagged with that tag!

What happens if we go to http://localhost:8000/tags/asdfjkl?

Eek, a DoesNotExist error! That's not so great. Our application is erroring on one of our view lines:

<source lang="python">
tag = Tag.objects.get(slug=tag_name)</source>

Fortunately, Django has a shortcut function that can help us -- get_object_or_404(). This function will attempt to get a Django model based on the parameters you give it, and if it fails, automatically throw an HTML 404 error.

<source lang="python">
from django.shortcuts import render, get_object_or_404
from bookmarks.models import Bookmark, Tag


[...]


def tag(request, tag_name):
tag = get_object_or_404(Tag, slug=tag_name)
bookmarks = tag.bookmarks.all()
context = {
'tag': tag,
'bookmarks': bookmarks,
}
return render(request, 'tag.html', context)
</source>

Now http://localhost:8000/tags/asdfjkl will throw a nicer 404 error. We could even make a pretty 404.html template to handle such errors! There are also shortcuts in Django for 500 and 403 (Forbidden) errors, if you want to handle and style those as well.

Save and commit your error-catching work.

==== Let me show off ====

So far, we've all only been able to access our own Django-powered sites. In this section, you will see how to access my (the instructor's) super cool bookmarks site! (We'll talk later about how you can share your code the same way. It might require firewall configuration changes on your computer, so we save that complexity for later.)

Assuming that you and the instructor are on the same wifi network, you can visit a temporary website on the instructor's computer. Visit this link:

http://192.168.1.1:8000/

(If you are wondering exactly who can connect to the instructor's app, it is typically only people on the same wifi/wired network as her, rather than the whole Internet. This is due to the a technique in wide use called "network address translation", and it is not considered perfect security.)

When you visit the instructor's app, you are interacting with the database stored on her laptop. So all the changes made by other people in the room are reflected in what you see! So try not to be too obscene.

<!-- Instructor git note: git push origin HEAD:pre-part-4 00>

=== Part 4: CRUD ===

Right now, we have a nice website that displays data from our database. Unfortunately, though, while we have a mock bookmark form in the app header, currently the only way to create new bookmarks is in the Python shell. That's not fun at all.

This section deals with "CRUD" functionality, which are key to all web applications. CRUD stands for Create, Read, Update, and Delete. We already have Reading bookmarks covered; now we need to handle Creating bookmarks!

==== Django forms ====

Django comes with some built-in classes that make it easy to create forms with built-in validation and other functionality. There are plain django.forms classes (which are useful for encapsulating forms-related processing functions all in one place) as well as ModelForm classes that create a form based on a Django model that can automagically save instances of that model. For this application, let's use ModelForms to make a form for our Bookmark model.

===== Making a basic, working ModelForm =====

Inside our bookmarks app folder, let's make a file named forms.py. Edit it to import ModelForm and our models, then create a BookmarkForm class:

<source lang="python">
from django import forms
from bookmarks.models import Bookmark


class BookmarkForm(forms.ModelForm):
class Meta:
model = Bookmark
</source>

The generated BookmarkForm class will have a form field for every Bookmark model field--the field type based on some defaults.

Let's add our BookmarkForm to our views and templates!

Edit views.py:

<source lang="python">
from django.shortcuts import render, get_object_or_404
from bookmarks.models import Bookmark, Tag
from bookmarks.forms import BookmarkForm


def index(request):
bookmarks = Bookmark.objects.all().order_by('-timestamp')[:10]
form = BookmarkForm()
context = {
'bookmarks': bookmarks,
'form': form
}
return render(request, 'index.html', context)
</source>

Then edit index.html to take advantage of the new form object in our context:

<source lang="html4strict">
{% block bookmark_widget %}
{% if request.user %}
<div id="new-bookmark-widget">
<form method="post">
<h3>New bookmark</h3>
{{ form.as_p }}
<p><button id="new-bookmark-submit">Submit</button>Submit</button>
</form>
</div>
{% endif %}
{% endblock %}
</source>

The .as_p call makes the form put each field inside a paragraph tag. There are similar methods for making the form appear inside a table or inside divs.

The other thing we need to do here is add a CSRF token to our form, since that isn't included by default. A CSRF token is a special bit of code, built into Django, that protects your site from Cross Site Request Forgeries. It tells Django that a POST from this form really came from this form, not some other malicious site. Django makes this super easy:

<source lang="html4strict">
{% block bookmark_widget %}
{% if request.user %}
<div id="new-bookmark-widget">
<form method="post">
{% csrf_token %}
<h3>New bookmark</h3>
{{ form.as_p }}
<p><button id="new-bookmark-submit">Submit</button>Submit</button>
</form>
</div>
{% endif %}
{% endblock %}
</source>

Just add the csrf_token tag inside your form, and it's good to go.

Also note that the form call doesn't include the external form tags or the submit button -- Django leaves those for you to write yourself. This makes things more flexible if you want to add additional elements inside the form tag, or if you want to combine multiple Django Form objects into one HTML form.

Spin up http://localhost:8000/, login if you're not logged in already, take a look at your beautiful new form!

Right now, if you try to submit bookmarks with this form, it'll just reload the page. That's because we haven't told the view to do anything with any form data that gets sent to it! Let's edit our index view in views.py to make it do something with this form's data:

<source lang="python">
def index(request):
if request.method == "POST":
form = BookmarkForm(request.POST)
if form.is_valid():
form.save()
bookmarks = Bookmark.objects.all().order_by('-timestamp')[:10]
form = BookmarkForm()
context = {
'bookmarks': bookmarks,
'form': form
}
return render(request, 'index.html', context)
</source>

What's going on here? We first check to see if the request method was a POST (as opposed to a GET, the usual method your browser uses when you're just reading a web page). If so, we use our ModelForm class to make an instance of the ModelForm using the data that we received via the POST from the HTML form's fields. If the form's built-in validator functions come back clean, we save the ModelForm, which makes a shiny new Bookmark in our database!

Save your work, then try making some new bookmarks via your new form. The page should reload, with your new bookmark at the top of the list! High five!

Save and commit your wonderful bookmark-creating Django form.

===== Customize your form fields =====

So our form works, but it doesn't look the way we originally expected. For one, there's a dropdown asking us to specify the bookmark's author. For another, we're missing a field for tags. We'll need to customize our BookmarkForm if we want it to look the way we want.

First, let's make the default author always the currently logged-in user. In views.py:

<source lang="python">
def index(request):
if request.method == "POST":
form = BookmarkForm(request.POST)
if form.is_valid():
form.save()
bookmarks = Bookmark.objects.all().order_by('-timestamp')[:10]
current_user = request.user
form = BookmarkForm(initial={'author': current_user})
context = {
'bookmarks': bookmarks,
'form': form
}
return render(request, 'index.html', context)
</source>

Now if you reload, thanks to the initial data we provided our BookmarkForm, you'll see that your account is chosen by default in the author dropdown.

Then let's hide the author field from the user. We'll do this in our ModelForm specification in forms.py:

<source lang="python">
from django import forms
from bookmarks.models import Bookmark


class BookmarkForm(forms.ModelForm):
class Meta:
model = Bookmark
widgets = {
'author': forms.HiddenInput(),
}
</source>

Now if you restart your server, you'll see the author field appears to be gone! Instead of the default text input widget, we told Django to use a hidden field for the author, so we no longer see it.

Since titles aren't even required, the URL is the most important piece of data for a bookmark. Let's change the order of the fields, so the URL comes first. Again in forms.py

<source lang="python">
class BookmarkForm(forms.ModelForm):
class Meta:
model = Bookmark
fields = ('author', 'url', 'title')
widgets = {
'author': forms.HiddenInput(),
}
</source>

Save and commit your changes.

===== Save tags with our form =====

Finally, let's add a tags field:

<source lang="python">
class BookmarkForm(forms.ModelForm):
class Meta:
model = Bookmark
fields = ('author', 'url', 'title', 'tags')
widgets = {
'author': forms.HiddenInput(),
}
tags = forms.CharField(max_length=100, required=False)
</source>

Note that since tags isn't a field in the Bookmark model, we're going to need to make sure it gets saved separately. For that, let's go back to views.py:

<source lang="python">
from django.shortcuts import render, get_object_or_404
from bookmarks.models import Bookmark, Tag
from bookmarks.forms import BookmarkForm
import urllib


def index(request):
if request.method == "POST":
form = BookmarkForm(request.POST)
if form.is_valid():
new_bookmark = form.save()
raw_tags = form.cleaned_data['tags'].split(',')
if raw_tags:
for raw_tag in raw_tags:
raw_tag = raw_tag.strip()
raw_tag = raw_tag.replace(' ', '-')
raw_tag = urllib.quote(raw_tag)
tag_slug = raw_tag.lower()
tag, created = Tag.objects.get_or_create(slug=tag_slug)
tag.save()
tag.bookmarks.add(new_bookmark)
bookmarks = Bookmark.objects.all().order_by('-timestamp')[:10]
current_user = request.user
form = BookmarkForm(initial={'author': current_user})
context = {
'bookmarks': bookmarks,
'form': form
}
return render(request, 'index.html', context)
</source>

What's happening here? First, we're saving our ModelForm to save our original bookmark, as is. Then we're accessing our ModelForm's cleaned_data attribute to look for our string of comma-delineated tags. (We use cleaned_data instead of data because that one was pre-sanitized when we ran form.is_valid().) We split the string on the commas, clean up the tags by removing excess whitespace, making them all lowercase, turning spaces into hyphens, and then using urllib to quote any remaining special characters. Then we use a model shortcut function called get_or_create(). What get_or_create() does is see if there's already a tag with this slug. If so, it returns us the old tag, plus a False argument since it didn't make anything new. If not, it creates a new tag with the slug, and returns that (plus True, since it did make a new tag).

Finally, we save our tag, then add our new bookmark to its ''bookmarks'' Many-to-Many attribute to link them together.

Phew! Save your work, let your development server automatically restart, and try adding a bookmark with some tags. It should work!

Make sure to commit your work.

==== CRUD with asynchronous Javascript ====

That's pretty cool, but what if we don't want to make our users reload the page every time we want them to see new data? Most web apps are going to involve asynchronous Javascript in one way or another.

Let's modify our application to send our new bookmarks asynchronously to the server, and make the new bookmark appear on the page without reloading!

(Warning: we'll be writing some Javascript in this section.)

First, we need to tell our templates to import some Javascript files. In 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">
<script src="/static/js/jquery-1.9.1.min.js"></script>
<script src="/static/js/script.js"></script>
</head>
</source>

Right now, our static/js/script.js is blank. Let's write a quick and dirty JS function that gets triggered when someone submits the bookmarks form:

<source lang="javascript">
$(
function(){
$('#new-bookmark-widget form').on('submit', function(e){
e.preventDefault();
var inputs = $('#new-bookmark-widget input');
var data = {};
$.each(inputs, function(index){
var input = inputs[index];
data[input.name] = input.value;
});
$.ajax({
type: "POST",
url: '/',
data: data,
success: function(data){
$('.bookmarks').prepend('<li>WHEEE!!!</li>');
}
});
});
}
);
</source>

What this does is when the user submits the bookmarks form, it prevent the page from reloading (like it would normally), serializes all the form fields, POSTs them to localhost:8000, and upon successfully receiving a response from the server displays a message at the top of our list of bookmarks.

If you try this out, you'll see that our JS-y form makes real bookmarks, just like the old version of our form did! However, it's putting junk in our bookmarks list on the page; we have to reload to actually see the bookmark our ajax call created.

There's several ways we could fix this. We could make our views.py send back a JSON serialization of our bookmark data, and have our JS file turn that into a list element somehow (using either a JS template or a giant string). Or we could have Django do the template rendering for us, and send our JS script the raw HTML we want it to use. This is called AHAH (Asychronous HTML and HTTP), or sometimes PJAX.

For the sake of speed and simplicity, we'll go with the latter. First, though, we need to refactor our templates a little.

Create a new template called bookmark.html, and paste in the bookmark list element from index.html in there.

<source lang="html4strict">
<li>
<a class="bookmark-link" href="{{ bookmark.url }}">{% if bookmark.title %}{{ bookmark.title }}{% else %}{{ bookmark.url }}{% endif %}</a>
<div class="metadata"><span class="author">Posted by {{ bookmark.author }}</span> | <span class="timestamp">{{ bookmark.timestamp|date:"Y-m-d" }}</span>
{% if bookmark.tag_set.all %}| <span class="tags">
{% for tag in bookmark.tag_set.all %}
<a href="{% url 'bookmarks.views.tag' tag.slug %}">{{ tag.slug }}</a></span>
{% endfor %}
{% endif %}
</div>
</li>
</source>

Note that unlike the other templates, this template doesn't inherit anything -- it's just a block of HTML with some template markup.

Then, rewrite index.html so the content block looks like this:

<source lang="html4strict">
{% block content %}
<ul class="bookmarks">
{% for bookmark in bookmarks %}
{% include 'bookmark.html' %}
{% endfor %}
</ul>
{% endblock %}
</source>

Reload the page. Nothing should have changed, in terms of how the page looks. We've just changed the structure of the templates. Using the include tag, the index template drops in the contents of bookmarks.html rendered with the values for a given bookmark, for each bookmark in the list.

Now we're ready to teach our view to send back a partial bit of HTML.

<source lang="python">
def index(request):
if request.method == "POST":
form = BookmarkForm(request.POST)
if form.is_valid():
new_bookmark = form.save()
raw_tags = form.cleaned_data['tags'].split(',')
if raw_tags:
for raw_tag in raw_tags:
raw_tag = raw_tag.strip()
raw_tag = raw_tag.replace(' ', '-')
raw_tag = urllib.quote(raw_tag)
tag_slug = raw_tag.lower()
tag, created = Tag.objects.get_or_create(slug=tag_slug)
tag.save()
tag.bookmarks.add(new_bookmark)
return render(request, 'bookmark.html', {'bookmark': new_bookmark})
else:
bookmarks = Bookmark.objects.all().order_by('-timestamp')[:10]
current_user = request.user
form = BookmarkForm(initial={'author': current_user})
context = {
'bookmarks': bookmarks,
'form': form
}
return render(request, 'index.html', context)
</source>

What are we doing here? First, we're turning our request.POST test into a true choice -- we no longer go on to send back the full index.html rendered template if the request is a POST. Second, if we manage to create a bookmark successfully, we send back just our bookmark.html -- not the full index.html -- rendered with the data for our new bookmark.

Reload your development server and your web page, and try creating a bookmark. It should appear right away on the page now!

Try submitting the form with a tag but no URL. Nothing happens, right? If you check the server log in your terminal window, you'll see an error message:

<source lang="bash">
ValueError: The view bookmarks.views.index didn't return an HttpResponse object.
</source>

Eek. Our index view doesn't handle the case where the request is a POST, but the form doesn't validate, and Django noticed this and returned an error. Let's fix this:

<source lang="python">
def index(request):
if request.method == "POST":
form = BookmarkForm(request.POST)
if form.is_valid():
new_bookmark = form.save()
raw_tags = form.cleaned_data['tags'].split(',')
if raw_tags:
for raw_tag in raw_tags:
raw_tag = raw_tag.strip()
raw_tag = raw_tag.replace(' ', '-')
raw_tag = urllib.quote(raw_tag)
tag_slug = raw_tag.lower()
tag, created = Tag.objects.get_or_create(slug=tag_slug)
tag.save()
tag.bookmarks.add(new_bookmark)
return render(request, 'bookmark.html', {'bookmark': new_bookmark})
else:
response = 'Errors: '
for key in form.errors.keys():
value = form.errors[key]
errors = ''
for error in value:
errors = errors + error + ' '
response = response + ' ' + key + ': ' + errors
return HttpResponse('<li class="error">' + response + '</li>')
else:
bookmarks = Bookmark.objects.all().order_by('-timestamp')[:10]
current_user = request.user
form = BookmarkForm(initial={'author': current_user})
context = {
'bookmarks': bookmarks,
'form': form
}
return render(request, 'index.html', context)
</source>

Now in the error case, we cheekily send back some HTML containing the errors that Django found, so there's no circumstance in which this view fails to provide some sort of response. (If we wanted to go to more effort, we could send back the errors as JSON, teach our callback function how to tell the difference between JSON and plain HTML responses, and render the former differently, instead of sticking our form error messages in the bookmarks list. But we'll run with this for now.)

Save and commit your JS-ification work!

In the real world, if you were doing lots of this sort of manipulation, instead of AHAH you might want to be using a Javascript framework such as Backbone to avoid getting confused, messy code. You'd also want to use templates for the elements that your Javascript hooks are adding and modifying. Ideally, you'd want those templates to be the same ones that your Django application used, to avoid repeating yourself! There's a lot of ways Django users deal with these problems. One way to do this is to use Django as an API data engine and do all the routing, application logic, and template rendering--at least, on the JS-heavy pages--via a Javascript-based template language. [http://django-tastypie.readthedocs.org/en/latest/ Tastypie] is a popular Django application for making APIs for this sort of thing. Another approach is to teach Django to speak the language of a JS-based templating language. Projects like [https://github.com/yavorskiy/django-handlebars django-handlebars] or [https://github.com/mjumbewu/djangobars djangobars] are examples of this approach!

<!-- Instructor note: Note that your 'git status' really ought to be empty. Is it? If not, fix the instructions by adding a git commit etc. in the above. Then, git push origin HEAD:pre-part-5 -->

=== Part 5: Style time ===

So that we're all on the same page, even if you didn't get all the way through the previous part, you should do these steps right now:

<source lang="bash">
# in django-for-designers/myproject
$ git branch my-branch-5 origin/pre-part-5
$ git checkout my-branch-5
</source>

Take some time to hack on your bookmarks app to make it more awesome. Whether that means changing the HTML to add your name in the footer, or customizing the CSS to look the way you want, or adding new JS features, or even (if you're daring) implementing new Django features (say, public vs. private bookmarks?)! Or just taking a quick nap. It's up to you!

At the end of this time, we'll learn how to show our (now unique!) Django apps to each other and the world.

Make sure to commit your changes! You will probably want to do many small commits, to track your progress and make it easy to revert and try again without losing too much progress.

=== Part 6: Sharing with others ===

Now that you've built a web app, you might want to enable other people to see it.

==== With runserver ====

The first, easiest way to let other people see it is to let other people connect to your computer. (Note that this might not work if your computer has a firewall enabled.)

You've already been using the Django ''runserver'' command, but you have been the only one able to connect to it. We'll change that with two steps.

First, we'll ask runserver to listen to requests from the whole world, which is not the default. Stop your current runserver with ctrl-c, and then run this command:

<source lang="bash">
# in django-for-designers/myproject
$ python manage.py runserver 0.0.0.0:8000</source>

Second, we need to find out what IP ''address'' your computer is using on the wifi network. To find that out, visit a temporary website on the instructor's computer. Visit this link:

http://192.168.1.1:8100/

You will see a number like ''192.168.3.11'' within the "Your IP" section of the page. That is your computer's IP address, and you can ask a neighbor to visit your web app by going to your IP address in their browser. For example, if your IP is 192.168.3.11, they should visit:

http://192.168.3.11:8000/

(If you are wondering exactly who can connect to your app, it is typically only people on the same wifi/wired network as you, rather than the whole Internet. This is due to the a technique in wide use called "network address translation", and it is not considered perfect security.)

When people visit your app, they are interacting with the database stored on your laptop.

(Side note: The instructor is using [https://github.com/paulproteus/address-shower this app] to run the service where you can see your IP address.)

==== Deployment on Heroku ====

This method of running the code requires that you use your laptop like a server, and keep it running all the time. This isn't how you want to generally share your work with the world.

A more common way to ''deploy'' Django code is to copy your code to a hosting service and ask them to run it for you. In this tutorial, we'll do that with Heroku.

Note that when you access a remotely-hosted version of your code, it will typically use a separate database. Any information you store there won't be reflected in the code you run on your own computer.

Make sure you have a Heroku account. Visit https://api.heroku.com/signup and either sign up or login.

Make sure you have the Heroku ''toolbelt'' installed on your own computer. You can test if you have them by running, within a terminal or command prompt:

<source lang="bash">
# in django-for-designers/myproject
$ heroku</source>

If you get a command not found error, visit https://toolbelt.heroku.com/ and install their toolbelt. You may need to quit and re-launch your terminal program for the program to become available.

Once you have the heroku tools, make sure you are logged in to Heroku with the following command:

<source lang="bash">
# in django-for-designers/myproject
$ heroku login</source>

To prepare the app to ''run'' on Heroku, we need to make a few changes. First, we must instruct Heroku on how to launch your app. Second, we must make your app work with their Postgres database, rather than sqlite like you are using now.

Heroku reads a file called ''Procfile'' that tells them how to run your app's ''runserver'' command. So, create a file called Procfile with the following contents:

<source lang="bash">
web: python manage.py runserver 0.0.0.0:$PORT --noreload
</source>

Make sure to put it at the ''top'' of the project directory, namely in ''django-for-designers'', not ''django-for-designers/myproject''.

To configure the database, we will employ a trick at the bottom of the settings file: if we detect we are running on Heroku, we change the configuration to the one that Heroku provides.

To do that, you need to open ''settings.py'', go to the ''end'', and add the following lines:

<source lang="python">
import os
if 'DATABASE_URL' in os.environ:
import dj_database_url
DATABASES['default'] = dj_database_url.config()
</source>

Since Heroku indicates the configuration through an environment variable called DATABASE_URL, this configuration block will only apply on Heroku. Therefore, it is reasonable to commit this. So we'll do that:

<source lang="bash">
# in django-for-designers
$ git commit -a -m "Adding Heroku database config"</source>

Now you'll need to actually deploy it! To do that, make sure you are logged into Heroku on your computer by running:

<source lang="bash">
# in django-for-designers
$ heroku login
</source>

Then create a Heroku URL for your app:

<source lang="bash">
# in django-for-designers
$ heroku create
</source>

Take the step of copying your code to Heroku's servers via git:

<source lang="bash">
# in django-for-designers
$ git push heroku HEAD:master
</source>

Now Heroku has a copy of your code, and is configured to run it when people visit the URL you saw when you ran ''heroku create''. But there is one more thing to do, which is to configure the database with the ''syncdb'' command.

<source lang="bash">
# in django-for-designers
$ heroku run 'cd myproject ; python manage.py syncdb --migrate'
</source>

The heroku shell will prompt you for questions that will seem earlier familiar.

After all that -- you are done! Congratulations, and enjoy your new live web app!

==== Brief discussion of other deployment options ====

Heroku is not the only way to deploy Python and Django apps to the web. There are a few other ways:

* If you rent "VPS" or dedicated server space, or connect a machine to the Internet and refuse to turn it off, you can install serve your app out through Apache and mod_wsgi, or any number of ways.
* If you want other hosting similar to Heroku, we recommend taking a look at OpenShift or CloudFoundry, or any number of other 'Platform as a service' systems.
* If you want something in the middle, you can use a service like WebFaction.

==== Sharing on Github ====

So far, people have been able to interact with your app, but they haven't been able to view its source code. In this section, you will share your app's code publicly on the web.

You'll also be able to browse your app's code yourself on Github!

(One important note: if the security of your app mattered, you would need to hide the ''SECRET_KEY'' variable from onlookers. For now, we will carry on without worry about that.)

You've been using git all along, but with Github, you can publish your work too.

First things first, you will need an account on Github. Visit https://github.com/ and create an account, or sign in if you already have one.

Then, click the button to ''create a new repo''. It is in the top-right, and should take you to https://github.com/new .

For consistency, make the repository name ''django-for-designers''. Be sure ''not'' to check the box asking to initialize the repo. Then click ''Create repository''. Wait for your new empty Github project page to load in your web browser.

Once you have done that, you will need to reconfigure the git repository on your own computer so it can easily copy data to Github. To do that, we will inform it about a new ''remote'' repository. For this to work, you have to visit your Github project page and find the "HTTP" link specific to django-for-designers within your Github account. Copy that to the clipboard, and then type:

<source lang="bash">
# in django-for-designers
$ git remote add mygithub __PASTE_HERE__
</source>

Once you have added it, you can push there:

<source lang="bash">
# in django-for-designers
$ git push mygithub --all
</source>

Now just reload that Github page, and you should see the files you have been working on! You'll also see that since we passed ''--all'' to git, all the branches are available on Github.

=== Part 7: Exercises for the reader ===

==== Updating and deleting bookmarks (the last two parts of CRUD) ====

==== Write your own styles / JS frontend behavior ====

==== Handling user-uploaded media ====

(let's add an image to the Tag, and add a form for it)

==== Installing django-debug-toolbar and what it's useful for ====

(explain what it is useful for)

(install it with pip)

(enable it in settings.py)

(show how it lets you see the template context when you visit your bookmarks site)

(mention that it "absorbs" redirects)

(mention DEBUG=True is required for it to show, which is mostly a good thing)

==== Writing your own tests ====

Automated testing is a way to run code that verifies your code. There are a few benefits to writing tests:

* If you wrote the tests before you wrote the code, then you can vividly see that your code worked: the test failed before you wrote the code, and it passes after you wrote it.

* Having a test suite gives you confidence that a change you are making does not break existing functionality. Since many, ''many'' bug fix accidentally break other functionality, this is of huge importance.

* Having a test suite gives other people confidence that your app does the things it says it does. This is helpful when showing the code to another developer or when listing its features to another stakeholder.

Django has your back here: it comes with a built-in ability to run tests if you write them, and it comes with a test suite of its own.

First, let's take a look at how to run tests. The ''auth'' app that we have been using for user login provides a test suite. You can run that test suite as follows:

<source lang="bash">
# in django-for-designers/myproject
$ python manage.py test auth
</source>

You'll see a lot of '''.''' characters print out, and finally a message like:

<pre>
Ran 181 tests in 7.764s

OK
Destroying test database for alias 'default'...
</pre>

Congratulations! You can now rest assured that the ''auth'' app works properly. (This is good, since you are relying on it!)

In the rest of this section, you'll see how to write your own tests and learn other helpful tips about testing.

===== Running and writing tests for bookmarks =====

We can run just the tests for our bookmarks app as follows:

<source lang="bash">
# in django-for-designers/myproject
$ python manage.py test bookmarks
</source>

We'll see something like:

<pre>
Creating test database for alias 'default'...
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK
</pre>

It may be surprising that there is even one test to run, given that so far, we have not paid any attention to tests! Take a look at bookmarks/tests.py; in there, you'll see some automatically created hints by Django about how to write new tests, and one simple test.

Let's deconstruct that test right now:

<source lang="python">
from django.test import TestCase


class SimpleTest(TestCase):
def test_basic_addition(self):
"""
Tests that 1 + 1 always equals 2.
"""
self.assertEqual(1 + 1, 2)
</source>

This is a Python class named ''SimpleTest'', inheriting from django's ''TestCase'' class. This inheritance is required for Django to ''discover'' the test when you run ''manage.py test''. (You can read more about that [https://docs.djangoproject.com/en/dev/topics/testing/ here].)

It has one method; the fact that it starts with ''test_'' is also essential to it being discovered. Within the method, there is a text description (known as a docstring), and finally, a one-line body.

This body is the heart of the test. It calculates a value, and then ''asserts'' that is equal to a stored, known-correct value.

To make this clearer, let's change the test so that it asserts that 1+1 is 3. To do that, replace the class with the following:

<source lang="python">
from django.test import TestCase


class SimpleTest(TestCase):
def test_basic_addition(self):
"""
Tests that 1 + 1 always equals 2.
"""
self.assertEqual(1 + 1, 3)
</source>

Now run the bookmarks test suite:

<source lang="bash">
# in django-for-designers/myproject
$ python manage.py test bookmarks
</source>

As the test runs, you see a '''F''' printed rather than pleasing, relaxing '''.'''. Additionally, once all the tests are over, Django's test runner prints the details of what failed:

<pre>
======================================================================
FAIL: test_basic_addition (bookmarks.tests.SimpleTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File ".../bookmarks/tests.py", line 16, in test_basic_addition
self.assertEqual(1 + 1, 3)
AssertionError: 2 != 3
</pre>

Now that we have a basic understanding of tests, go into ''bookmarks/tests.py'' and remove the SimpleTest class entirely.

===== Adding a new test =====

Writing tests for a Django app is similar to interacting with the app in the manage.py shell. One key difference is that every test case starts out with a blank database, rather than using your app's database.

Let's add a test for your bookmarks app that verifies the index view: namely, that if there is a bookmark in the database, it gets passed to the template in a context variable called ''bookmarks''.

To do that, open up bookmarks/tests.py and add the following to the end of the file.

<source lang="python">
from django.contrib.auth.models import User
from bookmarks.models import Bookmark

class BookmarkViewTest(TestCase):
def test_bookmark_shows_up(self):
# Create a user to own the bookmark
me = User.objects.create(username='me')

# Create a sample bookmark
mark = Bookmark.objects.create(user=me, title='Title of the song', url='http://example.com/')

# Visit the home page
c = Client()
response = c.get('/')

# Make sure it has the bookmark data
as_sent_to_template = response.context['bookmarks']
self.assertTrue(mark in as_sent_to_template)
</source>

This class has just one method, which begins with ''test_'' so that it can be picked-up by the Django test runner.

In the test database, which starts out as blank, there are no users, so we must create one. (You can change that through [https://docs.djangoproject.com/en/dev/topics/testing/overview/#django.test.TestCase.fixtures test fixtures].)

Then we create one bookmark, and ask a the Django test client to load the home page. The test client is like a special, Django-aware web browser. Instead of doing an HTTP request against the actual website, it calls code directly within the Django app. This gives it access to, for example, the template context.

Finally, we extract the bookmark list sent to the template, and we verify that the bookmark we created shows up in the list.

If we run this test, we will see the familiar (if terse) output from the test runner:

<source lang="bash">
# in django-for-designers/myproject
$ python manage.py test bookmarks
</source>

<pre>
Creating test database for alias 'default'...
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK
Destroying test database for alias 'default'...
</pre>

Hooray! You have written your first test.

Notice that test only validates that we pass the correct data to the template. We do not yet validate that the template actually renders the bookmark! To do that, you would need to test ''response.body''.

One other aspect of testing that can be very helpful is using tests to help you refactor your code. (Refactoring is the process of taking code that works and making it simpler to understand without breaking it.)

In particular, our index view contains some string manipulation to normalize tags. That code, so far, has been somewhat under-specified; we do not really have a statement anywhere of precisely how it should work. One very valid exercise would be to remove that code from index, move it into a function, and then write at few test cases to cover it with tests.

At some point, you may be wondering how you know if you have written enough tests. One answer is by measuring your code's ''coverage'', which is the fraction of your web app that the test suite executes. You can be more confident that the code works properly if it is covered by your test suite. Django does not come with coverage tools built-in, but with the help of [https://bitbucket.org/kmike/django-coverage/ django-coverage] you can take those measurements. Code coverage is not a perfect way to determine if you have written enough tests, but getting your code to 100$ covered is a solid first goal.

==== Providing your data as an API ====

APIs, short for application programming interfaces, are a way for your Django site to provide services to other bits of code.

One common use for APIs is making data available to your own Javascript code. Another common use is making them available for other people to use in their own Python scripts or in Javascript widgets. In this section, you will learn how to add a Django app named ''tastypie'' to your project and see how it can easily make your data available.

===== Deciding on the API we'll provide =====

Before going forward, we should decide what kind of information we will export, and how we will export it.

For this example, since all the bookmarks on the site are public, we will provide a list of all bookmarks. We will let users of the API filter the bookmarks by the user, if they like. To keep things simple at first, we won't provide the bookmarks' tags.

===== Adding Tastypie as a dependency =====

First, we will edit ''requirements.txt'' to add a dependency on django-tastypie. To do so, we simply add one line to the end of requirements.txt:

<source lang="python">
django-tastypie
</source>

Now that our project depends on it, you can simply run (so long as the virtualenv is activated):

<source lang="bash">
# in django-for-designers
$ pip install -r requirements.txt
</source>

That will download and install all the dependencies for the app, which will bring in tastypie.

Finally, add it to INSTALLED_APPS, toward the end. Within that tuple, add this on a line on its own:

<source lang="python">
'tastypie',
</source>

With all those changes made, let's commit:

<source lang="bash">
# in django-for-designers
$ git commit -a -m 'Adding dependency on tastypie'
</source>

===== create blank api.py file and add it to urls.py =====

* Create api.py, with a basically blank Resource subclass

* Add relevant stuff to urls.py

* Test that viewing our app at the API URL doesn't crash

* git commit

===== Add our api method to api.py =====

* Update Python code to export data via Tastypie

* Demo that it works, in a browser

* git commit

===== Add the ability to filter by user =====


* Update Python code to export data via Tastypie

* Demo that it works, in a browser

* git commit

===== Further directions =====

* Exporting tags

==== Regular expressions ====

Text patterns can be encoded as ''regular expressions''. You've already seen them in urls.py. We won't go into great depth here, but we will recommend some resources so you can understand them well, visualize them, and know how to write Python code that takes advantage of them.

First, get to know how they work. To do that, I recommend ''visualizing'' them.

===== Visualizing =====

To start with that, open up http://www.regexper.com/ and enter in one of the regular expressions we used. For example, enter this:
<pre>^$</pre>

Then, click the ''Display'' button (or hit ENTER on your keyboard). Contrast that with these examples:

<pre>^bookmarks\/$</pre>

(This is similar to the URL for the bookmarks view. We had to ''escape'' the slash character (/) by putting a backslash (\) before it because the regexper.com tool uses JavaScript-esque regular expressions rather than Python regular expressions. Let's ignore that for now.)

You'll see that it matches the ''exact'' string of "bookmarks/"; it also constrains things so that the line ends after the word "bookmarks/". If you remove that "$" character, the string "bookmarks/ahoy" would also match.

===== Executing regular expressions =====

You can execute regular expressions in a few ways.

First, there is the Python API for that. You can see it in action here:

* http://codepad.org/SFjk5YzF

You can also use http://regexpal.com/ to execute regular expressions

===== More reading and resources =====

* http://regexpal.com/ has a ''great'' Quick Reference that you can find by clicking on those words in the top right of the web page.

* http://www.diveintopython.net/regular_expressions/street_addresses.html explains more about why regular expressions are nice, and how to use them well.


==== Learn about relational databases ====

Django uses a ''relational'' database to store your data. In this section, we will explain the very basics of relational databases, give you pointers to more information, and show you how to explore your data.

Within the tutorial, we have used Django's ORM to access our data. That has obscured how the data is actually stored... so now it is time to take a quick look.

SQL is a query language for creating, reading, and modifying databases. SQL queries look something like:

<pre>SELECT title FROM bookmarks</pre>

You can get access to a SQL shell from within Django by running:

<source lang="bash">
# in django-for-designers/myproject
$ python manage.py dbshell
</source>

and you can now execute the above query.

These databases are considered ''relational'' because the normal way to use them involves using references between tables to avoid repetition of data. In our tutorial, for example, a Bookmark pointed to a User. This is not the only way to build the app; we could have embedded all of the user's information within each Bookmark. That would have a major downside: the various copies of the information could get out of date.

This tutorial used sqlite, a very popular relational database, but there are plethora of others, including:

* MySQL
* Postgres
* Oracle
* Microsoft SQL Server

Django can be used with any of these. sqlite is convenient because it requires no setup step; however, popular hosting services typically use MySQL or Postgres. sqlite has one major downside -- it cannot support multiple changes to the database made while each other are being made. That limitation is completely OK for an app running on your laptop.

Even though Django's ORM looks just like Python, it does complex work behind the scenes. This work deserves some respect! One common source of slowness in web applications is executing ''too many'' SQL queries.

===== Exploring your app's data =====

The simplest way to explore your app's data visually is by finding the ''database.db'' file and opening it in a graphical browser. On Linux, Mac, or Windows, install Firefox, and then install the https://addons.mozilla.org/en-us/firefox/addon/sqlite-manager/ add-on.

From there, you can open your database and browse around.

Try to run the above SQL query against your database within SQLite Manager.

===== Other tools =====

* Use http://sqlzoo.net/ to learn more about SQL by trying interactively to write queries that solve problems.

* Use http://sqlfiddle.com/#!7/781d4/1 to try SQL queries within the web browser.

Latest revision as of 04:30, 9 January 2014

Introduction

In this tutorial, we will explain topics and provide commands for you to run on your own computer. You will leave with a working social bookmarking web app!

This is a tutorial on web programming, so we will go beyond just Django and discuss third-party Django apps and other real-world web development tools. We'll also be emphasizing areas of Django that particularly affect designers, such as static files, template inheritance, and AJAX.

Things you should know already

  • HTML familiarity
  • Basic Python proficiency
  • Basic or better Javascript proficiency
  • Pre-requisites that we will help with

We expect you to have git, Python, and a few other elements ready on your laptop before the tutorial. We have published a laptop setup guide that steps you through:

  • Installing Python, git, pip, virtualenv, and a reasonable text editor
  • Setting up your env with Django, South, and django-debug-toolbar
  • Basic command line knowledge (cd, ls, etc)
  • Basic git knowledge
  • Setting up your git repo for the tutorial

Things you do not need to know already:

  • Django :)
  • What an ORM is
  • Anything database related

Curriculum

A possibly-helpful cheatsheet for common command line operations (PDF)