Download as odp, pdf, or txt
Download as odp, pdf, or txt
You are on page 1of 109

Django Tutorial II

Django 入門簡介 ( 二 )

Django: a high-level Python Web framework


that encourages rapid development and
clean, pragmatic design.

On Freenode IRC: #python.tw


Speaker: timchen119 (aka. tim/ 使徒提姆 )
Review:
Web Announce System
#1: Start project (django-admin.py startproject)
#2: Create application (manage.py startapp)
#3: Write your model (models.py)
#4: Create Database/Install Models (settings.py)
#5: Add admin site (admin.py)
#6: Write URLConf (urls.py)
#7: Write Your View (views.py)
#8: Write Your Template (templatedir/index.html)
#9: Start Server (Apache/development server)
Review:

MVC
Django File Structures
Development Environment/Web Server
HelloWorld
Settings.py
Admin Interface (admin.py)
Very Basic URLConf Usage
Very Basic Template Usage
Very Basic Model Usage
HTTP
Request

Controller (Django’s View)

View (Django’s Template) Model


Django 動手作 單元 4-2

Write Books Management System


Most Examples/Explanations come From
Django Book Chapter 5
Learn Object-relational mapper (ORM)
Learn Django Database Shell
Follow me. #1: Start project
django-admin startproject mysite

mysite/
__init__.py
manage.py
settings.py
urls.py
Follow me #2: Create application
cd mysite
python manage.py startapp books
mysite/books/
__init__.py
models.py
views.py
Follow me #3-1: Write model
mysite/books/models.py
from django.db import models #auto generate
class Publisher(models.Model):
name = models.CharField(max_length=30)
address = models.CharField(max_length=50)
city = models.CharField(max_length=60)
state_province =models.CharField(max_length=30)
country = models.CharField(max_length=50)
website = models.URLField()
Follow me #3-2: Write model

class Author(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=40)
email = models.EmailField()

class Book(models.Model):
title = models.CharField(max_length=100)
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher)
publication_date = models.DateField()
Explain Model

each model is represented by a Python class


that is a subclass of django.db.models.Model.
parent class, Model, contains all the machinery
necessary to make these objects capable of
interacting with a database
Each model generally corresponds to a single
database table, and each attribute on a model
generally corresponds to a column in that
database table.
SQL: Publisher model

CREATE TABLE "books_publisher" (


"id" serial NOT NULL PRIMARY KEY,
"name" varchar(30) NOT NULL,
"address" varchar(50) NOT NULL,
"city" varchar(60) NOT NULL,
"state_province" varchar(30) NOT NULL,
"country" varchar(50) NOT NULL,
"website" varchar(200) NOT NULL
);
id

we haven’t explicitly defined a primary key in


any of these models. Unless you instruct it
otherwise, Django automatically gives every
model an auto-incrementing integer primary
key field called id. Each Django model is
required to have a single-column primary key.
More Model Definition

Fields
CharField
EmailField
IPAddressField
DateTimeField
.... lots of fields to define
http://www.djangobook.com/en/1.0/appendixB/
Follow me #4: Create Database
notepad++ settings.py
DATABASE_ENGINE : sqlite3
DATABASE_NAME : books.db
INSTALLED_APPS = 'mysite.books'
python manage.py syncdb
# also create a superuser
Review: What Did We Do?

Project: mysite (a python package – dir with


__init__.py)
App: books (another python package – dir with
__init__.py)
Models: models.py (python module – a .py file)
INSTALLED_APPS: mysite.books – in
settings.py
Stop Here!

We Can Finish This.


(just like We wrote a Web Announce System
Last Time)
Could just:
Add admin.py
Add Some Views (controllers)
Add Some Templates (HTML pages)
However,
Let's Playing Django Models First.
Try Some Commands

python manage.py validate


python manage.py sqlall books
python manage.py syncdb
python manage.py dbshell
python manage.py shell
Basic Data Access (CRUD)
CRUD: Create,Read,Update,Delete
python manage.py shell
>>> from books.models import Publisher
>>> p1 = Publisher(name='Apress', address='2855
Telegraph Avenue',city='Berkeley',
state_province='CA', country='U.S.A.',
website='http://www.apress.com/')
>>> p1.save()
You Create and Saved a Publisher Object to
Database! :)
Basic Data Access (CRUD)

>>> p2 = Publisher(name="O'Reilly",
address='10 Fawcett St.',
... city='Cambridge', state_province='MA',
country='U.S.A.',
... website='http://www.oreilly.com/')
>>> p2.save()
>>> publisher_list = Publisher.objects.all()
>>> publisher_list
[<Publisher: Publisher object>, <Publisher:
Publisher object>]
2 Create Methods

p1 = Publisher(...)
# At this point, p1 is not saved to the database yet!
p1.save()
=========================================
>>> p1 = Publisher.objects.create(name='Apress',
... address='2855 Telegraph Avenue',
... city='Berkeley', state_province='CA',
country='U.S.A.',
... website='http://www.apress.com/')
__unicode__()

>>> publisher_list = Publisher.objects.all()


>>> publisher_list
[<Publisher: Publisher object>, <Publisher:
Publisher object>]
A __unicode__() method tells Python how to
display the “unicode” representation of an
object.
Was __str__()
Add __unicode__ to models

from django.db import models

class Publisher(models.Model):
name = models.CharField(max_length=30)
address = models.CharField(max_length=50)
....

def __unicode__(self):
return self.name
Add __unicode__ to models

class Author(models.Model):
first_name =
models.CharField(max_length=30)
last_name =
models.CharField(max_length=40)
email = models.EmailField()
def __unicode__(self):
return u'%s %s' % (self.first_name,
self.last_name)
Add __unicode__ to models

class Book(models.Model):
title = models.CharField(max_length=100)
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher)
publication_date = models.DateField()

def __unicode__(self):
return self.title
__unicode__() effect

Was:
>>> publisher_list
[<Publisher: Publisher object>, <Publisher:
Publisher object>]
Now:
>>> from books.models import Publisher
>>> publisher_list = Publisher.objects.all()
>>> publisher_list
[<Publisher: Apress>, <Publisher: O'Reilly>]
Advanced: lambda, anonymous
functions
Publisher.__unicode__ = lambda self: self.name
Author.__unicode__ = lambda self: u'%s %s' %
(self.first_name, self.last_name)
Book.__unicode__ = lambda self: self.title
You could just define a Named function and
bind it in the django shell without use lambda.
>>> def bookunicode(self):
>>> return self.title
>>> Book.__unicode__ = bookunicode
Create More Objects (INSERT)

>>> p = Publisher(name='Apress',
... address='2855 Telegraph Ave.',
... city='Berkeley',
... state_province='CA',
... country='U.S.A.',
... website='http://www.apress.com/')
>>> p.save()
SQL

INSERT INTO books_publisher


(name, address, city, state_province, country,
website)
VALUES
('Apress', '2855 Telegraph Ave.', 'Berkeley',
'CA',
'U.S.A.', 'http://www.apress.com/');
How can I see the raw SQL
queries Django is running?
>>> from django.db import connection
>>> connection.queries
DEBUG=True (settings.py)
``sql`` -- The raw SQL statement
``time`` -- How long the statement took to
execute, in seconds.
EDIT Objects (UPDATE)

>>> p.id
52
>>> p.name = 'Apress Publishing'
>>> p.save()
SQL

UPDATE books_publisher SET


name = 'Apress Publishing',
address = '2855 Telegraph Ave.',
city = 'Berkeley',
state_province = 'CA',
country = 'U.S.A.',
website = 'http://www.apress.com'
WHERE id = 52;
Selecting Objects

>>> Publisher.objects.all()
[<Publisher: Apress>, <Publisher: O'Reilly>]
SQL

SELECT id, name, address, city, state_province,


country, website
FROM books_publisher;
Manager

Publisher.objects.all()

Manager
Advanced: Manager

All models automatically get a objects manager;


you’ll use it any time you want to look up
model instances.
a model’s manager is an object through which
Django models perform database queries.
Each Django model has at least one manager,
and you can create custom managers in order
to customize database access.
Custom Manager: Write commonly
executed queries in DSLs.
DSL: Domain Specific Language.
You DON'T really NEED it.
Use it when you feel comfortable.
Add a class extended
django.db.models.Manager.
Write a custom function.
Replace default manager 'objects'.
Benefit: Prevent Duplicate Code.
Defects: Create Difficult Code. (possibly)
Advanced: Book Manager
class BookManager(models.Manager):
def title_count(self, keyword):
return self.filter(title__icontains=keyword).count()
class Book(models.Model):
title = models.CharField(max_length=100)
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher)
publication_date = models.DateField()
num_pages = models.IntegerField(blank=True,
null=True)
objects = BookManager()
Examples:

>>> Book.objects.title_count('django')
4
>>> Book.objects.title_count('python')
18
How to manually use SQL in
models?
Standard Python DBAPI
>>> from django.db import connection
>>> cursor = connection.cursor()
>>> cursor.execute("""
... SELECT DISTINCT first_name
... FROM people_person
... WHERE last_name = %s""", ['Lennon'])
>>> row = cursor.fetchone()
>>> print row
['John']
Advanced Manager Usage:
Custom SQL in Models
from django.db import connection, models

class PersonManager(models.Manager):
def first_names(self, last_name):
cursor = connection.cursor()
cursor.execute("""
SELECT DISTINCT first_name
FROM people_person
WHERE last_name = %s""", [last_name])
return [row[0] for row in cursor.fetchone()]
Custom SQL define in models

class Person(models.Model):
first_name =
models.CharField(max_length=50)
last_name =
models.CharField(max_length=50)
objects = PersonManager()
SQL? Didn't see it now.

>>> Person.objects.first_names('Lennon')
['John', 'Cynthia']
Filtering Data

>>> Publisher.objects.filter(name='Apress')
[<Publisher: Apress>]
SQL

SELECT id, name, address, city, state_province,


country, website
FROM books_publisher
WHERE name = 'Apress';
Filtering Data (more)

>>> Publisher.objects.filter(country="U.S.A.",
state_province="CA")
[<Publisher: Apress>]
SQL

SELECT id, name, address, city, state_province,


country, website
FROM books_publisher
WHERE country = 'U.S.A.'
AND state_province = 'CA';
XXX__contains

>>>
Publisher.objects.filter(name__contains="pres
s")
[<Publisher: Apress>]
SQL

SELECT id, name, address, city, state_province,


country, website
FROM books_publisher
WHERE name LIKE '%press%';
Lookup Examples:

>>> Entry.objects.filter(headline__istartswith='will')
>>> Entry.objects.filter(headline__endswith='cats')
>>> Entry.objects.filter(headline__iendswith='cats')
>>> start_date = datetime.date(2005, 1, 1)
>>> end_date = datetime.date(2005, 3, 31)
>>>
Entry.objects.filter(pub_date__range=(start_date,
end_date))
More Field Lookups

_ _ ==> MAGICS in python


__contains
__icontains (case insensitive contains)
__startswith
__endswith
__range (SQL between)
....
http://www.djangobook.com/en/1.0/appendixC/
Retrieving Single Objects
filter() returned a QuerySet
>>> Publisher.objects.filter(country="U.S.A.",
state_province="CA")
[<Publisher: Apress>]
>>> Publisher.objects.get(name="Apress")
<Publisher: Apress>
>>> Publisher.objects.get(country="U.S.A.")
Traceback (most recent call last):
...
MultipleObjectsReturned: get() returned more than one
Publisher --
it returned 2! Lookup parameters were {'country': 'U.S.A.'}
DoesNotExist exception

>>> Publisher.objects.get(name="Penguin")
Traceback (most recent call last):
...
DoesNotExist: Publisher matching query does
not exist.
Handle Exception

try:
p = Publisher.objects.get(name='Apress')
except Publisher.DoesNotExist:
print "Apress isn't in the database yet."
else:
print "Apress is in the database."
Ordering Data

>>> Publisher.objects.order_by("name")
[<Publisher: Apress>, <Publisher: O'Reilly>]
>>> Publisher.objects.order_by("address")
[<Publisher: O'Reilly>, <Publisher: Apress>]

>>>
Publisher.objects.order_by("state_province")
[<Publisher: Apress>, <Publisher: O'Reilly>]
Ordering Data

order by multiple fields


>>>
Publisher.objects.order_by("state_province",
"address")
[<Publisher: Apress>, <Publisher: O'Reilly>]
reverse ordering
>>> Publisher.objects.order_by("-name")
[<Publisher: O'Reilly>, <Publisher: Apress>]
Default ordering in model

class Publisher(models.Model):
name = models.CharField(max_length=30)
address = models.CharField(max_length=50)
city = models.CharField(max_length=60)
state_province =
models.CharField(max_length=30)
country = models.CharField(max_length=50)
website = models.URLField()
class Meta:
ordering = ['name']
Chaining Lookups

>>>
Publisher.objects.filter(country="U.S.A.").ord
er_by("-name")
[<Publisher: O'Reilly>, <Publisher: Apress>]
SQL

SELECT id, name, address, city, state_province,


country, website
FROM books_publisher
WHERE country = 'U.S.A'
ORDER BY name DESC;
Slicing Data

>>> Publisher.objects.order_by('name')[0]
<Publisher: Apress>

SQL:
SELECT id, name, address, city, state_province,
country, website
FROM books_publisher
ORDER BY name
LIMIT 1;
Slicing Data

>>> Publisher.objects.order_by('name')[0:2]

SELECT id, name, address, city, state_province,


country, website
FROM books_publisher
ORDER BY name
OFFSET 0 LIMIT 2;
Django Slice: No Negative
indexing
>>> Publisher.objects.order_by('name')[-1]
Traceback (most recent call last):
...
AssertionError: Negative indexing is not
supported.

Publisher.objects.order_by('-name')[0]
Tips: SQL optimize Tricks:
update()
update()
Update Objects using save()

>>> p = Publisher.objects.get(name='Apress')
>>> p.name = 'Apress Publishing'
>>> p.save()
Problem: updates all columns in a row.
SQL
SELECT id, name, address, city, state_province, country,
website
FROM books_publisher
WHERE name = 'Apress';
UPDATE books_publisher SET
name = 'Apress Publishing',
address = '2855 Telegraph Ave.',
city = 'Berkeley',
state_province = 'CA',
country = 'U.S.A.',
website = 'http://www.apress.com'
WHERE id = 52;
Update Objects Idioms

update()

>>>
Publisher.objects.filter(id=52).update(name='
Apress Publishing')
SQL

UPDATE books_publisher
SET name = 'Apress Publishing'
WHERE id = 52;
Update QuerySet

update() method works on any QuerySet


>>>
Publisher.objects.all().update(country='USA')
2
2 -- an integer representing how many records
changed
Deleting Objects

>>> p = Publisher.objects.get(name="O'Reilly")
>>> p.delete()
>>> Publisher.objects.all()
[<Publisher: Apress Publishing>]
>>>
Publisher.objects.filter(country='USA').delete()
>>> Publisher.objects.all().delete()
>>> Publisher.objects.all()
[]
Deleting Objects
Delete All Objects:
>>> Publisher.objects.all().delete()
Delete Subset Objects:
>>>
Publisher.objects.filter(country='USA').delete()
Remember use all()
>>> Publisher.objects.delete()
Traceback (most recent call last):
File "<console>", line 1, in <module>
AttributeError: 'Manager' object has no attribute
'delete'
Dynamic Web Site

Template
Remember Helloworld?

from django.http import HttpResponse

def hello(request):
return HttpResponse("Hello world")
What do you need for a django
program?
You Just need these things in Django's view:
Define a function
IN: Request
OUT: Response
No template Need.
No models (database) Need.
Just view and urlconf are enough for web
scripts.
views.py

from django.http import HttpResponse


import datetime

def current_datetime(request):
now = datetime.datetime.now()
html = "<html><body>It is now
%s.</body></html>" % now
return HttpResponse(html)
What's Wrong?

HTML is mixed in python code – UGLY.


Any change to the design of the page requires a
change to the Python code. -- The design of a
site tends to change far more frequently.
Writing Python code and designing HTML are
two different disciplines -- Designers and
HTML/CSS coders shouldn’t be required to
edit Python code to get their job done.
Django Template come to saves!

programmers can work on Python code and


designers can work on templates at the same
time.
Django’s template language is designed to
strike a balance between power and ease.
It’s designed to feel comfortable to those used
to working with HTML.
Templates

A template is simply a text file. It can generate


any text-based format (HTML, XML, CSV,
etc.).
A template contains variables, which get
replaced with values when the template is
evaluated, and tags, which control the logic of
the template.
TEMPLATE_DIRS
a minimal template
{% extends "base_generic.html" %}
{% block title %}{{ section.title }}{% endblock %}
{% block content %}
<h1>{{ section.title }}</h1>
{% for story in story_list %}
<h2> <a href="{{ story.get_absolute_url }}">
{{ story.headline|upper }}
</a> </h2>
<p>{{ story.tease|truncatewords:"100" }}</p>
{% endfor %}
{% endblock %}
Variables

{{ variable }}
Use a dot (.) to access attributes of a variable.
* Dictionary lookup
* Attribute lookup
* Method call
* List-index lookup
http://docs.djangoproject.com/en/dev/topics/templates/#using-the-built-in-reference
Example:
{{ section.title }}
Variables

{% extends "base_generic.html" %}
{% block title %}{{ section.title }}{% endblock %}
{% block content %}
<h1>{{ section.title }}</h1>
{% for story in story_list %}
<h2> <a href="{{ story.get_absolute_url }}">
{{ story.headline|upper }}
</a> </h2>
<p>{{ story.tease|truncatewords:"100" }}</p>
{% endfor %}
{% endblock %}
Filters

modify variables for display by using filters.


{{ name|lower }}
This displays the value of the {{ name }} variable
after being filtered through the lower filter,
which converts text to lowercase.
{{ text|escape|linebreaks }} is a common idiom
for escaping text contents, then converting
line breaks to <p> tags.
http://docs.djangoproject.com/en/dev/ref/templates/builtins/#ref-templates-builtins-filters
default filter

If a variable is false or empty, use given default.


Otherwise, use the value of the variable

{{ value|default:"nothing" }}

If value isn't provided or is empty, the above will


display "nothing".
length filter

Returns the length of the value. This works for


both strings and lists;

{{ value|length }}

If value is ['a', 'b', 'c', 'd'], the output will be 4.


striptags filter

Strips all [X]HTML tags

{{ value|striptags }}
If value is "<b>Joel</b> <button>is</button> a
<span>slug</span>", the output will be "Joel is
a slug".
escape filter

Escapes a string's HTML. Specifically, it makes


these replacements:

< is converted to &lt;


> is converted to &gt;
' (single quote) is converted to &#39;
" (double quote) is converted to &quot;
& is converted to &amp;
Tags

Tags look like this:


{% tag %}
more complex than variables
control flow
load external information
create text in the output
....
for

<ul>
{% for athlete in athlete_list %}
<li>{{ athlete.name }}</li>
{% endfor %}
</ul>
if else

{% if athlete_list %}
Number of athletes: {{ athlete_list|length }}
{% else %}
No athletes.
{% endif %}
ifequal and ifnotequal

{% ifequal athlete.name coach.name %}


...
{% endifequal %}
{% ifnotequal athlete.name "Joe" %}
...
{% endifnotequal %}
Comments

comment syntax: {# #}
{# greeting #}hello
Only render 'hello'
A comment can contain any template code
{# {% if foo %}bar{% else %} #}
only be used for single-line comments
Multiple lines:
{% comment %} and {% endcomment %}
Template inheritance

block and extends -- tags


For extra readability, you can optionally give a
name to your {% endblock %} tag. For
example:

{% block content %}
...
{% endblock content %}
Base.html
<head><link rel="stylesheet" href="style.css" />
<title>{% block title %}My amazing site{% endblock %}</title></head>
<body>
<div id="sidebar">
{% block sidebar %}
<ul>
<li><a href="/">Home</a></li>
<li><a href="/blog/">Blog</a></li>
</ul>
{% endblock %}
</div>
<div id="content">
{% block content %}{% endblock %}
</div>
</body>
Child.html

{% extends "base.html" %}

{% block title %}My amazing blog{% endblock %}


{% block content %}
{% for entry in blog_entries %}
<h2>{{ entry.title }}</h2>
<p>{{ entry.body }}</p>
{% endfor %}
{% endblock %}
Final Result
<head> <link rel="stylesheet" href="style.css" />
<title>My amazing blog</title></head>
<body><div id="sidebar">
<ul>
<li><a href="/">Home</a></li>
<li><a href="/blog/">Blog</a></li></ul></div>
<div id="content">
<h2>Entry one</h2>
<p>This is my first entry.</p>
<h2>Entry two</h2>
<p>This is my second entry.</p>
</div>
</body>
tips for template inheritance

the child template didn't define the sidebar


block, the value from the parent template is
used instead.
Content within a {% block %} tag in a parent
template is always used as a fallback.
If you use {% extends %} in a template, it must
be the first template tag in that template.
Template inheritance won't work, otherwise.
tips for template inheritance
More {% block %} tags in your base templates are
better. Remember, child templates don't have to
define all parent blocks, so you can fill in
reasonable defaults in a number of blocks, then
only define the ones you need later. It's better to
have more hooks than fewer hooks.
you can't define multiple {% block %} tags with the
same name in the same template.
tips for template inheritance

{{ block.super }} variable can get the content of


the block from the parent template, This is
useful if you want to modify/add to the
contents of a parent block instead of
completely overriding it.
example:
{% block menu %}
{{ block.super }}
<li>another stuff</li>
{% endblock menu %}
Custom tag and filter libraries

To access custom tags and filters in a template,


use the {% load %} tag
Example:
{% load comments %}

{% comment_form for blogs.entries entry.id


with is_public yes %}

{% load comments i18n %}


Forms

interactive Web sites


Google’s search box
access user-submitted form data, validate it and
do something with it.
HttpRequest and Form objects.
Hello World Again

from django.http import HttpResponse

def hello(request):
return HttpResponse("Hello world")
HttpRequest

Have Information About Submitted Data


request.GET and request.POST
POST data generally is submitted from an
HTML <form>
GET data can come from a <form> or the query
string in the page’s URL.
Django 動手作 單元 6

views.py
from django.shortcuts import
render_to_response
def search_form(request):
return
render_to_response('search_form.html')
HTML
search_form.html
<html>
<head>
<title>Search</title>
</head>
<body>
<form action="/search/" method="get">
<input type="text" name="q">
<input type="submit" value="Search">
</form></body>
</html>
urls.py

from mysite.books import views

urlpatterns = patterns('',
# ...
(r'^search-form/$', 'books.views.search_form'),
# ...
)
Add Search function

# urls.py

urlpatterns = patterns('',
# ...
(r'^search-form/$', 'books.views.search_form'),
(r'^search/$', 'books.views.search'),
# ...
)
views.py (1 -- Easy)

# views.py

def search(request):
if 'q' in request.GET:
message = 'You searched for: %r' %
request.GET['q']
else:
message = 'You submitted an empty form.'
return HttpResponse(message)
views.py (2-1 Use DataBase)

from django.http import HttpResponse


from django.shortcuts import
render_to_response
from mysite.books.models import Book
views.py (2-2 Use DataBase)

def search(request):
if 'q' in request.GET and request.GET['q']:
q = request.GET['q']
books = Book.objects.filter(title__icontains=q)
return render_to_response('search_results.html',
{'books': books, 'query': q})
else:
return HttpResponse('Please submit a search
term.')
search_results.html
<p>You searched for: <strong>{{ query }}</strong></p>

{% if books %}
<p>Found {{ books|length }} book{{ books|pluralize }}.</p>
<ul>
{% for book in books %}
<li>{{ book.title }}</li>
{% endfor %}
</ul>
{% else %}
<p>No books matched your search criteria.</p>
{% endif %}
Thanks

To Be Continued!
辛苦了 ! 請繼續參加 Django 入門簡介 ( 三 )

You might also like