Вы находитесь на странице: 1из 190

Web framework for Python

Django Book: pdf version

compiled by Suvash Sedhain

bir2su.blogspot.com

Visit www.djangobook.com for online version of the book


The Django Book

The Dj ango Book

Table of cont ent s


Bet a, English

Chapt er 1: I nt roduct ion t o Dj ango Oct ober 30, 2006

Chapt er 2: Get t ing st art ed Oct ober 30, 2006

Chapt er 3: The basics of generat ing Web pages Novem ber 6, 2006

Chapt er 4: The Dj ango t em plat e syst em Novem ber 7, 2006

Chapt er 5: I nt eract ing wit h a dat abase: m odels Novem ber 13, 2006

Chapt er 6: The Dj ango adm in sit e Novem ber 13, 2006

Chapt er 7: Form processing TBA

Chapt er 8: Advanced views and URLconfs Decem ber 11, 2006

Chapt er 9: Generic views Novem ber 20, 2006

Chapt er 10: Ext ending t he t em plat e engine Decem ber 4, 2006

Chapt er 11: Out put t ing non- HTML cont ent Decem ber 11, 2006

Chapt er 12: Sessions, users, and regist rat ion Decem ber 24, 2006

Chapt er 13: Com m ent s TBA

Chapt er 14: Caching Novem ber 20, 2006

Chapt er 15: Ot her cont ribut ed sub- fram eworks Decem ber 18, 2006

Chapt er 16: Middleware Decem ber 25, 2006

Chapt er 17: I nt egrat ing wit h legacy dat abases and applicat ions Decem ber 25, 2006

Chapt er 18: Cust om izing t he Dj ango adm in January 3, 2007

file:///D|/books/computer/programming/python/books/DJANGO BOOK/TOC.HTML (1 of 2)9/28/2007 2:33:44 PM


The Django Book

Chapt er 19: I nt ernat ionalizat ion and localizat ion January 8, 2007

Chapt er 20: Securit y January 8, 2007

Chapt er 21: Deploying Dj ango January 24, 2007

Appendix A: Case st udies TBA

Appendix B: Model definit ion reference TBA

Appendix C: Dat abase API reference TBA

Appendix D: URL dispat ch reference TBA

Appendix E: Set t ings reference TBA

Appendix F: Built - in t em plat e t ag/ filt er reference TBA

Appendix G: The dj ango- adm in ut ilit y TBA

Appendix H: Request and response obj ect reference TBA

Appendix I : Regular expression reference TBA

Copyright 2006 Adrian Holovat y and Jacob Kaplan- Moss.


This work is licensed under t he GNU Free Docum ent License.

file:///D|/books/computer/programming/python/books/DJANGO BOOK/TOC.HTML (2 of 2)9/28/2007 2:33:44 PM


Chapter 1: Introduction to Django

The Dj ango Book


t able of cont ent s ◊ next »

Chapt er 1: Int roduct ion t o Dj ango


I f you go t o t he Web sit e dj angoproj ect .com using your Web browser — or, depending on t he decade in which you’re reading t his
dest ined- t o- be- t im eless lit erary work, using your cell phone, elect ronic not ebook, shoe, or any I nt ernet - superceding cont rapt ion
— you’ll find t his explanat ion:

“ Dj ango is a high-level Pyt hon Web f ramework t hat encourages rapid development and clean, pragmat ic design. ”

That ’s a m out hful — or eyeful or pixelful, depending on whet her t his book is being recit ed, read on paper or proj ect ed t o you on a
Jum bot ron, respect ively.

Let ’s break it down.

Dj ango is a high-level Pyt hon Web f ramework…

A high- level Web fram ework is soft ware t hat eases t he pain of building dynam ic Web sit es. I t abst ract s com m on problem s of Web
developm ent and provides short cut s for frequent program m ing t asks.

For clarit y, a dynam ic Web sit e is one in which pages aren’t sim ply HTML docum ent s sit t ing on a server’s filesyst em som ewhere.
I n a dynam ic Web sit e, rat her, each page is generat ed by a com put er program — a so- called “ Web applicat ion” — t hat you, t he
Web developer, creat e. A Web applicat ion m ay, for inst ance, ret rieve records from a dat abase or t ake som e act ion based on user
input .

A good Web fram ework addresses t hese com m on concerns:

● I t pr ovide s a m e t h od of m a ppin g r e qu e st e d URLs t o code t h a t h a n dle s r e qu e st s. I n ot her words, it gives you a way
of designat ing which code should execut e for which URL. For inst ance, you could t ell t he fram ework, “ For URLs t hat look
like /users/joe/, execut e code t hat displays t he profile for t he user wit h t hat usernam e.”
● I t m a k e s it e a sy t o displa y, va lida t e a n d r e displa y H TM L for m s. HTML form s are t he prim ary way of get t ing input dat a
from Web users, so a Web fram ework had bet t er m ake it easy t o display t hem and handle t he t edious code of form display
and redisplay ( wit h errors highlight ed) .
● I t con ve r t s u se r - su bm it t e d in pu t in t o da t a st r u ct u r e s t h a t ca n be m a n ipu la t e d con ve n ie n t ly. For exam ple, t he
fram ework could convert HTML form subm issions int o nat ive dat a t ypes of t he program m ing language you’re using.
● I t h e lps se pa r a t e con t e n t fr om pr e se n t a t ion via a t e m pla t e syst e m , so you can change your sit e’s look- and- feel
wit hout affect ing your cont ent , and vice- versa.
● I t con ve n ie n t ly in t e gr a t e s w it h st or a ge la ye r s — such as dat abases — but doesn’t st rict ly require t he use of a
dat abase.
● I t le t s you w or k m or e pr odu ct ive ly, a t a h igh e r le ve l of a bst r a ct ion , t han if you were coding against , say, HTTP. But it
doesn’t rest rict you from going “ down” one level of abst ract ion when needed.
● I t ge t s ou t of you r w a y, neglect ing t o leave dirt y st ains on your applicat ion such as URLs t hat cont ain “ .aspx” or “ .php” .

Dj ango does all of t hese t hings well — and int roduces a num ber of feat ures t hat raise t he bar for what a Web fram ework should
do.

The fram ework is writ t en in Pyt hon, a beaut iful, concise, powerful, high- level program m ing language. To develop a sit e using
Dj ango, you writ e Pyt hon code t hat uses t he Dj ango libraries. Alt hough t his book doesn’t include a full Pyt hon t ut orial, it
highlight s Pyt hon feat ures and funct ionalit y where appropriat e, part icularly when code doesn’t im m ediat ely m ake sense.

…t hat encourages rapid development …

Regardless of how m any powerful feat ures it has, a Web fram ework is wort hless if it doesn’t save you t im e. Dj ango’s philosophy
is t o do all it can t o facilit at e hyper- fast developm ent . Wit h Dj ango, you build Web sit es in a m at t er of hours, not days; weeks,
not years.

This is possible largely t hanks t o Pyt hon it self. Oh, Pyt hon, how we love t hee, let us count t he bullet point s:

● Pyt hon is an in t e r pr e t e d la n gu a ge , which m eans t here’s no need t o com pile code. Just writ e your program and execut e it .
I n Web developm ent , t his m eans you can develop code and im m ediat ely see result s by hit t ing “ reload” in your Web browser.
● Pyt hon is dyn a m ica lly t ype d, which m eans you don’t have t o worry about declaring dat a t ypes for your variables.
● Pyt hon synt ax is con cise ye t e x pr e ssive , which m eans it t akes less code t o accom plish t he sam e t ask t han in ot her, m ore
verbose, languages such as Java. One line of pyt hon usually equals 10 lines of Java. ( This has a convenient side benefit :
Fewer lines of code m eans fewer bugs.)
● Pyt hon offers pow e r fu l in t r ospe ct ion a n d m e t a - pr ogr a m m in g feat ures, which m ake it possible t o inspect and add

file:///C|/Documents and Settings/Suren/Desktop/Chapter 1 Introduction to Django.htm (1 of 4)9/28/2007 4:13:54 PM


Chapter 1: Introduction to Django

behavior t o obj ect s at runt im e.

Beyond t he product ivit y advant ages inherent in Pyt hon, Dj ango it self m akes every effort t o encourage rapid developm ent . Every
part of t he fram ework was designed wit h product ivit y in m ind. We’ll see exam ples t hroughout t his book.

…and clean, pragmat ic design

Finally, Dj ango st rict ly m aint ains a clean design t hroughout it s own code and m akes it easy t o follow best Web- developm ent
pract ices in t he applicat ions you creat e.

That m eans, if you t hink of Dj ango as a car, it would be an elegant sport s car, capable not only of high speeds and sharp t urns,
but delivering excellent m ileage and clean em issions.

The philosophy here is: Dj ango m akes it easy t o do t hings t he “ right ” way.

Specifically, Dj ango encourages loose coupling: t he program m ing philosophy t hat different pieces of t he applicat ion should be
int erchangeable and should com m unicat e wit h each ot her via clear, concise API s.

For exam ple, t he t em plat e syst em knows not hing about t he dat abase- access syst em , which knows not hing about t he HTTP
request / response layer, which knows not hing about caching. Each one of t hese layers is dist inct and loosely coupled t o t he rest .
I n pract ice, t his m eans you can m ix and m at ch t he layers if need be.

Dj ango follows t he “ m odel- view- cont roller” ( MVC) archit ect ure. Sim ply put , t his is a way of developing soft ware so t hat t he code
for defining and accessing dat a ( t he m odel) is separat e from t he business logic ( t he cont roller) , which in t urn is separat e from t he
user int erface ( t he view) .

MVC is best explained by an exam ple of what not t o do. For inst ance, look at t he following PHP code, which ret rieves a list of
people from a MySQL dat abase and out put s t he list in a sim ple HTML page. ( Yes, we realize it ’s possible for disciplined
program m ers t o writ e clean PHP code; we’re sim ply using PHP t o illust rat e a point .) :

<html>
<head><title>Friends of mine</title></head>
<body>
<h1>Friends of mine</h1>
<ul>
<?php
$connection = @mysql_connect("localhost", "my_username", "my_pass");
mysql_select_db("my_database");
$people = mysql_query("SELECT name, age FROM friends");
while ( $person = mysql_fetch_array($people, MYSQL_ASSOC) ) {
?>
<li>
<?php echo $person['name'] ?> is <?php echo $person['age'] ?> years old.
</li>
<?php } ?>

</ul>

</body>
</html>

While t his code is concept ually sim ple for beginners — because everyt hing is in a single file — it ’s bad pract ice for several reasons:

1. Th e pr e se n t a t ion is t ie d t o t h e code . I f a designer want ed t o edit t he HTML of t his page, he or she would have t o edit t his
code, because t he HTML and PHP core are int ert wined.

By cont rast , t he Dj ango/ MVC approach encourages separat ion of code and present at ion, so t hat present at ion is governed by
t em plat es and business logic lives in Pyt hon m odules. Program m ers deal wit h code, and designers deal wit h HTML.

2. Th e da t a ba se code is t ie d t o t h e bu sin e ss logic. This is a problem of redundancy: I f you renam e your dat abase t ables or
colum ns, you’ll have t o rewrit e your SQL.

By cont rast , t he Dj ango/ MVC approach encourages a single, abst ract ed dat a- access layer t hat ’s responsible for all dat a
access. I n Dj ango’s case, t he dat a- access layer knows your dat abase t able and colum n nam es and let s you execut e SQL
queries via Pyt hon inst ead of writ ing SQL m anually. This m eans, if dat abase t able nam es change, you can change it in a
single place — your dat a- m odel definit ion — inst ead of in each SQL st at em ent lit t ered t hroughout your code.

3. Th e URL is cou ple d t o t h e code . I f t his PHP file lives at /foo/index.php, it ’ll be execut ed for all request s t o t hat address.
But what if you want t his sam e code t o execut e for request s t o /bar/ and /baz/? You’d have t o set up som e sort of includes
or rewrit e rules, and t hose get unm anageable quickly.

file:///C|/Documents and Settings/Suren/Desktop/Chapter 1 Introduction to Django.htm (2 of 4)9/28/2007 4:13:54 PM


Chapter 1: Introduction to Django

By cont rast , Dj ango decouples URLs from callback code, so you can change t he URLs for a given piece of code.

4. Th e da t a ba se con n e ct ion pa r a m e t e r s a n d ba ck e n d a r e h a r d- code d. I t ’s m essy t o have t o specify connect ion


inform at ion — t he server, usernam e and password — wit hin t his code, because t hat ’s configurat ion, not program m ing logic.
Also, t his exam ple hard- codes t he fact t hat t he dat abase engine is MySQL.

By cont rast , Dj ango has a single place for st oring configurat ion, and t he dat abase- access layer is abst ract ed so t hat
swit ching dat abase servers ( say, from MySQL t o Post greSQL) is easy.

What Dj ango doesn’ t do

Of course, we want t his book t o be fair and balanced. Wit h t hat in m ind, we should be honest and out line what Dj ango doesn’t do:

● Feed your cat .


● Mind- read your proj ect requirem ent s and im plem ent t hem on a carefully t im ed basis so as t o fool your boss int o t hinking
you’re not really st aying hom e t o wat ch “ The Price is Right .”

On a m ore serious not e, Dj ango does not yet reverse t he effect s of global warm ing.

Why was Dj ango developed?

Dj ango is deeply root ed in t he problem s and solut ions of t he Real World. I t wasn’t creat ed t o be m arket ed and sold t o developers,
nor was it creat ed as an academ ic exercise in som ebody’s spare t im e. I t was built from Day One t o solve daily problem s for an
indust ry- leading Web- developm ent t eam .

I t st art ed in fall 2003, at — wait for it — a sm all- t own newspaper in Lawrence, Kansas.

For one reason or anot her, The Lawrence Journal- World newspaper m anaged t o at t ract a t alent ed bunch of Web designers and
developers in t he early 2000s. The newspaper’s Web operat ion, World Online, quickly t urned int o one of t he m ost innovat ive
newspaper Web operat ions in t he world. I t s t hree m ain sit es, LJWorld.com ( news) , Lawrence.com ( ent ert ainm ent / m usic) and
KUsport s.com ( college sport s) , began winning award aft er award in t he online- j ournalism indust ry. I t s innovat ions were m any,
including:

● The m ost in- dept h local ent ert ainm ent sit e in t he world, Lawrence.com , which m erges dat abases of local event s, bands,
rest aurant s, drink specials, downloadable songs and t radit ional- form at news st ories.
● A sum m er sect ion of LJWorld.com t hat t reat ed local Lit t le League players like t hey were t he New York Yankees — giving each
t eam and league it s own page, hooking int o weat her dat a t o display forecast s for gam es, providing 360- degree panoram as
of every playing field in t he vicinit y and alert ing parent s via cell- phone t ext m essages when gam es were cancelled.
● Cell- phone gam e alert s for Universit y of Kansas basket ball and foot ball gam es, which let fans get not ified of scores and key
st at s during gam es, and a second syst em t hat used art ificial- int elligence algorit hm s t o let fans send plain- English t ext
m essages t o t he syst em t o query t he dat abase ( “ how m any point s does giddens have” or “ pt s giddens” ) .
● A deep dat abase of all t he college foot ball and basket ball st at s you’d ever want , including a way t o com pare any t wo or m ore
players or t eam s in t he NCAA.
● Giving out blogs t o com m unit y m em bers and feat uring com m unit y writ ing prom inent ly — back before blogs were t rendy.

Journalism pundit s worldwide point ed t o World Online as an exam ple of t he fut ure of j ournalism . The New York Tim es did a front -
page business- sect ion st ory on t he com pany; Nat ional Public Radio did a t wo- day series on it . World Online’s head edit or, Rob
Curley, spoke nearly weekly at j ournalism conferences across t he globe, showcasing World Online’s innovat ive ideas and sit e
feat ures. I n a bleak, old- fashioned indust ry resist ant t o change, World Online was a rare except ion.

Much of World Online’s success was due t o t he t echnology behind it s sit es, and t he philosophy t hat com put er program m ers are
j ust as im port ant in creat ing qualit y 21st Cent ury j ournalism as are j ournalist s t hem selves.

This is why Dj ango was developed: World Online’s developers needed a fram ework for developing com plex dat abase- driven Web
sit es painlessly, easily and on j ournalism deadlines.

I n fall 2003, World Online’s t wo developers, Adrian Holovat y and Sim on Willison, set about creat ing t his fram ework. They decided
t o use Pyt hon, a language wit h which t hey’d recent ly fallen in love. Aft er exploring ( and being disappoint ed by) t he available
Pyt hon Web- program m ing libraries, t hey began creat ing Dj ango.

Two years lat er, in sum m er 2005, aft er having developed Dj ango t o a point where it was efficient ly powering m ost of World
Online’s sit es, t he World Online t eam , which now included Jacob Kaplan- Moss, decided it ’d be a good idea t o open- source t he
fram ework. That way, t hey could give back t o t he open- source com m unit y, get free im provem ent s from out side developers, and
generat e som e buzz for t heir com m ercial Dj ango- powered cont ent - m anagem ent syst em , Ellingt on ( ht t p: / / www.ellingt oncm s.
com / ) . Dj ango was open- sourced in July 2005 and quickly becam e popular.

Alt hough Dj ango is now an open- source proj ect wit h cont ribut ors across t he planet , t he original World Online developers st ill
provide cent ral guidance for t he fram ework’s growt h, and World Online cont ribut es ot her im port ant aspect s such as em ployee
t im e, m arket ing m at erials and host ing/ bandwidt h for t he fram ework’s Web sit e ( ht t p: / / www.dj angoproj ect .com / ) .

file:///C|/Documents and Settings/Suren/Desktop/Chapter 1 Introduction to Django.htm (3 of 4)9/28/2007 4:13:54 PM


Chapter 1: Introduction to Django

Who uses Dj ango?

Web developers around t he world use Dj ango. Som e specific exam ples:

● World Online, of course, cont inues t o use Dj ango for all it s Web sit es, bot h int ernal and for com m ercial client s. Som e of it s
Dj ango- powered sit es are:

❍ ht t p: / / www.lj world.com /
❍ ht t p: / / www.lawrence.com /
❍ ht t p: / / www.6newslawrence.com /
❍ ht t p: / / www.visit lawrence.com /
❍ ht t p: / / www.lawrencecham ber.com /
❍ ht t p: / / www2.kusport s.com / st at s/

● The Washingt on Post ’s Web sit e, washingt onpost .com , uses Dj ango for dat abase proj ect s and various bit s of funct ionalit y
across t he sit e. Som e exam ples:

❍ The U.S. Congress vot es dat abase, ht t p: / / proj ect s.washingt onpost .com / congress/
❍ The st aff direct ory and funct ionalit y t hat let s readers cont act report ers, appearing as links on m ost art icle pages.
❍ Faces of t he Fallen, ht t p: / / proj ect s.washingt onpost .com / fallen/

● Chicagocrim e.org, a freely browsable dat abase of crim e report ed in Chicago and one of t he original Google Maps m ashups,
was developed in Dj ango.

● Tabblo.com , an innovat ive phot o- sharing sit e, uses Dj ango. The sit e let s you piece t oget her your phot os t o creat e phot o
pages t hat t ell st ories.

● Texasgigs.com , a local m usic sit e in Dallas, Texas, was writ t en wit h Dj ango.

● Grono.net , a Polish social- net working sit e, st art ed replacing it s Java code wit h Dj ango. I t found t hat Dj ango not only was
fast er ( and m ore fun) t o develop in — it perform ed bet t er t han Java and required less hardware.

● Traincheck.com was developed in Dj ango. The sit e let s you send t ext - m essages from your cell phone t o get subway t rain
schedules for your im m ediat e locat ion.

An up- t o- dat e list of dozens of sit es t hat use Dj ango is locat ed at ht t p: / / code.dj angoproj ect .com / wiki/ Dj angoPoweredSit es

About t his book

The goal of t his book is t o explain all t he t hings Dj ango does — and t o m ake you an expert at using it .

By reading t his book, you’ll learn t he skills needed t o develop powerful Web sit es quickly, wit h code t hat ’s clean and easy t o
m aint ain.

We’re glad you’re here!

t able of cont ent s ◊ next »


Copyright 2006 Adrian Holovat y and Jacob Kaplan- Moss.
This work is licensed under t he GNU Free Docum ent License.

file:///C|/Documents and Settings/Suren/Desktop/Chapter 1 Introduction to Django.htm (4 of 4)9/28/2007 4:13:54 PM


Chapter 2: Getting started

The Dj ango Book


« previous ◊ t able of cont ent s ◊ next »

Chapt er 2: Get t ing st art ed


Let ’s get st art ed, shall we?

Fort unat ely, inst alling Dj ango is easy. Because Dj ango runs anywhere Pyt hon does, Dj ango can be configured in m any ways.
We’ve t ried t o cover t he com m on scenarios for Dj ango inst allat ions in t his chapt er.

Inst alling Pyt hon

Dj ango is writ t en in 100% pure Pyt hon code, so you’ll need t o inst all Pyt hon on your syst em . Dj ango requires Pyt hon 2.3 or
higher.

I f you’re on Linux or Mac OS X, you probably already have Pyt hon inst alled. Type python at a com m and prom pt ( or in Term inal,
in OS X) . I f you see som et hing like t his, t hen Pyt hon is inst alled:

Python 2.4.1 (#2, Mar 31 2005, 00:05:10)


[GCC 3.3 20030304 (Apple Computer, Inc. build 1666)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>

Ot herwise, if you see an error such as "command not found", you’ll have t o download and inst all Pyt hon. See ht t p: / / www.pyt hon.
org/ download/ t o get st art ed. The inst allat ion is fast and easy.

Inst alling Dj ango

Installing an official release

Most people will want t o inst all t he lat est official release from ht t p: / / www.dj angoproj ect .com / download/ . Dj ango uses t he
st andard Pyt hon distutils inst allat ion m et hod, which in Linux land looks like:

1. Download t he t arball, which will be nam ed som et hing like Django-1.0.tar.gz.


2. tar xzvf Django-*.tar.gz
3. cd Django-*
4. sudo python setup.py install
I f everyt hing worked, you should be able t o im port t he m odule django from t he Pyt hon int eract ive int erpret er.

>>> import django


>>> django.VERSION
(1, 0, ‘official’)

Th e Pyt h on in t e r a ct ive in t e r pr e t e r :
The Pyt hon int eract ive int erpret er is a com m and- line program t hat let s you writ e a Pyt hon program int eract ively. To
st art it , j ust run t he com m and python at t he com m and line. Throughout t his book, we’ll feat ure exam ple Pyt hon
code t hat ’s print ed as if it ’s being ent ered in t he int eract ive int erpret er. The t riple great er- t han signs ( “ > > > ” )
signify a prom pt .

Installing Dj ango from Subversion

I f you want t o work on t he bleeding edge, or if you want t o cont ribut e code t o Dj ango it self, you should inst all Dj ango from it s
Subversion reposit ory.

Subversion is a free, open- source revision- cont rol syst em sim ilar t o CVS, and t he Dj ango t eam uses it t o m anage changes t o t he
Dj ango codebase. At any given t im e, you can use a Subversion client t o grab t he very lat est Dj ango source code, and, at any
given t im e, you can updat e your local version of t he Dj ango code — known as your “ local checkout ” — t o get t he lat est changes
and im provem ent s m ade by Dj ango developers.

The lat est - and- great est Dj ango developm ent code is referred t o as “ t he t runk.”

file:///C|/Documents and Settings/Suren/Desktop/DJANGO/CH2.html (1 of 4)9/28/2007 2:07:18 PM


Chapter 2: Getting started

To grab t he lat est Dj ango t runk:

1. Make sure you have a Subversion client inst alled. You can get t he soft ware free from ht t p: / / subversion.t igris.org/ and
excellent docum ent at ion from ht t p: / / svnbook.red- bean.com /
2. Check out t he t runk using t he com m and svn co http://code.djangoproject.com/svn/django/trunk django_src
3. Sym link django_src/django so t hat django is wit hin your Pyt hon site-packages direct ory, or updat e your PYTHONPATH t o
point t o it .
When inst alling from Subversion, you don’t need t o run python setup.py install.

Because t he Dj ango t runk changes oft en wit h bug fixes and feat ure addit ions, you’ll probably want t o updat e it every once in a
while — or hourly, if you’re really obsessed. To updat e t he code, j ust run t he com m and svn update from wit hin t he django_src
direct ory. When you run t hat com m and, Subversion will cont act our Web server, see if any code has changed and updat e your
local version of t he code wit h any changes t hat have been m ade since you last updat ed. I t ’s quit e slick.

Set t ing up a dat abase

Dj ango’s only prerequisit e is a working inst allat ion of Pyt hon. However, t his book focuses on one of Dj ango’s sweet spot s, which
is developing dat abase- backed Web sit es — so you’ll need t o inst all a dat abase server of som e sort , for st oring your dat a.

I f you j ust want t o get st art ed playing wit h Dj ango, skip ahead t o St art ing a proj ect , but t rust us — you’ll want t o inst all a
dat abase event ually. All of t he exam ples in t he book assum e you’ve got a dat abase set up.

As of version 1.0, Dj ango support s five dat abase engines:

● Post greSQL ( ht t p: / / www.post gresql.org/ )


● SQLit e 3 ( ht t p: / / www.sqlit e.org/ )
● MySQL ( ht t p: / / www.m ysql.com / )
● Microsoft SQL Server ( ht t p: / / www.m icrosoft .com / sql/ )
● Oracle ( ht t p: / / www.oracle.com / dat abase/ )

We’re quit e fond of Post greSQL ourselves, for reasons out side t he scope of t his book, so we m ent ion it first . However, all t hose
engines will work equally well wit h Dj ango.

SQLit e also deserves special not ice: I t ’s an ext rem ely sim ple in- process dat abase engine t hat doesn’t require any sort of server
set up or configurat ion. I t ’s by far t he easiest t o set up if you j ust want t o play around wit h Dj ango.

Using Dj ango with PostgreSQL

I f you’re using Post greSQL, you’ll need t he psycopg package available from ht t p: / / init d.org/ proj ect s/ psycopg1. Make sure you
use version 1, not version 2 ( which is st ill in bet a) .

I f you’re using Post greSQL on Windows, you can find precom piled binaries of psycopg at ht t p: / / st ickpeople.com / proj ect s/ pyt hon/
win- psycopg/ .

Using Dj ango with SQLite 3

You’ll need SQLit e 3 — not version 2 — and t he pysqlite package from ht t p: / / init d.org/ t racker/ pysqlit e. Make sure you
have pysqlite version 2.0.3 or higher.

Using Dj ango with MySQL

Dj ango requires MySQL 4.0 or above; t he 3.x versions don’t support t ransact ions, nest ed procedures, and som e ot her fairly
st andard SQL st at em ent s. You’ll also need t he MySQLdb package from ht t p: / / sourceforge.net / proj ect s/ m ysql- pyt hon.

Using Dj ango with MSSQL

Using Dj ango with Oracle

Using Dj ango without a database

As m ent ioned above, Dj ango doesn’t act ually require a dat abase. I f you j ust want t o use it t o serve dynam ic pages t hat don’t hit
a dat abase, t hat ’s perfect ly fine.

file:///C|/Documents and Settings/Suren/Desktop/DJANGO/CH2.html (2 of 4)9/28/2007 2:07:18 PM


Chapter 2: Getting started

Wit h t hat said, bear in m ind t hat som e of t he ext ra t ools bundled wit h Dj ango do require a dat abase, so if you choose not t o use
a dat abase, you’ll m iss out on t hose feat ures. ( We’ll highlight t hese feat ures t hroughout t his book.)

St art ing a proj ect

I f t his is your first t im e using Dj ango, you’ll have t o t ake care of som e init ial set up.

Run t he com m and django-admin.py startproject mysite. That ’ll creat e a mysite direct ory in your current direct ory.

N ot e
django-admin.py should be on your syst em pat h if you inst alled Dj ango via it s set up.py ut ilit y. I f it ’s not on your
pat h, you can find it in site-packages/django/bin; consider sym linking t o it from som e place on your pat h, such
as / usr/ local/ bin.

A proj ect is a collect ion of set t ings for an inst ance of Dj ango — including dat abase configurat ion, Dj ango- specific opt ions and
applicat ion- specific set t ings. Let ’s look at what startproject creat ed:

mysite/
__init__.py
manage.py
settings.py
urls.py

These files are:

manage.py
A com m and- line ut ilit y t hat let s you int eract wit h t his Dj ango proj ect in various ways.

settings.py
Set t ings/ configurat ion for t his Dj ango proj ect .

urls.py
The URL declarat ions for t his Dj ango proj ect ; a “ t able of cont ent s” of your Dj ango- powered sit e.

W h e r e sh ou ld t h is code live ?
I f your background is in PHP, you’re probably used t o put t ing code under t he Web server’s docum ent root ( in a
place such as /var/www) . Wit h Dj ango, you don’t do t hat . I t ’s not a good idea t o put any of t his Pyt hon code wit hin
your Web server’s docum ent root , because it risks t he possibilit y t hat people m ay be able t o view your code over
t he Web. That ’s not good for securit y.

Put your code in som e direct ory ou t side of t he docum ent root , such as /home/mycode.

The development server

Change int o t he mysite direct ory, if you haven’t already, and run t he com m and python manage.py runserver. You’ll see
som et hing like t his:

Validating models...
0 errors found.

Django version 1.0, using settings 'mysite.settings'


Development server is running at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

You’ve st art ed t he Dj ango developm ent server, a light weight Web server you can use while developing your sit e. We’ve included
t his wit h Dj ango so you can develop t hings rapidly, wit hout having t o deal wit h configuring your product ion Web server ( e.g.,
Apache) unt il you’re ready for product ion. This developm ent server wat ches your code for changes and aut om at ically reloads,
helping you m ake m any rapid changes t o your proj ect wit hout needing t o rest art anyt hing.

Alt hough t he developm ent server is ext rem ely nice for, well, developm ent , resist t he t em pt at ion t o use t his server in anyt hing
resem bling a product ion environm ent . The developm ent server can only handle a single request at a t im e reliably, and it has not
gone t hrough a securit y audit of any sort . When t he t im e com es t o launch your sit e, see Chapt er XXX for inform at ion on how t o
deploy Dj ango.

Ch a n gin g t h e h ost or t h e por t

file:///C|/Documents and Settings/Suren/Desktop/DJANGO/CH2.html (3 of 4)9/28/2007 2:07:18 PM


Chapter 2: Getting started

By default , t he runserver com m and st art s t he developm ent server on port 8000, list ening only for local
connect ions. I f you want t o change t he server’s port , pass it as a com m and- line argum ent :

python manage.py runserver 8080

You can also change t he I P address t hat t he server list ens on. This is especially helpful if you’d like t o share a
developm ent sit e wit h ot her developers:

python manage.py runserver 0.0.0.0:8080

will m ake Dj ango list en on any net work int erface, t hus allowing ot her com put ers t o connect t o t he developm ent
server.

Now t hat t he server’s running, visit ht t p: / / 127.0.0.1: 8000/ wit h your Web browser. You’ll see a “ Welcom e t o Dj ango” page, in
pleasant , light - blue past el. I t worked!

What ’ s next ?

Now t hat we’ve got everyt hing inst alled and t he developm ent server running, let ’s writ e som e basic code t hat dem onst rat es how
t o serve Web pages using Dj ango.

« previous ◊ t able of cont ent s ◊ next »


Copyright 2006 Adrian Holovat y and Jacob Kaplan- Moss.
This work is licensed under t he GNU Free Docum ent License.

file:///C|/Documents and Settings/Suren/Desktop/DJANGO/CH2.html (4 of 4)9/28/2007 2:07:18 PM


Chapter 3: The basics of generating Web pages

The Dj ango Book


« previous ◊ t able of cont ent s ◊ next »

Chapt er 3: The basics of dynamic Web pages


I n t he previous chapt er, we explained how t o set up a Dj ango proj ect and run t he Dj ango developm ent server. Of course, t hat
sit e doesn’t act ually do anyt hing useful yet — all it does is display t he “ I t worked! ” m essage. Let ’s change t hat .

This chapt er int roduces how t o creat e dynam ic Web pages wit h Dj ango.

Your f irst view: Dynamic cont ent


As our first goal, let ’s creat e a Web page t hat displays t he current dat e and t im e. This is a good exam ple of a dynam ic Web page,
because t he cont ent s of t he page are not st at ic — rat her, t he cont ent s change according t o t he result of a com put at ion ( in t his
case, a calculat ion of t he current t im e) .

This sim ple exam ple doesn’t involve a dat abase or any sort of user input — j ust t he out put of your server’s int ernal clock.

To creat e t his page, we’ll writ e a vie w fu n ct ion . A view funct ion, or vie w for short , is sim ply a Pyt hon funct ion t hat t akes a Web
request and ret urns a Web response. This response can be t he HTML cont ent s of a Web page, or a redirect , or a 404 error, or an
XML docum ent , or an im age…or anyt hing, really. The view it self cont ains what ever arbit rary logic is necessary t o ret urn t hat
response.

Here’s a view t hat ret urns t he current dat e and t im e, as an HTML docum ent :

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)

Let ’s st ep t hrough t his code one line at a t im e:

● First , we im port t he class HttpResponse, which lives in t he django.http m odule.

● Next , we im port t he datetime m odule from Pyt hon’s st andard library — t he set of useful m odules t hat com es wit h Pyt hon.
The datetime m odule cont ains several funct ions and classes for dealing wit h dat es and t im es, including a funct ion t hat
ret urns t he current t im e.

● Next , we define a funct ion called current_datetime. This is t he vie w fu n ct ion , and, as such, it t akes an HttpRequest
obj ect as it s first param et er. Each view funct ion t akes an HttpRequest obj ect as it s first param et er. I n t his case, we call t hat
param et er request.

Not e t hat t he nam e of t he view funct ion doesn’t m at t er; Dj ango doesn’t care what it ’s called, and it doesn’t have t o be
nam ed in a cert ain way in order for Dj ango t o recognize it . We’re calling it current_datetime here, because t hat nam e
clearly indicat es what it does, but it could j ust as well be nam ed super_duper_awesome_current_time, or som et hing equally
revolt ing. Dj ango doesn’t care. ( How does Dj ango find t his funct ion, t hen? We’ll get t o t hat in a m om ent .)

● The first line of code wit hin t he funct ion calculat es t he current dat e/ t im e, as a datetime.datetime obj ect , and st ores t hat as
t he local variable now.

● The second line of code wit hin t he funct ion const ruct s an HTML response using Pyt hon’s form at - st ring capabilit y. The %s
wit hin t he st ring is a placeholder, and t he percent sign aft er t he st ring m eans “ replace t he %s wit h t he value of t he
variable now.”

( A not e t o t he HTML purist s: Yes, we know we’re m issing a DOCTYPE, and a <head>, and all t hat st uff. We’re t rying t o keep
it sim ple.)

● Finally, t he view ret urns an HttpResponse obj ect t hat cont ains t he generat ed HTML. Each view funct ion is responsible for
ret urning an HttpResponse obj ect . ( There are except ions, but we’ll get t o t hose lat er.)

Your f irst URLconf


So, t o recap, t his view funct ion ret urns an HTML page t hat includes t he current dat e and t im e. But where should t his code live,
how do we t ell Dj ango t o use t his code?

The answer t o t he first quest ion is: This code can live anywhere you want , as long as it ’s on your Pyt hon pat h. There’s no ot her

file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH3.html (1 of 9)9/28/2007 2:08:35 PM


Chapter 3: The basics of generating Web pages

requirem ent — no “ m agic,” so t o speak. For t he sake of put t ing it som ewhere, let ’s creat e a file called views.py, copy t his view
code int o t hat file and save it int o t he mysite direct ory you creat ed in t he previous chapt er.

You r Pyt h on pa t h
The Pyt hon pat h is t he list of direct ories on your syst em where Pyt hon looks when you use t he Pyt hon import
st at em ent .

For exam ple, let ’s say your Pyt hon pat h is set t o ['', '/usr/lib/python2.4/site-packages', '/home/mycode'].
I f you execut e t he Pyt hon code from foo import bar, Pyt hon will first check for a m odule called foo.py in t he
current direct ory. ( The first ent ry in t he Pyt hon pat h, an em pt y st ring, m eans “ t he current direct ory.” ) I f t hat file
doesn’t exist , Pyt hon will look for t he file /usr/lib/python2.4/site-packages/foo.py. I f t hat file doesn’t exist , it
will t ry /home/mycode/foo.py. Finally, if t hat file doesn’t exist , it will raise ImportError.

I f you’re int erest ed in seeing t he value of your Pyt hon pat h, st art t he Pyt hon int eract ive int erpret er and
t ype import sys, followed by print sys.path.

Generally you don’t have t o worry about set t ing your Pyt hon pat h — Pyt hon and Dj ango will t ake care of t hings for
you aut om at ically behind t he scenes. ( I f you’re curious, set t ing t he Pyt hon pat h is one of t he t hings t hat
t he manage.py file does.)

How do we t ell Dj ango t o use t his view code? That ’s where URLconfs com e in.

A URLcon f is like a t able of cont ent s for your Dj ango- powered Web sit e. Basically, it ’s a m apping bet ween URL pat t erns and t he
view funct ions t hat should be called for t hose URL pat t erns. I t ’s how you t ell Dj ango “ For t his URL, call t his code, and for t hat
URL, call t hat code.”

When you execut ed django-admin.py startproject in t he previous chapt er, t he script creat ed a URLconf for you aut om at ically:
t he file urls.py. Let ’s edit t hat file. By default , it looks som et hing like t his:

from django.conf.urls.defaults import *

urlpatterns = patterns('',
# Example:
# (r'^mysite/', include('mysite.apps.foo.urls.foo')),

# Uncomment this for admin:


# (r'^admin/', include('django.contrib.admin.urls')),
)

Let ’s st ep t hrough t his code one line at a t im e:

● The first line im port s all obj ect s from t he django.conf.urls.defaults m odule, including a funct ion called patterns.
● The second line calls t he funct ion patterns() and saves t he result int o a variable called urlpatterns. The patterns()
funct ion get s passed only a single argum ent — t he em pt y st ring. The rest of t he lines are com m ent ed out .

The m ain t hing t o see here is t he variable urlpatterns. This defines t he m apping bet ween URLs and t he code t hat handles t hose
URLs.

By default , everyt hing in t he URLconf is com m ent ed out — your Dj ango applicat ion is a blank slat e. ( As a side not e, t hat ’s how
Dj ango knew t o show you t he “ I t worked! ” page in t he last chapt er: I f your URLconf is em pt y, Dj ango assum es you j ust st art ed a
new proj ect and, hence, displays t hat m essage.)

Let ’s edit t his file t o expose our current_datetime view:

from django.conf.urls.defaults import *


from mysite.views import current_datetime

urlpatterns = patterns('',
(r'^now/$', current_datetime),
)

We m ade t wo changes here. First , we im port ed t he current_datetime view from it s m odule ( mysite/views.py, which t ranslat es
int o mysite.views in Pyt hon im port synt ax) . Next , we added t he line (r'^now/$', current_datetime),. This line is referred t o
as a URLpa t t e r n — it ’s a Pyt hon t uple in which t he first elem ent is a sim ple regular expression and t he second elem ent is t he
view funct ion t o use for t hat pat t ern.

I n a nut shell, we j ust t old Dj ango t hat any request t o t he URL /now/ should be handled by t he current_datetime view funct ion.

A few t hings are wort h point ing out :

● Not e t hat , in t his exam ple, we passed t he current_datetime view funct ion as an obj ect wit hout calling t he funct ion. This is

file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH3.html (2 of 9)9/28/2007 2:08:35 PM


Chapter 3: The basics of generating Web pages

a key feat ure of Pyt hon ( and ot her dynam ic languages) : Funct ions are first - class obj ect s, which m eans you can pass t hem
around j ust like any ot her variables. Cool st uff, eh?

● There’s no need t o add a slash at t he beginning of t he '^now/$' expression in order t o m at ch /now/. Dj ango aut om at ically
put s a slash before every expression.

● The caret charact er ( '^') and dollar sign charact er ( '$') are im port ant . The caret m eans “ require t hat t he pat t ern m at ches
t he st art of t he st ring,” and t he dollar sign m eans “ require t hat t he pat t ern m at ches t he end of t he st ring.”

This concept is best explained by exam ple. I f we had inst ead used t he pat t ern '^now/' ( wit hout a dollar sign at t he end) ,
t hen any URL t hat st art s wit h now/ would m at ch — such as /now/foo and /now/bar, not j ust /now/. Sim ilarly, if we had left
off t he init ial caret charact er ( 'now/$') , Dj ango would m at ch any URL t hat ends wit h now/ — e.g., /foo/bar/now/. Thus, we
use bot h t he caret and dollar sign t o ensure t hat only t he URL /now/ m at ches. Not hing m ore, not hing less.

To t est our changes t o t he URLconf, st art t he Dj ango developm ent server, as you did in Chapt er 1, by running t he
com m and python manage.py runserver. ( I f you left it running, t hat ’s fine, t oo. The developm ent server aut om at ically det ect s
changes t o your Pyt hon code and reloads as necessary, so you don’t have t o rest art t he server bet ween changes.) The server is
running at t he address http://127.0.0.1:8000/, so open up a Web browser and go t o http://127.0.0.1:8000/now/ — and
you should see t he out put of your Dj ango view.

Hooray! You’ve m ade your first Dj ango- powered Web page.

How Dj ango processes a request


We should point out several t hings about what j ust happened. Here’s t he nit t y- grit t y of what goes on when you run t he Dj ango
developm ent server and m ake request s t o Web pages:

● The com m and python manage.py runserver looks for a file called settings.py. This file cont ains all sort s of opt ional
configurat ion for t his part icular Dj ango inst ance, but one of t he m ost im port ant set t ings is one called ROOT_URLCONF.
The ROOT_URLCONF set t ing t ells Dj ango which Pyt hon m odule should be used as t he URLconf for t his Web sit e.

Rem em ber when django-admin.py startproject creat ed t he files settings.py and urls.py? Well, t he aut o-
generat ed settings.py has a ROOT_URLCONF t hat point s t o t he aut o- generat ed urls.py. Convenient .

● When a request com es in — say, a request t o t he URL /now/ — Dj ango loads t he URLconf point ed- t o by t he ROOT_URLCONF
set t ing. Then it checks each of t he URLpat t erns in t hat URLconf in order, com paring t he request ed URL wit h t he pat t erns one
at a t im e, unt il it finds one t hat m at ches. When it finds one t hat m at ches, it calls t he view funct ion associat ed wit h t hat
pat t ern, passing a HttpRequest obj ect as t he first param et er t o t he funct ion. ( More on HttpRequest lat er.)

● The view funct ion is responsible for ret urning an HttpResponse obj ect .

Wit h t his knowledge, you know t he basics of how t o m ake Dj ango- powered pages. I t ’s quit e sim ple, really — j ust writ e view
funct ions and m ap t hem t o URLs via URLconfs.

URLconf s and loose coupling


Now’s a good t im e t o point out a key philosophy behind URLconfs, and behind Dj ango in general: t he principle of loose cou plin g.
Sim ply put , loose coupling is a soft ware- developm ent approach t hat values t he im port ance of m aking pieces int erchangeable. I f
t wo pieces of code are “ loosely coupled,” t hen m aking changes t o one of t he pieces will have lit t le- t o- no effect on t he ot her.

Dj ango’s URLconfs are a good exam ple of t his principle in pract ice. I n a Dj ango Web applicat ion, t he URL definit ions and t he view
funct ions t hey call are loosely coupled; t hat is, t he decision of what t he URL should be for a given funct ion, and t he
im plem ent at ion of t he funct ion it self, reside in t wo separat e places. This let s a developer swit ch out one piece wit hout affect ing
t he ot her.

I n cont rast , ot her Web developm ent plat form s couple t he URL t o t he program . I n basic PHP ( ht t p: / / www.php.net / ) , for exam ple,
t he URL of your applicat ion is designat ed by where you place t he code on your filesyst em . I n t he CherryPy Pyt hon Web
fram ework ( ht t p: / / www.cherrypy.org/ ) , t he URL of your applicat ion corresponds t o t he nam e of t he m et hod in which your code
lives. This m ay seem like a convenient short cut in t he short t erm , but it can get unm anageable in t he long run.

For exam ple, consider t he view funct ion we wrot e above, which displays t he current dat e and t im e. I f we want ed t o change t he
URL for t he applicat ion — say, m ove it from /now/ t o /currenttime/ — we could m ake a quick change t o t he URLconf, wit hout
having t o worry about t he underlying im plem ent at ion of t he funct ion. Sim ilarly, if we want ed t o change t he view funct ion —
alt ering it s logic som ehow — we could do t hat wit hout affect ing t he URL t o which t he funct ion is bound. Furt herm ore, if we
want ed t o expose t he current - dat e funct ionalit y at several URLs, we could easily t ake care of t hat by edit ing t he URLconf, wit hout
having t o t ouch t he view code.

That ’s loose coupling in act ion. And we’ll cont inue t o point out exam ples of t his im port ant philosophy t hroughout t his book.

404 errors
I n our URLconf t hus far, we’ve only defined a single URLpat t ern — t he one t hat handles request s t o t he URL /now/. What happens
when a different URL is request ed?

To find out , t ry running t he Dj ango developm ent server and hit t ing a page such as http://127.0.0.1:8000/hello/

file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH3.html (3 of 9)9/28/2007 2:08:35 PM


Chapter 3: The basics of generating Web pages

or http://127.0.0.1:8000/does-not-exist/, or even http://127.0.0.1:8000/ ( t he sit e “ root ” ) .

You should see a “ Page not found” m essage. ( Pret t y, isn’t it ? We Dj ango people sure do like our past el colors.) Dj ango displays
t his m essage because you request ed a URL t hat ’s not defined in your URLconf.

The ut ilit y of t his page goes beyond t he basic 404 error m essage: I t also t ells you precisely which URLconf Dj ango used and every
pat t ern in t hat URLconf. From t hat inform at ion, you should be able t o t ell why t he request ed URL t hrew a 404.

Nat urally, t his is sensit ive inform at ion int ended only for you, t he Web developer. I f t his were a product ion sit e deployed live on
t he I nt ernet , we wouldn’t want t o expose t hat inform at ion t o t he public. For t hat reason, t his “ Page not found” page is only
displayed if your Dj ango proj ect is in de bu g m ode . We’ll explain how t o deact ivat e debug m ode lat er. For now, j ust know t hat
every Dj ango proj ect is in debug m ode aut om at ically when you st art it .

Your second view: Dynamic URLs


I n our first view exam ple, t he cont ent s of t he page — t he current dat e/ t im e — were dynam ic, but t he URL ( “ / now/ ” ) was st at ic.
I n m ost dynam ic Web applicat ions, t hough, a URL cont ains param et ers t hat influence t he out put of t he page.

As anot her ( slight ly cont rived) exam ple, let ’s creat e a second view, which displays t he current dat e and t im e offset by a cert ain
num ber of hours. The goal is t o craft a sit e in such a way t hat t he page /now/plus1hour/ displays t he dat e/ t im e one hour int o
t he fut ure, t he page /now/plus2hours/ displays t he dat e/ t im e t wo hours int o t he fut ure, t he page /now/plus3hours/ displays
t he dat e/ t im e t hree hours int o t he fut ure, and so on.

A novice m ight t hink t o code a separat e view funct ion for each hour offset , which m ight result in a URLconf t hat looked like t his:

urlpatterns = patterns('',
(r'^now/$', current_datetime),
(r'^now/plus1hour/$', one_hour_ahead),
(r'^now/plus2hours/$', two_hours_ahead),
(r'^now/plus3hours/$', three_hours_ahead),
(r'^now/plus4hours/$', four_hours_ahead),
)

Clearly, t his line of t hought is flawed. Not only would t his result in redundant view funct ions, but t he applicat ion is fundam ent ally
lim it ed t o support ing only t he predefined hour ranges — one, t wo, t hree or four hours. I f, all of a sudden, we want ed t o creat e a
page t hat displayed t he t im e five hours int o t he fut ure, we’d have t o creat e a separat e view and URLconf line for t hat , furt hering
t he duplicat ion and insanit y. We need t o do som e abst ract ion here.

A word about pretty URLs


I f you’re experienced in anot her Web developm ent plat form , such as PHP or Java, you m ay be t hinking: “ Hey, let ’s use a query-
st ring param et er! ” That ’d be som et hing like /now/plus?hours=3, in which t he hours would be designat ed by t he hours param et er
in t he URL’s query st ring ( t he part aft er t he ?) .

You can do t hat wit h Dj ango — and we’ll t ell you how lat er, if you really m ust know — but one of Dj ango’s core philosophies is
t hat URLs should be beaut iful. The URL /now/plus3hours/ is far cleaner, sim pler, m ore readable, easier t o recit e t o som ebody
aloud and … j ust plain pret t ier t han it s query- st ring count erpart . Pret t y URLs are a sign of a qualit y Web applicat ion.

Dj ango’s URLconf syst em encourages pret t y URLs by m aking it easier t o use pret t y URLs t han not t o.

Wildcard URLpatterns
Cont inuing wit h our hours_ahead exam ple, let ’s put a wildcard in t he URLpat t ern. As we m ent ioned above, a URLpat t ern is a
regular expression, and, hence, we can use t he regular expression pat t ern \d+ t o m at ch one or m ore digit s:

from django.conf.urls.defaults import *


from mysite.views import current_datetime, hours_ahead

urlpatterns = patterns('',
(r'^now/$', current_datetime),
(r'^now/plus\d+hours/$', hours_ahead),
)

This URLpat t ern will m at ch any URL such as /now/plus2hours/, /now/plus25hours/ or even /now/plus100000000000hours/.
Com e t o t hink of it , let ’s lim it it so t hat t he m axim um allowed offset is 99 hours. That m eans we want t o allow eit her one- or t wo-
digit num bers; in regular expression synt ax, t hat t ranslat es int o \d{1,2}:

(r'^now/plus\d{1,2}hours/$', hours_ahead),

( When building Web applicat ions, it ’s always im port ant t o consider t he m ost out landish dat a input possible, and decide whet her
t he applicat ion should support t hat input or not . We’ve curt ailed t he out landishness here by lim it ing t he offset t o 99 hours. And,
by t he way, The Out landishness Curt ailers would be a fant ast ic, if verbose, band nam e.)

file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH3.html (4 of 9)9/28/2007 2:08:35 PM


Chapter 3: The basics of generating Web pages

Re gu la r e x pr e ssion s
Regular expressions ( or “ regexes” ) are a com pact way of specifying pat t erns in t ext . While Dj ango URLconfs allow
arbit rary regexes for powerful URL- m at ching capabilit y, you’ll probably only use a few regex pat t erns in pract ice.
Here’s a sm all select ion of com m on pat t erns:

Sym bol M a t ch e s
. ( dot ) Any charact er
\d Any digit
[A-Z] Any charact er from A- Z ( uppercase)
[a-z] Any charact er from a- z ( lowercase)
[A-Za-z] Any charact er from a- z ( case- insensit ive)
[^/]+ All charact ers unt il a forward slash ( excluding t he slash it self)
+ One or m ore of t he previous charact er ( e.g., \d+ m at ches one or m ore digit )
? Zero or m ore of t he previous charact er ( e.g., \d* m at ches zero or m ore digit s)
{1,3} Bet ween one and t hree ( inclusive) of t he previous charact er

For m ore on regular expressions, see Appendix XXX, Regular Expressions.

Now t hat we’ve designat ed a wildcard for t he URL, we need a way of passing t hat dat a t o t he view funct ion, so t hat we can use a
single view funct ion for any arbit rary hour offset . We do t his by placing parent heses around t he dat a in t he URLpat t ern t hat we
want t o save. I n t he case of our exam ple, we want t o save what ever num ber was ent ered in t he URL — so let ’s put parent heses
around t he \d{1,2}:

(r'^now/plus(\d{1,2})hours/$', hours_ahead),

I f you’re fam iliar wit h regular expressions, you’ll be right at hom e here; we’re using parent heses t o capt ure dat a from t he
m at ched t ext .

The final URLconf, including our previous current_datetime view, looks like t his:

from django.conf.urls.defaults import *


from mysite.views import current_datetime, hours_ahead

urlpatterns = patterns('',
(r'^now/$', current_datetime),
(r'^now/plus(\d{1,2})hours/$', hours_ahead),
)

Wit h t hat t aken care of, let ’s writ e t he hours_ahead view.

..adm onit ion: : Coding order

In t his case, we wrot e t he URLpat t ern f irst and t he view second, but in t he previous example, we wrot e t he view
f irst , t hen t he URLpat t ern. Which t echnique is bet t er?

Well, every developer is dif f erent .

If you’ re a big-pict ure t ype of person, it may make most sense t o you t o writ e all of t he URLpat t erns f or your
applicat ion at t he same t ime, at t he st art of your proj ect , t hen coding up t he views. This has t he advant age of
giving you a clear t o-do list , and it essent ially def ines t he paramet er requirement s f or t he view f unct ions you’ ll
need t o writ e.

If you’ re more of a bot t om-up developer, you might pref er t o writ e t he views f irst , t hen anchor t hem t o URLs
af t erward. That ’ s OK, t oo.

In t he end, it comes down t o what f it s your brain t he best . Eit her approach is valid.

hours_ahead is very sim ilar t o t he current_datetime view we wrot e earlier, wit h a key difference: it t akes an ext ra argum ent ,
t he num ber of hours of offset . Here it is:

file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH3.html (5 of 9)9/28/2007 2:08:35 PM


Chapter 3: The basics of generating Web pages

from django.http import HttpResponse


import datetime

def hours_ahead(request, offset):


offset = int(offset)
dt = datetime.datetime.now() + datetime.timedelta(hours=offset)
html = "<html><body>In %s hour(s), it will be %s.</body></html>" % (offset, dt)
return HttpResponse(html)

Let ’s st ep t hrough t his code one line at a t im e:

● Just as we did for our current_datetime view, we im port t he class django.http.HttpResponse and t he datetime m odule.

● The view funct ion, hours_ahead, t akes t wo param et ers: request and offset.

❍ request is an HttpRequest obj ect , j ust as in current_datetime. We’ll say it again: Each view always t akes
an HttpRequest obj ect as it s first param et er.

❍ offset is t he st ring capt ured by t he parent heses in t he URLpat t ern. For exam ple, if t he request ed URL
were /now/plus3hours/, t hen offset would be t he st ring '3'. I f t he request ed URL were /now/plus21hours/,
t hen offset would be t he st ring '21'. Not e t hat capt ured st rings will always be st rings, not int egers, even if t he st ring is
com posed of only digit s, such as '21'.

We decided t o call t he variable offset, but you can call it what ever you’d like, as long as it ’s a valid Pyt hon ident ifier. The
variable nam e doesn’t m at t er; all t hat m at t ers is t hat it ’s t he second argum ent t o t he funct ion ( aft er request) .

● The first t hing we do wit hin t he funct ion is call int() on offset. This convert s t he st ring value t o an int eger.

Not e t hat Pyt hon will raise a ValueError except ion if you call int() on a value t hat cannot be convert ed t o an int eger, such
as t he st ring 'foo'. However, we don’t have t o worry about cat ching t hat except ion, because we can be cert ain offset will
be a st ring cont aining only digit s. We know t hat because t he regular- expression pat t ern in our URLconf — \d{1,2} —
capt ures only digit s. This illust rat es anot her nicet y of URLconfs: They provide a fair level of input validat ion.

● The next line of t he funct ion shows why we called int() on offset. On t his line, we calculat e t he current t im e plus a t im e
offset of offset hours, st oring t he result in dt. The datetime.timedelta funct ion requires t he hours param et er t o be an
int eger.

● Next , we const ruct t he HTML out put of t his view funct ion, j ust as we did in current_datetime. A sm all difference in t his line
from t he previous line is t hat it uses Pyt hon’s form at - st ring capabilit y wit h t wo values, not j ust one. Hence, t here are t wo %s
sym bols in t he st ring and a t uple of values t o insert — (offset, dt).

● Finally, we ret urn an HttpResponse of t he HTML — again, j ust as we did in current_datetime.

Wit h t hat view funct ion and URLconf writ t en, st art t he Dj ango developm ent server ( if it ’s not already running) , and
visit http://127.0.0.1:8000/now/plus3hours/ t o verify it works. Then t ry http://127.0.0.1:8000/now/plus5hours/.
Then http://127.0.0.1:8000/now/plus24hours/. Finally, visit http://127.0.0.1:8000/now/plus100hours/ t o verify t hat t he
pat t ern in your URLconf only accept s one- or t wo- digit num bers; Dj ango should display a “ Page not found” error in t his case, j ust
as we saw in t he “ 404 errors” sect ion above. The URL http://127.0.0.1:8000/now/plushours/ ( wit h no hour designat ion)
should also t hrow a 404.

I f you’re following along while coding at t he sam e t im e, you’ll not ice t hat t he views.py file now cont ains t wo views. ( We om it t ed
t he current_datetime view from t he last set of exam ples for clarit y.) Put t oget her, views.py should look like t his:

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)

def hours_ahead(request, offset):


offset = int(offset)
dt = datetime.datetime.now() + datetime.timedelta(hours=offset)
html = "<html><body>In %s hour(s), it will be %s.</body></html>" % (offset, dt)
return HttpResponse(html)

Dj ango’ s pret t y error pages


Take a m om ent t o adm ire t he fine Web applicat ion we’ve m ade so far…and break it !

Let ’s deliberat ely int roduce a Pyt hon error int o our views.py file, by com m ent ing- out t he offset = int(offset) line in
t he hours_ahead view:

file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH3.html (6 of 9)9/28/2007 2:08:35 PM


Chapter 3: The basics of generating Web pages

def hours_ahead(request, offset):


# offset = int(offset)
dt = datetime.datetime.now() + datetime.timedelta(hours=offset)
html = "<html><body>In %s hour(s), it will be %s.</body></html>" % (offset, dt)
return HttpResponse(html)

Now load up t he developm ent server and navigat e t o /now/plus3hours/. You’ll see an error page wit h a significant am ount of
inform at ion, including a TypeError m essage displayed at t he very t op: “ unsupport ed t ype for t im edelt a hours com ponent : st r” .

What happened?

Well, t he datetime.timedelta funct ion expect s t he hours param et er t o be an int eger, and we com m ent ed- out t he bit of code
t hat convert ed offset t o an int eger. That caused datetime.timedelta t o raise t he TypeError. I t ’s t he t ypical kind of sm all bug
t hat every program m er runs int o at som e point .

The point of t his exam ple was t o dem onst rat e Dj ango’s error pages. Take som e t im e t o explore t he error page and get t o know
t he various bit s of inform at ion it gives you.

Som e highlight s:

● At t he t op of t he page, you get t he key inform at ion about t he except ion: t he t ype of except ion, any param et ers t o t he
except ion ( e.g., t he "unsupported type" m essage in t his case) , t he file in which t he except ion was raised and t he offending
line num ber.

● Under t hat , t he page displays t he full Pyt hon t raceback for t his except ion. This is sim ilar t o t he st andard t raceback you get in
Pyt hon’s com m and- line int erpret er, except it ’s m ore int eract ive. For each fram e in t he st ack, Dj ango displays t he nam e of
t he file, t he funct ion/ m et hod nam e, t he line num ber and t he source code of t hat line.

Click t he line of source code ( in dark gray) , and you’ll see several lines from before and aft er t he erroneous line, t o give you
cont ext .

Click “ Local vars” under any fram e in t he st ack t o view a t able of all local variables, and t heir values, in t hat fram e, at t he
exact point in t he code at which t he except ion was raised. This debugging inform at ion is invaluable.

● Not e t he “ Swit ch t o copy- and- past e view” t ext j ust under t he “ Traceback” header. Click t hose words, and t he t raceback will
swit ch t o a alt ernat e version t hat can be easily copied and past ed. Use t his when you want t o share your except ion
t raceback wit h ot hers t o get t echnical support — such as t he kind folks in t he Dj ango I RC chat room or on t he Dj ango users
m ailing list .

● Next , t he “ Request inform at ion” sect ion includes a wealt h of inform at ion about t he incom ing Web request t hat spawned t he
error: GET and POST inform at ion, cookie values and m et a inform at ion, such as CGI headers. I f t his inform at ion seem s like
gibberish t o you at t he m om ent , don’t fret — we’ll explain it lat er in t his book.

Below, t he “ Set t ings” sect ion list s all of t he set t ings for t his part icular Dj ango inst allat ion. Again, we’ll explain set t ings lat er
in t his book. For now, t ake a look at t he set t ings t o get an idea of t he inform at ion available.

The Dj ango error page is capable of displaying m ore inform at ion in cert ain special cases, such as t he case of t em plat e synt ax
errors. We’ll get t o t hose lat er, when we discuss t he Dj ango t em plat e syst em . For now, uncom m ent t he offset = int(offset)
line t o get t he view funct ion working properly again.

Are you t he t ype of program m er who likes t o debug wit h t he help of carefully placed print st at em ent s? You can use t he Dj ango
error page t o do j ust t hat — j ust wit hout t he print st at em ent s. At any point in your view, t em porarily insert an assert False t o
t rigger t he error page. Then, you can view t he local variables and st at e of t he program . ( There’s a m ore advanced way t o debug
Dj ango views, which we’ll explain lat er, but t his is t he quickest and easiest .)

Finally, it ’s obvious t hat m uch of t his inform at ion is sensit ive — it exposes t he innards of your Pyt hon code and Dj ango
configurat ion — and it would be foolish t o show t his inform at ion on t he public I nt ernet . A m alicious person could use it t o at t em pt
t o reverse- engineer your Web applicat ion and do nast y t hings. For t hat reason, t he Dj ango error page is only displayed when
your Dj ango proj ect is in debug m ode. We’ll explain how t o deact ivat e debug m ode lat er. For now, j ust know t hat every Dj ango
proj ect is in debug m ode aut om at ically when you st art it . ( Sound fam iliar? The “ Page not found” errors, described in t he “ 404
errors” sect ion above, work t he sam e way.)

Exercises
Here are a few exercises t hat will solidify som e of t he t hings you learned in t his chapt er. ( Hint : Even if you t hink you underst ood
everyt hing, at least give t hese exercises, and t heir respect ive answers, a read. We int roduce a couple of new t ricks here.)

1. Creat e anot her view, hours_behind, t hat works like hours_ahead but inst ead displays t he dat e/ t im e wit h an offset int o t he
past , not t he fut ure. This view should bind t o URLs in t he st yle /now/minusXhours/, where X is t he offset , in hours.
2. Once you’ve done t hat , be a good program m er and not ice how sim ilar t he hours_ahead and hours_behind views are. How
redundant ! Elim inat e t he redundancy and com bine t hem int o a single view, hour_offset. The URLs should st ay t he sam e as
before: e.g., /now/minusXhours/ and /now/plusXhours/. Don’t forget t o change t he HTML t o say eit her “ I n X hour( s) ” or “ X
hour( s) ago” , depending on whet her t he offset is posit ive or negat ive.
3. We were lazy and hard- coded t he plural form of “ hour” in t he URL, result ing in t he gram m at ic at rocit y /now/plus1hours/.
Do your part t o uphold proper English gram m ar, and im prove t he applicat ion so t hat it accept s t he URL /now/plus1hour/.

file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH3.html (7 of 9)9/28/2007 2:08:35 PM


Chapter 3: The basics of generating Web pages

For bonus point s, be a perfect ionist : allow /now/plus1hour/ and /now/plus2hours/ but disallow /now/plus1hours/
and /now/plus2hour/.
4. Sim ilarly, we were lazy in t he HTML display, saying "In %s hour(s), it will be %s." Fix t his t o rem ove t he hour(s).
The (s) was such a cop- out ! I f t he offset is singular, use 'hour'; ot herwise, use 'hours'.

Answers t o exercises
1. Here’s one im plem ent at ion of t he hours_behind view:

def hours_behind(request, offset):


offset = int(offset)
dt = datetime.datetime.now() - datetime.timedelta(hours=offset)
html = "<html><body>%s hour(s) ago, it was %s.</body></html>" % (offset, dt)
return HttpResponse(html)

Not m uch is different bet ween t his view and hours_ahead — only t he calculat ion of dt and t he t ext wit hin t he HTML.

The URLpat t ern would look like t his:

(r'^now/minus(\d{1,2})hours/$', hours_behind),

2. Here’s one im plem ent at ion of t he hour_offset view:

def hour_offset(request, plus_or_minus, offset):


offset = int(offset)
if plus_or_minus == 'plus':
dt = datetime.datetime.now() + datetime.timedelta(hours=offset)
html = 'In %s hour(s), it will be %s.' % (offset, dt)
else:
dt = datetime.datetime.now() - datetime.timedelta(hours=offset)
html = '%s hour(s) ago, it was %s.' % (offset, dt)
html = '<html><body>%s</body></html>' % html
return HttpResponse(html)

The URLpat t ern would look like t his:

(r'^now/(plus|minus)(\d{1,2})hours/$', hour_offset),

I n t his im plem ent at ion, we capt ure t wo values from t he URL — t he offset , as we did before, but also t he st ring t hat
designat es whet her t he offset should be posit ive or negat ive. They’re passed t o t he view funct ion in t he order in which
t hey’re capt ured.

I nside t he view code, t he variable plus_or_minus will be eit her t he st ring 'plus' or t he st ring 'minus'. We t est t hat t o
det erm ine how t o calculat e t he offset — eit her by adding or subt ract ing a datetime.timedelta.

I f you’re part icularly anal, you m ay find it inelegant t hat t he view code is “ aware” of t he URL, having t o t est for t he
st ring 'plus' or 'minus' rat her t han som e ot her variable t hat has been abst ract ed from t he URL. There’s no way around
t hat ; Dj ango does not include any sort of “ m iddlem an” layer t hat convert s capt ured URL param et ers t o abst ract ed dat a
st ruct ures, for sim plicit y’s sake.

3. To accom plish t his, we wouldn’t have t o change t he hour_offset view at all. We’d j ust need t o edit t he URLconf slight ly.
Here’s one way t o do it , by using t wo URLpat t erns:

(r'^now/(plus|minus)(1)hour/$', hour_offset),
(r'^now/(plus|minus)([2-9]|\d\d)hours/$', hour_offset),

More t han one URLpat t ern can point t o t he sam e view; Dj ango processes t he pat t erns in order and doesn’t care how m any
t im es a cert ain view is referenced. I n t his case, t he first pat t ern m at ches t he URLs /now/plus1hour/ and /now/minus1hour/
. The (1) is a neat lit t le t rick — it passes t he value '1' as t he capt ured value, wit hout allowing any sort of wildcard.

The second pat t ern is m ore com plex, as it uses a slight ly t ricky regular expression. The key part is ([2-9]|\d\d). The pipe
charact er ( '|') m eans “ or,” so t he pat t ern in full m eans “ m at ch eit her t he pat t ern [2-9] or \d\d.” I n ot her words, t hat
m at ches any one- digit num ber from 2 t hrough 9, or any t wo- digit num ber.

4. Here’s a basic way of accom plishing t his. Alt er t he hour_offset funct ion like so:

def hour_offset(request, plus_or_minus, offset):


offset = int(offset)
if offset == 1:

file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH3.html (8 of 9)9/28/2007 2:08:35 PM


Chapter 3: The basics of generating Web pages

hours = 'hour'
else:
hours = 'hours'
if plus_or_minus == 'plus':
dt = datetime.datetime.now() + datetime.timedelta(hours=offset)
output = 'In %s %s, it will be %s.' % (offset, hours, dt)
else:
dt = datetime.datetime.now() - datetime.timedelta(hours=offset)
output = '%s %s ago, it was %s.' % (offset, hours, dt)
output = '<html><body>%s</body></html>' % output
return HttpResponse(output)

I deally, t hough, we wouldn’t have t o edit Pyt hon code t o m ake sm all present at ion- relat ed changes like t his. Wouldn’t it be
nice if we could separat e present at ion from Pyt hon logic? Ah, foreshadowing…

« previous ◊ t able of cont ent s ◊ next »


Copyright 2006 Adrian Holovat y and Jacob Kaplan- Moss.
This work is licensed under t he GNU Free Docum ent License.

file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH3.html (9 of 9)9/28/2007 2:08:35 PM


Chapter 4: The Django template system

The Dj ango Book


« previous ◊ t able of cont ent s ◊ next »

Chapt er 4: The Dj ango t emplat e syst em


I n t he previous chapt er, you m ay have not iced som et hing peculiar in how we ret urned t he HTML in our exam ple views. Nam ely, t he
HTML was hard- coded direct ly in our Pyt hon code!

This arrangem ent leads t o several problem s:

● Obviously, any change t o t he design of t he page would require a change t o t he Pyt hon code. The design of a sit e t ends t o
change far m ore frequent ly t han t he underlying Pyt hon code, so it would be convenient if t he frequency of HTML changes were
separat ed from changes t o Pyt hon code.
● Second, writ ing backend Pyt hon code and designing/ coding HTML are t wo different disciplines, and m ost professional Web
developm ent environm ent s split t hese responsibilit ies across separat e people ( or even separat e depart m ent s) . Designers and
HTML/ CSS coders shouldn’t have t o edit Pyt hon code t o get t heir j ob done; t hey should deal wit h HTML.
● Sim ilarly, it ’s m ost efficient if program m ers can work on Pyt hon code and designers can work on t em plat es at t he sam e t im e,
rat her t han one person wait ing for t he ot her t o finish edit ing a single file t hat cont ains bot h Pyt hon and HTML.

For t hese reasons, it ’s m uch cleaner and m ore m aint ainable t o separat e t he design of t he page from t he Pyt hon code it self. We can
do t his wit h Dj ango’s t e m pla t e syst e m .

Templat e syst em basics


A Dj ango t em plat e is a st ring of t ext t hat is int ended t o separat e t he present at ion of a docum ent from it s dat a. A t em plat e defines
placeholders and various bit s of basic logic — t a gs — t hat regulat e how t he docum ent should be displayed. Usually, t em plat es are
used for out put t ing HTML, but Dj ango t em plat es are equally capable of generat ing any t ext - based form at .

Let ’s dive in wit h a sim ple exam ple t em plat e. This t em plat e describes an HTML page t hat t hanks a person for m aking an order from
a com pany. Think of it as a form let t er:

<html>
<head><title>Ordering notice</title></head>

<body>

<p>Dear {{ person_name }},</p>

<p>Thanks for placing an order from {{ company }}. It's scheduled to


ship on {{ ship_date|date:"F j, Y" }}.</p>
<p>Here are the items you've ordered:</p>

file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH4.html (1 of 34)9/28/2007 2:09:26 PM


Chapter 4: The Django template system

<ul>
{% for item in item_list %}
<li>{{ item }}</li>
{% endfor %}
</ul>
{% if ordered_warranty %}
<p>Your warranty information will be included in the packaging.</p>
{% endif %}
<p>Sincerely,<br />{{ company }}</p>
</body>
</html>

This t em plat e is basic HTML wit h som e va r ia ble s and t e m pla t e t a gs t hrown in. Let ’s st ep t hrough it :

● Any t ext surrounded by a pair of braces — e.g., {{ person_name }} — is a va r ia ble . This m eans “ insert t he value of t he
variable wit h t he given nam e.” ( How do we specify t he values of t he variables? We’ll get t o t hat in a m om ent .)

● Any t ext t hat ’s surrounded by curly braces and percent signs — e.g., {% if ordered_warranty %} — is a block t a g. The
definit ion of a block t ag is quit e broad: A block t ag j ust t ells t he t em plat e syst em t o do som et hing.

This exam ple t em plat e cont ains t wo block t ags — t he {% for item in item_list %} t ag ( a “ for” t ag) and
t he {% if ordered_warranty %} t ag ( an “ if” t ag) . A “ for” t ag act s as a sim ple loop const ruct , let t ing you loop over each it em
in a sequence. An “ if” t ag, as you m ay expect , act s as a logical “ if” st at em ent . I n t his part icular case, t he t ag checks whet her
t he value of t he ordered_warranty variable evaluat es t o True. I f it does, t he t em plat e syst em will display everyt hing bet ween
t he {% if ordered_warranty %} and {% endif %}. I f not , t he t em plat e syst em won’t display it . The t em plat e syst em also
support s {% else %} and ot her various logic st at em ent s.

Each Dj ango t em plat e has access t o several built - in block t ags. I n addit ion, you can writ e your own t ags.

● Finally, t he second paragraph of t his t em plat e has an exam ple of a filt e r . Filt ers are a way t o alt er t he display of a variable. I n
t his exam ple — {{ ship_date|date:"F j, Y" }} — we’re passing t he ship_date variable t o t he date filt er, giving t he date
filt er an argum ent "F j, Y". The date filt er form at s dat es in a given form at , as specified by t hat argum ent . Filt ers are
at t ached using a pipe charact er ( |) , as a reference t o Unix pipes.

Each Dj ango t em plat e has access t o several built - in filt ers. I n addit ion, you can writ e your own filt ers.

Using t he t emplat e syst em


To use t he t em plat e syst em in Pyt hon code, j ust follow t hese t wo st eps:

● First , creat e a Template obj ect by providing t he raw t em plat e code as a st ring. Dj ango also offers a way t o creat e Template
obj ect s by designat ing t he pat h t o a t em plat e file on t he filesyst em ; we’ll see t hat in a bit .
● Then, call t he render() m et hod of t he Template obj ect wit h a given set of variables — t he cont ext . This ret urns a fully
rendered t em plat e, as a st ring, wit h all of t he variables and block t ags evaluat ed according t o t he cont ext .
file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH4.html (2 of 34)9/28/2007 2:09:26 PM
Chapter 4: The Django template system

Creating template obj ects


The easiest way t o creat e a Template obj ect is t o inst ant iat e it direct ly. The Template class lives in t he django.template m odule,
and t he const ruct or t akes one argum ent , t he raw t em plat e code. Let ’s dip int o t he Pyt hon int eract ive int erpret er t o see how t his
works in code. ( Type python at t he com m and line t o st art t he int eract ive int erpret er.) Here’s a basic walkt hrough:

>>> from django.template import Template


>>> t = Template("My name is {{ my_name }}.")
>>> print t

I f you’re following along int eract ively, you’ll see som et hing like t his aft er t yping print t:

<django.template.Template object at 0xb7d5f24c>

That 0xb7d5f24c will be different every t im e, and it doesn’t really m at t er; it ’s sim ply t he Pyt hon “ ident it y” of t he Template obj ect .

I n t e r a ct ive in t e r pr e t e r e x a m ple s
Throughout t his book, we’ll feat ure exam ple Pyt hon int eract ive int erpret er sessions. You can recognize t hese
exam ples by spot t ing t he t riple great er- t han signs ( >>>) , which designat e t he int erpret er’s prom pt . I f you’re copying
exam ples from t his book, don’t copy t hose great er- t han signs.

Mult iline st at em ent s in t he int eract ive int erpret er are padded wit h t hree dot s ( ...) . For exam ple:

>>> print """This is a


... string that spans
... three lines."""
This is a
string that spans
three lines.
>>> def my_function(value):
... print value
>>> my_function('hello')
hello

Those t hree dot s at t he st art of t he addit ional lines are insert ed by t he Pyt hon shell — t hey’re not part of our input .
We include t hem here t o be fait hful t o t he act ual out put of t he int erpret er. I f you copy our exam ples t o follow along,
don’t copy t hose dot s.

When you creat e a Template obj ect , t he t em plat e syst em com piles t he raw t em plat e code int o an int ernal, opt im ized form , ready
for rendering. But if your t em plat e code includes any synt ax errors, t he call t o Template() will cause a TemplateSyntaxError
except ion:

file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH4.html (3 of 34)9/28/2007 2:09:26 PM


Chapter 4: The Django template system

>>> from django.template import Template


>>> t = Template('{% notatag %} ')
Traceback (most recent call last):
File "<stdin>", line 1, in ?
...
django.template.TemplateSyntaxError: Invalid block tag: 'notatag'

The syst em raises a TemplateSyntaxError except ion for any of t he following cases:

● I nvalid block t ags


● I nvalid argum ent s t o valid block t ags
● I nvalid filt ers
● I nvalid argum ent s t o valid filt ers
● I nvalid t em plat e synt ax
● Unclosed block t ags ( for block t ags t hat require closing t ags)

Rendering a template
Once you have a Template obj ect , you can pass it dat a by giving it a con t e x t . A cont ext is sim ply a set of variables and t heir
associat ed values. A t em plat e uses t his t o populat e it s variable t ags and evaluat e it s block t ags.

A cont ext is represent ed in Pyt hon by t he Context class, which lives in t he django.template m odule. I t s const ruct or t akes one
opt ional argum ent : a dict ionary m apping variable nam es t o variable values. Call t he Template obj ect ’s render() m et hod wit h t he
cont ext t o “ fill” t he t em plat e. For exam ple:

>>> from django.template import Context, Template


>>> t = Template("My name is {{ name }}.")
>>> c = Context({"name": "Stephane"})
>>> t.render(c)
'My name is Stephane.'

Variable nam es m ust begin wit h a let t er ( A- Z or a- z) and m ay cont ain digit s, underscores and dot s. ( Dot s are a special case we’ll
get t o in a m om ent .) Variable nam es are case sensit ive.

Here’s an exam ple of t em plat e com pilat ion and rendering, using t he sam ple t em plat e from t he beginning of t his chapt er:

>>> from django.template import Template, Context


>>> raw_template = """<p>Dear {{ person_name }},</p>
...
... <p>Thanks for ordering {{ product }} from {{ company }}. It's scheduled to
... ship on {{ ship_date|date:"F j, Y" }}.</p>
...
... {% if ordered_warranty %}
... <p>Your warranty information will be included in the packaging.</p>
file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH4.html (4 of 34)9/28/2007 2:09:26 PM
Chapter 4: The Django template system

... {% endif %}
...
... <p>Sincerely,<br />{{ company }}</p>"""
>>> t = Template(raw_template)
>>> import datetime
>>> c = Context({'person_name': 'John Smith',
... 'product': 'Super Lawn Mower',
... 'company': 'Outdoor Equipment',
... 'ship_date': datetime.date(2009, 4, 2),
... 'ordered_warranty': True})
>>> t.render(c)
"<p>Dear John Smith,</p>\n\n<p>Thanks for ordering Super Lawn Mower from Outdoor Equipment.
It's scheduled to ship on April 2, 2009.</p>\n\n<p>Your warranty information will be included
in the packaging.</p>\n\n\n<p>Sincerely,<br />Outdoor Equipment</p>"

Let ’s st ep t hrough t his one st at em ent at a t im e:

● First , we im port t he classes Template and Context, which bot h live in t he m odule django.template.

● Next , we save t he raw t ext of our t em plat e int o t he variable raw_template. Not e t hat we use a t riple quot e m arks t o designat e
t he st ring, because it wraps over m ult iple lines; st rings designat ed wit h single quot e m arks cannot be wrapped over m ult iple
lines.

● Next , we creat e a t em plat e obj ect t by passing raw_template t o t he Template class const ruct or.

● Then we im port t he datetime m odule from Pyt hon’s st andard library, because we’ll need it in t he following st at em ent .

● Next , we creat e a cont ext obj ect c. The Context const ruct or t akes a Pyt hon dict ionary m apping variable nam es t o values.
Here, for exam ple, we specify t hat t he person_name is 'John Smith', product is 'Super Lawn Mower', et c.

● Finally, we call t he render() m et hod on our t em plat e obj ect , passing it t he cont ext . This ret urns t he rendered t em plat e — t hat
is, it replaces t em plat e variables wit h t he act ual values of t he variables, and it execut es any block t ags.

Not e t hat t he warrant y paragraph was displayed because t he ordered_warranty variable evaluat ed t o True. Also not e t he
dat e, April 2, 2009, which is displayed according t o t he form at st ring 'F j, Y'. ( We’ll explain form at st rings for t he date
filt er short ly.)

I f you’re new t o Pyt hon, you m ay wonder why t his out put includes newline charact ers ( '\n') rat her t han displaying t he line
breaks. That ’s happening because of a subt let y in t he Pyt hon int eract ive int erpret er: The call t o t.render(c) ret urns a st ring,
and by default t he int eract ive int erpret er displays t he represent at ion of t he st ring, rat her t han t he print ed value of t he st ring.
I f you want t o see t he st ring wit h line breaks displayed as t rue line breaks rat her t han '\n' charact ers, use t he print
st at em ent : print t.render(c).

Those are t he fundam ent als of using t he Dj ango t em plat e syst em — j ust writ e a t em plat e, creat e a t em plat e obj ect , creat e a
cont ext and call t he render() m et hod.

Multiple contexts, same template


file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH4.html (5 of 34)9/28/2007 2:09:26 PM
Chapter 4: The Django template system

Once you have a t em plat e obj ect , you can render m ult iple cont ext s t hrough it . For exam ple:

>>> from django.template import Template, Context


>>> t = Template('Hello, {{ name }}')
>>> print t.render(Context({'name': 'John'}))
Hello, John
>>> print t.render(Context({'name': 'Julie'}))
Hello, Julie
>>> print t.render(Context({'name': 'Pat'}))
Hello, Pat

Whenever you’re using t he sam e t em plat e t o render m ult iple cont ext s like t his, it ’s m ost efficient t o creat e t he Template obj ect
once, t hen call render() on it m ult iple t im es. For exam ple:

# Bad
for name in ('John', 'Julie', 'Pat'):
t = Template('Hello, {{ name }}')
print t.render(Context({'name': name}))
# Good
t = Template('Hello, {{ name }}')
for name in ('John', 'Julie', 'Pat'):
print t.render(Context({'name': name}))

Dj ango’s t em plat e parsing is quit e fast . Behind t he scenes, m ost of t he parsing happens via a single call t o a short regular
expression. This is a st ark cont rast t o XML- based t em plat ing engines, which incur t he overhead of an XML parser and t end t o be
orders of m agnit ude slower t han Dj ango’s t em plat e rendering engine.

Context variable lookup


I n t he exam ples so far, we’ve passed sim ple values in t he t em plat e cont ext s — m ost ly st rings, plus a datetime.date exam ple.
However, t he t em plat e syst em elegant ly handles m ore com plex dat a st ruct ures, such as list s, dict ionaries and cust om obj ect s.

The key t o t raversing com plex dat a st ruct ures in Dj ango t em plat es is t he dot ( .) charact er. Use a dot t o access dict ionary keys,
at t ribut es, indices or m et hods of an obj ect .

This is best illust rat ed wit h a few exam ples. First , say you’re passing a Pyt hon dict ionary t o a t em plat e. To access t he values of t hat
dict ionary by dict ionary key, use a dot :

>>> from django.template import Template, Context


>>> person = {'name': 'Sally', 'age': '43'}
>>> t = Template('{{ person.name }} is {{ person.age }} years old.')
>>> c = Context({'person': person})
>>> t.render(c)
'Sally is 43 years old.'
file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH4.html (6 of 34)9/28/2007 2:09:26 PM
Chapter 4: The Django template system

Sim ilarly, dot s also allow access of obj ect at t ribut es. For exam ple, a Pyt hon datetime.date obj ect has year, month and day
at t ribut es, and you can use a dot t o access t hose at t ribut es in a Dj ango t em plat e:

>>> from django.template import Template, Context


>>> import datetime
>>> d = datetime.date(1993, 5, 2)
>>> d.year
1993
>>> d.month
5
>>> d.day
2
>>> t = Template('The month is {{ date.month }} and the year is {{ date.year }}.')
>>> c = Context({'date': d})
>>> t.render(c)
'The month is 5 and the year is 1993.'

This exam ple uses a cust om class:

>>> from django.template import Template, Context


>>> class Person(object):
... def __init__(self, first_name, last_name):
... self.first_name, self.last_name = first_name, last_name
>>> t = Template('Hello, {{ person.first_name }} {{ person.last_name }}.')
>>> c = Context({'person': Person('John', 'Smith')})
>>> t.render(c)
'Hello, John Smith.'

Dot s are also used t o access list indices. For exam ple:

>>> from django.template import Template, Context


>>> t = Template('Item 2 is {{ items.2 }}.')
>>> c = Context({'items': ['apples', 'bananas', 'carrots']})
>>> t.render(c)
'Item 2 is carrots.'

Negat ive list indices are not allowed. For exam ple, t he t em plat e variable {{ items.-1 }} would cause a TemplateSyntaxError.

Finally, dot s are also used t o call m et hods on obj ect s. For exam ple, each Pyt hon st ring has t he m et hods upper() and isdigit(),
and you can call t hose in Dj ango t em plat es using t he sam e dot synt ax:

>>> from django.template import Template, Context


>>> t = Template('{{ var }} -- {{ var.upper }} -- {{ var.isdigit }}')
file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH4.html (7 of 34)9/28/2007 2:09:26 PM
Chapter 4: The Django template system

>>> t.render(Context({'var': 'hello'}))


'hello -- HELLO -- False'
>>> t.render(Context({'var': '123'}))
'123 -- 123 -- True'

Not e t hat , in t he m et hod calls, you don’t include parent heses. Also, it ’s not possible t o pass argum ent s t o t he m et hods; you can
only call m et hods t hat have no required argum ent s. ( We’ll explain t his philosophy lat er in t his chapt er.)

The dot lookups can be sum m arized like t his: When t he t em plat e syst em encount ers a dot in a variable nam e, it t ries t he following
lookups, in t his order:

● Dict ionary lookup. Exam ple: foo["bar"]


● At t ribut e lookup. Exam ple: foo.bar
● Met hod call. Exam ple: foo.bar()
● List - index lookup. Exam ple: foo[bar]

The syst em uses t he first lookup t ype t hat works. I t ’s short - circuit logic.

Dot lookups can be nest ed m ult iple levels deep. For inst ance, t he following exam ple uses {{ person.name.upper }}, which
t ranslat es int o a dict ionary lookup ( person['name']) , t hen a m et hod call ( upper()) :

>>> from django.template import Template, Context


>>> person = {'name': 'Sally', 'age': '43'}
>>> t = Template('{{ person.name.upper }} is {{ person.age }} years old.')
>>> c = Context({'person': person})
>>> t.render(c)
'SALLY is 43 years old.'

A word about method calls


Met hod calls are slight ly m ore com plex t han t he ot her lookup t ypes. Here are som e t hings t o keep in m ind:

● I f, during t he m et hod lookup, a m et hod raises an except ion, t he except ion will be propagat ed, unless t he except ion has an
at t ribut e silent_variable_failure whose value is True. I f t he except ion does have a silent_variable_failure at t ribut e,
t he variable will render as an em pt y st ring. For exam ple:

>>> t = Template("My name is {{ person.first_name }}.")


>>> class PersonClass3:
... def first_name(self):
... raise AssertionError, "foo"
>>> p = PersonClass3()
>>> t.render(Context({"person": p}))
Traceback (most recent call last):
...
AssertionError: foo
file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH4.html (8 of 34)9/28/2007 2:09:26 PM
Chapter 4: The Django template system

>>> class SilentAssertionError(AssertionError):


... silent_variable_failure = True
>>> class PersonClass4:
... def first_name(self):
... raise SilentAssertionError
>>> p = PersonClass4()
>>> t.render(Context({"person": p}))
"My name is ."

● A m et hod call will only work if t he m et hod has no required argum ent s. Ot herwise, t he syst em will m ove t o t he next lookup
t ype ( list - index lookup) .

● Obviously, som e m et hods have side effect s, and it ’d be foolish at best , and possibly even a securit y hole, t o allow t he t em plat e
syst em t o access t hem .

Say, for inst ance, you have a BankAccount obj ect t hat has a delete() m et hod. The t em plat e syst em shouldn’t be allowed t o
do som et hing like t his:

I will now delete this valuable data. {{ account.delete }}

To prevent t his, set a funct ion at t ribut e alters_data on t he m et hod. The t em plat e syst em won’t execut e a m et hod if t he
m et hod has alters_data=True set . For exam ple:

def delete(self):
# Delete the account
delete.alters_data = True

How invalid variables are handled


By default , if a variable doesn’t exist , t he t em plat e syst em renders it as an em pt y st ring, failing silent ly. For exam ple:

>>> from django.template import Template, Context


>>> t = Template('Your name is {{ name }}.')
>>> t.render(Context())
'Your name is .'
>>> t.render(Context({'var': 'hello'}))
'Your name is .'
>>> t.render(Context({'NAME': 'hello'}))
'Your name is .'
>>> t.render(Context({'Name': 'hello'}))
'Your name is .'

The syst em fails silent ly rat her t han raising an except ion because it ’s int ended t o be resilient t o hum an error. I n t he real world, it ’s
unaccept able for a Web sit e t o becom e inaccessible due t o a sm all t em plat e synt ax error.
file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH4.html (9 of 34)9/28/2007 2:09:26 PM
Chapter 4: The Django template system

Not e t hat it ’s possible t o change Dj ango’s default behavior in t his regard, by t weaking a set t ing in your Dj ango configurat ion. We’ll
discuss t his in Chapt er 10, “ Ext ending t he t em plat e engine.”

Playing with Context obj ects


Most of t he t im e, you’ll inst ant iat e Context obj ect s by passing in a fully- populat ed dict ionary t o Context(). But you can add and
delet e it em s from a Context obj ect once it ’s been inst ant iat ed, t oo, using st andard Pyt hon dict ionary synt ax:

>>> from django.template import Context


>>> c = Context({"foo": "bar"})
>>> c['foo']
'bar'
>>> del c['foo']
>>> c['foo']
''
>>> c['newvariable'] = 'hello'
>>> c['newvariable']
'hello'

A Context obj ect is a st ack. That is, you can push() and pop() it . I f you pop() t oo m uch, it ’ll
raise django.template.ContextPopException:

>>> c = Context()
>>> c['foo'] = 'first level'
>>> c.push()
>>> c['foo'] = 'second level'
>>> c['foo']
'second level'
>>> c.pop()
>>> c['foo']
'first level'
>>> c['foo'] = 'overwritten'
>>> c['foo']
'overwritten'
>>> c.pop()
Traceback (most recent call last):
...
django.template.ContextPopException

Using a Context as a st ack com es in handy in som e cust om t em plat e t ags, as you’ll see in Chapt er 10.

Basic t emplat e t ags and f ilt ers


As we’ve m ent ioned already, t he t em plat e syst em ships wit h built - in t ags and filt ers. Here’s a rundown of t he m ost com m on ones.
file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH4.html (10 of 34)9/28/2007 2:09:26 PM
Chapter 4: The Django template system

Appendix 6 includes a full list of all built - in t ags and filt ers, and it ’s a good idea t o fam iliarize yourself wit h t hat list t o have an idea
of what ’s possible.

if/ else
The {% if %} t ag evaluat es a variable, and if t hat variable is “ t rue” ( i.e., it exist s, is not em pt y, and is not a false boolean value) ,
t he syst em will display everyt hing bet ween {% if %} and {% endif %}. For exam ple:

{% if today_is_weekend %}
<p>Welcome to the weekend!</p>
{% endif %}

An {% else %} t ag is opt ional:

{% if today_is_weekend %}
<p>Welcome to the weekend!</p>
{% else %}
<p>Get back to work.</p>
{% endif %}

The {% if %} t ag accept s and, or or not for t est ing m ult iple variables, or t o negat e a given variable. For exam ple:

{% if athlete_list and coach_list %}


Both athletes and coaches are available.
{% endif %}
{% if not athlete_list %}
There are no athletes.
{% endif %}
{% if athlete_list or coach_list %}
There are some athletes or some coaches.
{% endif %}
{% if not athlete_list or coach_list %}
There are no athletes or there are some coaches (OK, so
writing English translations of boolean logic sounds
stupid; it's not our fault).
{% endif %}
{% if athlete_list and not coach_list %}
There are some athletes and absolutely no coaches.
{% endif %}

{% if %} t ags don’t allow and and or clauses wit hin t he sam e t ag, because t he order of logic would be am biguous. For exam ple,
file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH4.html (11 of 34)9/28/2007 2:09:26 PM
Chapter 4: The Django template system

t his is invalid:

{% if athlete_list and coach_list or cheerleader_list %}

I f you need t o com bine and and or t o do advanced logic, j ust use nest ed {% if %} t ags. For exam ple:

{% if athlete_list %}
{% if coach_list or cheerleader_list %}
We have athletes, and either coaches or cheerleaders!
{% endif %}
{% endif %}

Mult iple uses of t he sam e logical operat or are fine, as long as you use t he sam e operat or. For exam ple, t his is valid:

{% if athlete_list or coach_list or parent_list or teacher_list %}

There is no {% elif %} t ag. Use nest ed {% if %} t ags t o accom plish t he sam e t hing:

{% if athlete_list %}
<p>Here are the athletes: {{ athlete_list }}.</p>
{% else %}
<p>No athletes are available.</p>
{% if coach_list %}
<p>Here are the coaches: {{ coach_list }}.</p>
{% endif %}
{% endif %}

Make sure t o close each {% if %} wit h an {% endif %}. Ot herwise, Dj ango will t hrow a TemplateSyntaxError.

for
The {% for %} t ag allows you t o loop over each it em in a sequence. As in Pyt hon’s for st at em ent , t he synt ax is for X in Y,
where Y is t he sequence t o loop over and X is t he nam e of t he variable t o use for a part icular cycle of t he loop. Each t im e t hrough
t he loop, t he t em plat e syst em will render everyt hing bet ween {% for %} and {% endfor %}.

For exam ple, t o display a list of at hlet es given a variable athlete_list:

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

file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH4.html (12 of 34)9/28/2007 2:09:26 PM


Chapter 4: The Django template system

Add reversed t o t he t ag t o loop over t he list in reverse:

{% for athlete in athlete_list reversed %}


...
{% endfor %}

I t ’s possible t o nest {% for %} t ags:

{% for country in countries %}


<h1>{{ country.name }}</h1>
<ul>
{% for city in country.city_list %}
<li>{{ city }}</li>
{% endfor %}
</ul>
{% endfor %}

There is no support for “ breaking” out of a loop before t he loop is finished. I f you want t o accom plish t his, change t he variable
you’re looping over so t hat it only includes t he values you want t o loop over. Sim ilarly, t here is no support for a “ cont inue”
st at em ent t hat would inst ruct t he loop processor t o ret urn im m ediat ely t o t he front of t he loop. ( See “ Philosophies and lim it at ions”
lat er in t his chapt er for t he reasoning behind t his design decision.)

The {% for %} t ag set s a m agic forloop t em plat e variable wit hin t he loop. This variable has a few at t ribut es t hat give you
inform at ion about t he progress of t he loop:

● forloop.counter is always set t o an int eger represent ing t he num ber of t im es t he loop has been ent ered. This is one- indexed,
so t he first t im e t hrough t he loop, forloop.counter will be set t o 1. Exam ple:

{% for item in todo_list %}


<p>{{ forloop.counter }}: {{ item }}</p>
{% endfor %}

● forloop.counter0 is like forloop.counter, except it ’s zero- indexed. I t s value will be set t o 0 t he first t im e t hrough t he loop.

● forloop.revcounter is always set t o an int eger represent ing t he num ber of rem aining it em s in t he loop. The first t im e
t hrough t he loop, forloop.revcounter will be set t o t he t ot al num ber of it em s in t he sequence you’re t raversing. The last
t im e t hrough t he loop, forloop.revcounter will be set t o 1.

● forloop.revcounter0 is like forloop.revcounter, except it ’s zero- indexed. The first t im e t hrough t he


loop, forloop.revcounter0 will be set t o t he num ber of elem ent s in t he sequence m inus one. The last t im e t hrough t he loop,
it will be set t o 0.

● forloop.first is a boolean value set t o True if t his is t he first t im e t hrough t he loop. This is convenient for special- casing:

file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH4.html (13 of 34)9/28/2007 2:09:26 PM


Chapter 4: The Django template system

{% for object in objects %}


{% if forloop.first %}<li class="first">{% else %}<li>{% endif %}
{{ object }}
</li>
{% endfor %}

● forloop.last is a boolean value set t o True if t his is t he last t im e t hrough t he loop. An exam ple use for t his would be t o put
pipe charact ers bet ween a list of links:

{% for link in links %}{{ link }}{% if not forloop.last %} | {% endif %}{% endfor %}

● forloop.parentloop is a reference t o t he forloop obj ect for t he parent loop, in case of nest ed loops. For exam ple:

{% for country in countries %}


<table>
{% for city in country.city_list %}
<tr>
<td>Country #{{ forloop.parentloop.counter }}</td>
<td>City #{{ forloop.counter }}</td>
<td>{{ city }}</td>
</tr>
{% endfor %}
</table>
{% endfor %}

The m agic forloop variable is only available wit hin loops. Aft er t he t em plat e parser has reached {% endfor %}, forloop
disappears.

I f your t em plat e cont ext already cont ains a variable called forloop, Dj ango will override it wit hin {% for %} t ags. I n ot her, non-
loop part s of t he t em plat e, your forloop will st ill be available and unchanged. We advise against set t ing t em plat e variables wit h
t he nam e forloop, but if you need t o do t his and want t o access your cust om forloop from wit hin a {% for %} t ag, you can
use forloop.parentloop, described above.

ifequal/ ifnotequal
The Dj ango t em plat e syst em deliberat ely is not a full- fledged program m ing language and, t hus, does not allow you t o execut e
arbit rary Pyt hon st at em ent s. ( More on t his in “ Philosophies and lim it at ions” below.) However, it ’s quit e a com m on t em plat e
requirem ent t o com pare t wo values and display som et hing if t hey’re equal — and Dj ango provides an {% ifequal %} t ag for t hat
purpose.

The {% ifequal %} t ag com pares t wo values and displays everyt hing bet ween {% ifequal %} and {% endifequal %} if t he values
are equal.

This exam ple com pares t he t em plat e variables user and currentuser:

file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH4.html (14 of 34)9/28/2007 2:09:26 PM


Chapter 4: The Django template system

{% ifequal user currentuser %}


<h1>Welcome!</h1>
{% endifequal %}

The argum ent s can be hard- coded st rings, wit h eit her single or double quot es, so t he following is valid:

{% ifequal section 'sitenews' %}


<h1>Site News</h1>
{% endifequal %}
{% ifequal section "community" %}
<h1>Community</h1>
{% endifequal %}

Just like {% if %}, t he {% ifequal %} t ag support s an opt ional {% else %}:

{% ifequal section 'sitenews' %}


<h1>Site News</h1>
{% else %}
<h1>No News Here</h1>
{% endifequal %}

Only t em plat e variables, st rings, int egers and decim al num bers are allowed as argum ent s t o {% ifequal %}. These are valid
exam ples:

{% ifequal variable 1 %}
{% ifequal variable 1.23 %}
{% ifequal variable 'foo' %}
{% ifequal variable "foo" %}

Any ot her t ypes of variables, such as Pyt hon dict ionaries, list s or booleans, can not be hard- coded in {% ifequal %}. These are
invalid exam ples:

{% ifequal variable True %}


{% ifequal variable [1, 2, 3] %}
{% ifequal variable {'key': 'value'} %}

I f you need t o t est whet her som et hing is t rue or false, use t he {% if %} t ags inst ead of {% ifequal %}.

Comments
Just as in HTML or in a program m ing language such as Pyt hon, t he Dj ango t em plat e language allows for com m ent s. To designat e a
com m ent , use {# #}. For exam ple:

file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH4.html (15 of 34)9/28/2007 2:09:26 PM


Chapter 4: The Django template system

{# This is a comment #}

Com m ent will not be out put when t he t em plat e is rendered.

A com m ent cannot span m ult iple lines. I n t he following t em plat e, t he rendered out put will look exact ly t he sam e as t he t em plat e ( i.
e., t he com m ent t ag will not be parsed as a com m ent ) :

This is a {# comment goes here


and spans another line #}
test.

Filters
As explained earlier in t his chapt er, t em plat e filt ers are sim ple ways of alt ering t he value of variables before t hey’re displayed.

Filt ers look like t his:

{{ name|lower }}

This displays t he value of t he {{ name }} variable aft er being filt ered t hrough t he lower filt er, which convert s t ext t o lowercase.
Use a pipe ( |) t o apply a filt er.

Filt ers can be chained — t hat is, t he out put of one filt er is applied t o t he next . Here’s a com m on idiom for escaping t ext cont ent s,
t hen convert ing line breaks t o <p> t ags:

{{ my_text|escape|linebreaks }}

Som e filt ers t ake argum ent s. A filt er argum ent looks like t his:

{{ bio|truncatewords:"30" }}

This displays t he first 30 words of t he bio variable. Filt er argum ent s always are in double quot es.

Here are a few of t he m ost im port ant filt ers:

● addslashes — Adds a backslash before any backslash, single quot e or double quot e. This is useful if you’re out put t ing som e
t ext int o a JavaScript st ring.

● date — Form at s a date or datetime obj ect according t o a form at st ring given in t he param et er. For exam ple:

{{ pub_date|date:"F j, Y" }}

file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH4.html (16 of 34)9/28/2007 2:09:26 PM


Chapter 4: The Django template system

Form at st rings are defined in Appendix 6.

● escape — Escapes am persands, quot es and angle bracket s in t he given st ring. This is useful for sanit izing user- subm it t ed dat a
and for ensuring dat a is valid XML or XHTML. Specifically, escape m akes t hese conversions:

❍ Convert s & t o &amp;


❍ Convert s < t o &lt;
❍ Convert s > t o &gt;
❍ Convert s " ( double quot e) t o &quot;
❍ Convert s ' ( single quot e) t o &#39;

● length — Ret urns t he lengt h of t he value. You can use t his on a list or a st ring, or any Pyt hon obj ect t hat knows how t o
det erm ine it s lengt h ( i.e., any obj ect t hat has a __len__() m et hod) .

Philosophies and limit at ions


Now t hat you’ve got t en a feel for t he Dj ango t em plat e language, we should point out som e of it s int ent ional lim it at ions, along wit h
som e philosophies on why it works t he way it works.

More t han any ot her com ponent of Web applicat ions, program m er opinions on t em plat e syst em s vary wildly — a st at em ent
support ed by t he fact t hat Pyt hon alone has dozens, if not hundreds, of open- source t em plat e- language im plem ent at ions, each
inevit ably creat ed because it s developer deem ed all exist ing t em plat e languages inadequat e. ( I n fact , it is said t o be a rit e of
passage for a Pyt hon developer t o writ e his or her own t em plat e language! And if you haven’t done t his yet , consider it . I t ’s a fun
exercise.)

Wit h t hat in m ind, t he first Dj ango philosophy t o point out is t hat Dj ango doesn’t require t hat you use it s t em plat e language.
Because Dj ango is int ended t o be a full- st ack Web fram ework t hat provides all t he pieces necessary t o be a product ive Web
developer, m any t im es it ’s m ore convenient t o use Dj ango’s t em plat e syst em t han ot her Pyt hon t em plat e libraries, but it ’s not a
st rict requirem ent in any sense. As we’ll see in t he sect ion “ Using t em plat es in views” below, it ’s very easy t o use anot her t em plat e
language wit h Dj ango — alm ost as easy as t o use Dj ango’s t em plat e language.

St ill, it ’s clear we have a st rong preference for t he way Dj ango’s t em plat e language works. The t em plat e syst em has root s in how
Web developm ent is done at World Online and t he com bined experience of Dj ango’s creat ors. Here are a few of t hose philosophies:

● Bu sin e ss logic sh ou ld be se pa r a t e d fr om pr e se n t a t ion logic. We see a t em plat e syst em as a t ool t hat cont rols
present at ion and present at ion- relat ed logic — and t hat ’s it . The t em plat e syst em shouldn’t support funct ionalit y t hat goes
beyond t his basic goal.

For t hat reason, it ’s im possible t o call Pyt hon code direct ly wit hin Dj ango t em plat es. All “ program m ing” is fundam ent ally
lim it ed t o t he scope of what t em plat e t ags can do. I t is possible t o writ e cust om t em plat e t ags t hat do arbit rary t hings, but t he
out - of- t he- box Dj ango t em plat e t ags int ent ionally do not allow for arbit rary Pyt hon code execut ion.

● Syn t a x sh ou ld be de cou ple d fr om H TM L/ XM L. Alt hough Dj ango’s t em plat e syst em is used prim arily t o out put HTML, it ’s
int ended t o be j ust as usable for non- HTML form at s, such as plain t ext . Som e ot her t em plat e languages are XML- based,
placing all t em plat e logic wit hin XML t ags or at t ribut es, but Dj ango deliberat ely avoids t his lim it at ion. Requiring valid XML t o
writ e t em plat es int roduces a world of hum an m ist akes and hard- t o- underst and error m essages, and using an XML engine t o
parse t em plat es incurs an unaccept able level of overhead in t em plat e processing.

file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH4.html (17 of 34)9/28/2007 2:09:26 PM


Chapter 4: The Django template system

● D e sign e r s a r e a ssu m e d t o be com for t a ble w it h H TM L code . The t em plat e syst em isn’t designed so t hat t em plat es
necessarily are displayed nicely in WYSI WYG edit ors such as Dream weaver. That is t oo severe of a lim it at ion and wouldn’t
allow t he synt ax t o be as nice as it is. Dj ango expect s t em plat e aut hors are com fort able edit ing HTML direct ly.

● D e sign e r s a r e a ssu m e d n ot t o be Pyt h on pr ogr a m m e r s. The t em plat e syst em aut hors recognize t hat Web page
t em plat es are m ost oft en writ t en by designers, not program m ers, and t herefore should not assum e Pyt hon knowledge.

However, t he syst em also int ends t o accom odat e sm all t eam s in which t he t em plat es are creat ed by Pyt hon program m ers. I t
offers a way t o ext end t he syst em ’s synt ax by writ ing raw Pyt hon code. ( More on t his in Chapt er 10.)

● Th e goa l is n ot t o in ve n t a pr ogr a m m in g la n gu a ge . The goal is t o offer j ust enough program m ing- esque funct ionalit y,
such as branching and looping, t hat is essent ial for m aking present at ion- relat ed decisions.

As a result of t hese design philosophies, t he Dj ango t em plat e language has t he following lim it at ions:

● A t e m pla t e ca n n ot se t a va r ia ble or ch a n ge t h e va lu e of a va r ia ble . I t ’s possible t o writ e cust om t em plat e t ags t hat


accom plish t hese goals ( see Chapt er 10) , but t he st ock Dj ango t em plat e t ags do not allow it .
● A t e m pla t e ca n n ot ca ll r a w Pyt h on code . There’s no way t o “ drop int o Pyt hon m ode” or use raw Pyt hon const ruct s. Again,
it ’s possible t o writ e cust om t em plat e t ags t o do t his, but t he st ock Dj ango t em plat e t ags don’t allow it .

Using t emplat es in views


We’ve learned t he basics of using t he t em plat e syst em ; now, let ’s int egrat e t his int o a view. Recall t he current_datetime view
from t he previous chapt er. Here’s what it looked like:

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)

Let ’s change t his view t o use Dj ango’s t em plat e syst em . At first , you m ight t hink t o do som et hing like t his:

from django.template import Template, Context


from django.http import HttpResponse
import datetime
def current_datetime(request):
now = datetime.datetime.now()
t = Template("<html><body>It is now {{ current_date }}.</body></html>")
html = t.render(Context({'current_date': now}))
return HttpResponse(html)

Sure, t hat uses t he t em plat e syst em , but it doesn’t solve t he problem s we point ed out in t he int roduct ion of t his chapt er. Nam ely,
file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH4.html (18 of 34)9/28/2007 2:09:26 PM
Chapter 4: The Django template system

t he t em plat e is st ill em bedded in t he Pyt hon code. Let ’s fix t hat by put t ing t he t em plat e in a separat e file, which t his view will load.

The sim ple, “ dum b” way t o do t his would be t o save your t em plat e som ewhere on your filesyst em and use Pyt hon’s built - in file-
opening funct ionalit y t o read t he cont ent s of t he t em plat e. Here’s what t hat m ight look like, assum ing t he t em plat e was saved as
t he file /home/djangouser/templates/mytemplate.html:

from django.template import Template, Context


from django.http import HttpResponse
import datetime
def current_datetime(request):
now = datetime.datetime.now()
# Simple, "dumb" way of saving templates on the filesystem.
# This doesn't account for missing files!
fp = open('/home/djangouser/templates/mytemplate.html')
t = Template(fp.read())
fp.close()
html = t.render(Context({'current_date': now}))
return HttpResponse(html)

This approach, however, is inelegant for t hese reasons:

● For one, it doesn’t handle t he case of a m issing file. I f t he file mytemplate.html doesn’t exist or isn’t readable, t he open() call
would raise an IOError except ion.
● Second, it hard- codes your t em plat e locat ion. I f you were t o use t his t echnique for every view funct ion, you’d be duplicat ing
t he t em plat e locat ions. Not t o m ent ion t hat ’s a lot of t yping!
● Third, it includes a lot of boring boilerplat e code. The calls t o open(), fp.read() and fp.close() require a lot of t yping and
not m uch creat ivit y.

To solve t hese issues, we’ll use t em plat e loading and t em plat e direct ories.

Templat e loading
Dj ango provides a convenient and powerful API for loading t em plat es from disk, wit h t he goal of rem oving redundancy bot h in your
t em plat e- loading calls and in your t em plat es t hem selves.

I n order t o use t his t em plat e- loading API , first you’ll need t o t ell t he fram ework where you st ore your t em plat es. The place t o do
t his is in your se t t in gs file .

A Dj ango set t ings file is t he place t o put configurat ion for your Dj ango inst ance ( aka your Dj ango proj ect ) . I t ’s a sim ple Pyt hon
m odule wit h m odule- level variables, one for each set t ing.

When you ran django-admin.py startproject mysite in Chapt er 2, t he script creat ed a default set t ings file for you, apt ly
nam ed settings.py. Have a look at t he file’s cont ent s. I t cont ains variables t hat look like t his ( t hough not necessarily in t his
order) :

file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH4.html (19 of 34)9/28/2007 2:09:26 PM


Chapter 4: The Django template system

DEBUG = True
TIME_ZONE = 'America/Chicago'
USE_I18N = True
ROOT_URLCONF = 'mysite.urls'

This is pret t y self- explanat ory; t he set t ings and t heir respect ive values are sim ple Pyt hon variables. And because t he set t ings file is
j ust a plain Pyt hon m odule, you can do dynam ic t hings such as checking t he value of one variable before set t ing anot her. ( This also
m eans t hat you should avoid Pyt hon synt ax errors in your set t ings file.)

We’ll cover set t ings files in dept h lat er in t his book, but for now, have a look at t he TEMPLATE_DIRS set t ing. This set t ing t ells
Dj ango’s t em plat e loading m echanism where t o look for t em plat es. By default , it ’s an em pt y t uple. Pick a direct ory where you’d like
t o st ore your t em plat es, and add it t o TEMPLATE_DIRS, like so:

TEMPLATE_DIRS = (
'/home/django/mysite/templates',
)

A few t hings t o not e:

● You can specify any direct ory you want , as long as t he direct ory and t em plat es wit hin t hat direct ory are readable by t he user
account under which your Web server runs. I f you can’t t hink of an obvious place t o put your t em plat es, we recom m end
creat ing a templates direct ory wit hin your Dj ango proj ect ( i.e., wit hin t he mysite direct ory you creat ed in Chapt er 2, if you’ve
been following along wit h our exam ples) .

● Don’t forget t he com m a at t he end of t he t em plat e- direct ory st ring! Pyt hon requires com m as wit hin single- elem ent t uples t o
disam biguat e t he t uple from a parent het ical st at em ent . This is a com m on newbie got cha.

I f you want t o avoid t his error, you can m ake TEMPLATE_DIRS a list inst ead of a t uple, because single- elem ent list s don’t
require a t railing com m a:

TEMPLATE_DIRS = [
'/home/django/mysite/templates'
]

A t uple is slight ly m ore efficient t han a list , t hough, so we recom m end using a t uple for your TEMPLATE_DIRS set t ing.

● I t ’s sim plest t o use absolut e pat hs, i.e. direct ory pat hs t hat st art at t he root of t he filesyst em . I f you want t o be a bit m ore
flexible and decoupled, t hough, you can t ake advant age of t he fact t hat Dj ango set t ings files are j ust Pyt hon code by
const ruct ing t he cont ent s of TEMPLATE_DIRS dynam ically. For exam ple:

import os.path
TEMPLATE_DIRS = (
os.path.join(os.path.basename(__file__), 'templates'),
)

file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH4.html (20 of 34)9/28/2007 2:09:26 PM


Chapter 4: The Django template system

This exam ple uses t he “ m agic” Pyt hon variable __file__, which is aut om at ically set t o t he filenam e of t he Pyt hon m odule in
which t he code lives.

● I f you’re on Windows, include your drive let t er and use Unix- st yle forward slashes rat her t han backslashes. For exam ple:

TEMPLATE_DIRS = (
'C:/www/django/templates',
)

Wit h TEMPLATE_DIRS set , t he next st ep is t o change t he view code t o use Dj ango’s t em plat e- loading funct ionalit y rat her t han hard-
coding t he t em plat e pat hs. Ret urning t o our current_datetime view, let ’s change it like so:

from django.template.loader import get_template


from django.template import Context
from django.http import HttpResponse
import datetime
def current_datetime(request):
now = datetime.datetime.now()
t = get_template('current_datetime.html')
html = t.render(Context({'current_date': now}))
return HttpResponse(html)

I n t his exam ple, we’re using t he funct ion django.template.loader.get_template() rat her t han loading t he t em plat e from t he
filesyst em m anually. The get_template() funct ion t akes a t em plat e nam e as it s argum ent , figures out where t he t em plat e lives on
t he filesyst em , opens t hat file and ret urns a com piled Template obj ect .

I f get_template() cannot find t he t em plat e wit h t he given nam e, it raises a TemplateDoesNotExist except ion. To see what t hat
looks like, fire up t he Dj ango developm ent server again, as in Chapt er 3, by running python manage.py runserver wit hin your
Dj ango proj ect ’s direct ory. Then, point your browser at t he page t hat act ivat es t he current_datetime view ( e.
g., http://127.0.0.1:8000/now/) . Assum ing your DEBUG set t ing is set t o True and you haven’t yet creat ed
a current_datetime.html t em plat e, you should see a Dj ango error page highlight ing t he TemplateDoesNotExist error.

( We’ll have a screenshot here.)

This error page is sim ilar t o t he one we explained in Chapt er 3, wit h one addit ional piece of debugging inform at ion: a “ Tem plat e-
loader post m ort em ” sect ion. This sect ion t ells you which t em plat es Dj ango t ried t o load, along wit h t he reason each at t em pt failed
( e.g., “ File does not exist ” ) . This inform at ion is invaluable when you’re t rying t o debug t em plat e- loading errors.

As you can probably t ell by looking at t he error m essages, Dj ango at t em pt ed t o look for a t em plat e by com bining t he direct ory in
your TEMPLATE_DIRS set t ing wit h t he t em plat e nam e you passed t o get_template(). So if your TEMPLATE_DIRS
cont ained '/home/django/templates', it would look for t he file '/home/django/templates/current_datetime.html'.

Moving along, creat e t he current_datetime.html file wit hin your t em plat e direct ory, using t he following t em plat e code:

file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH4.html (21 of 34)9/28/2007 2:09:26 PM


Chapter 4: The Django template system

<html><body>It is now {{ current_date }}.</body></html>

Refresh t he page in your Web browser, and you should see t he fully rendered page.

render_to_response()
Because it ’s such a com m on idiom t o load a t em plat e, fill a Context and ret urn an HttpResponse obj ect wit h t he result of t he
rendered t em plat e, Dj ango provides a short cut t hat let s you do t hose t hings in one line of code. This short cut is a funct ion
called render_to_response(), which lives in t he m odule django.shortcuts. Most of t he t im e, you’ll be
using render_to_response() rat her t han loading t em plat es and creat ing Context and HttpResponse obj ect s m anually.

Here’s t he ongoing current_datetime exam ple rewrit t en t o use render_to_response():

from django.shortcuts import render_to_response


import datetime
def current_datetime(request):
now = datetime.datetime.now()
return render_to_response('current_datetime.html', {'current_date': now})

What a difference! Let ’s st ep t hrough t he code changes:

● We no longer have t o im port get_template, Template, Context or HttpResponse. I nst ead, we


im port django.shortcuts.render_to_response. The import datetime rem ains.
● Wit hin t he current_datetime funct ion, we st ill calculat e now, but t he t em plat e loading, cont ext creat ion, t em plat e rendering
and HttpResponse creat ion is all t aken care of by t he render_to_response() call. Because render_to_response() ret urns
an HttpResponse obj ect , we can sim ply return t hat value in t he view.

The first argum ent t o render_to_response() should be t he nam e of t he t em plat e t o use, relat ive t o your t em plat e direct ory. The
second argum ent , if given, should be a dict ionary t o use in creat ing a Context for t hat t em plat e. I f you don’t provide a second
argum ent , render_to_response() will use an em pt y dict ionary.

The locals() trick


Consider our lat est incarnat ion of current_datetime:

def current_datetime(request):
now = datetime.datetime.now()
return render_to_response('current_datetime.html', {'current_date': now})

Many t im es, as in t his exam ple, you’ll find yourself calculat ing som e values, st oring t hem in variables ( e.g., now above) and sending
t hose variables t o t he t em plat e. Part icularly lazy program m ers would not e t hat it ’s slight ly redundant t o have t o give nam es for
t em porary variables and give nam es for t he t em plat e variables. Not only is it redundant ; it ’s ext ra t yping.

So if you’re one of t hose lazy program m ers and you like keeping code part icularly concise, you can t ake advant age of a built - in
file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH4.html (22 of 34)9/28/2007 2:09:26 PM
Chapter 4: The Django template system

Pyt hon funct ion called locals(). locals() ret urns a dict ionary of all variables defined wit hin t he local scope, along wit h t heir
values. Thus, t he above view could be rewrit t en like so:

def current_datetime(request):
current_date = datetime.datetime.now()
return render_to_response('current_datetime.html', locals())

Here, inst ead of m anually specifying t he cont ext dict ionary as before, we inst ead pass t he value of locals(), which will include all
variables defined at t hat point in t he funct ion’s execut ion. As a consequence, we’ve renam ed t he now variable t o current_date,
because t hat ’s t he variable nam e t hat t he t em plat e expect s. I n t his exam ple, locals() doesn’t offer a huge im provem ent , but t his
t echnique can save you som e t yping if you’ve got several t em plat e variables t o define — or if you’re lazy.

One t hing t o wat ch out for when using locals() is t hat it includes every local variable, which m ay com prise m ore variables t han
you act ually want your t em plat e t o have access t o. I n t he above exam ple, locals() will also include request. Whet her t his
m at t ers t o you depends on your applicat ion.

A final t hing t o consider is t hat locals() incurs a sm all bit of overhead, because when you call it , Pyt hon has t o creat e t he
dict ionary dynam ically. I f you specify t he cont ext dict ionary m anually, you avoid t his overhead.

Subdirectories in get_template()
I t can get unwieldy t o st ore all of your t em plat es in a single direct ory. You m ight like t o st ore t em plat es in subdirect ories of your
t em plat e direct ory, and t hat ’s fine. ( I n fact , we’d recom m end it , and som e m ore advanced Dj ango feat ures, such as t he generic
views syst em we’ll cover in Chapt er 9, expect t his t em plat e layout as a default convent ion.)

Accom plishing t hat is easy. I n your calls t o get_template(), j ust include t he subdirect ory nam e and a slash before t he t em plat e
nam e, like so:

t = get_template('dateapp/current_datetime.html')

Because render_to_response() is a sm all wrapper around get_template(), you can do t he sam e t hing wit h t he first argum ent
t o render_to_response().

There’s no lim it t o t he dept h of your subdirect ory t ree. Feel free t o use subdirect ories of subdirect ories of subdirect ories.

Windows users, not e: Make sure t o use forward slashes rat her t han backslashes. get_template() assum es a Unix- st yle filenam e
designat ion.

The include template tag


Now t hat we’ve covered t he t em plat e loading m echanism , we can int roduce a built - in t em plat e t ag t hat t akes advant age of
it : {% include %}. This t ag allows you t o include t he cont ent s of anot her t em plat e. The argum ent t o t he t ag should be t he nam e of
t he t em plat e t o include, and t he t em plat e nam e can be eit her a variable or a hard- coded ( quot ed) st ring, in eit her single or double
quot es.

These t wo exam ples include t he cont ent s of t he t em plat e nav.html. The exam ples are equivalent and illust rat e t hat eit her single or
double quot es are allowed:
file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH4.html (23 of 34)9/28/2007 2:09:26 PM
Chapter 4: The Django template system

{% include 'nav.html' %}
{% include "nav.html" %}

This exam ple includes t he cont ent s of t he t em plat e includes/nav.html:

{% include 'includes/nav.html' %}

This exam ple includes t he cont ent s of t he t em plat e whose nam e is cont ained in t he variable template_name:

{% include template_name %}

As in get_template(), t he filenam e of t he t em plat e is det erm ined by adding t he t em plat e direct ory from TEMPLATE_DIRS t o t he
request ed t em plat e nam e.

I f an included t em plat e cont ains any t em plat e code — such as t ags or variables — t hen it will get evaluat ed wit h t he cont ext of t he
t em plat e t hat ’s including it .

I f a t em plat e wit h t he given nam e isn’t found, Dj ango will do one of t wo t hings:

● I f your DEBUG set t ing is set t o True, you’ll see t he TemplateDoesNotExist except ion on a Dj ango error page.
● I f your DEBUG set t ing is set t o False, t he t ag will fail silent ly, displaying not hing in t he place of t he t ag.

Templat e inherit ance


Our t em plat e exam ples so far have been t iny HTML snippet s, but in t he real world, you’ll be using Dj ango’s t em plat e syst em t o
out put ent ire HTML pages. This leads t o a com m on Web developm ent problem : Across a Web sit e, how does one reduce t he
duplicat ion and redundancy of com m on page areas, such as sit ewide navigat ion?

A classic way of solving t his problem is t o use server- side includes, direct ives you can em bed wit hin your HTML pages t o “ include”
one Web page inside anot her. I ndeed, Dj ango support s t hat approach, wit h t he {% include %} t em plat e t ag we described above.
But t he preferred way of solving t his problem wit h Dj ango is t o use a m ore elegant st rat egy called t e m pla t e in h e r it a n ce .

I n essence, t em plat e inherit ance let s you build a base “ skelet on” t em plat e t hat cont ains all t he com m on part s of your sit e and
defines “ blocks” t hat child t em plat es can override.

Let ’s see an exam ple of t his by creat ing a m ore com plet e t em plat e for our current_datetime view, by edit ing
t he current_datetime.html file:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">


<html lang="en">
<head>
<title>The current time</title>
</head>
file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH4.html (24 of 34)9/28/2007 2:09:26 PM
Chapter 4: The Django template system

<body>
<h1>My helpful timestamp site</h1>
<p>It is now {{ current_date }}.</p>
<hr>
<p>Thanks for visiting my site.</p>
</body>
</html>

That looks j ust fine, but what happens when we want t o creat e a t em plat e for anot her view — say, t he hours_ahead view from
Chapt er 3? I f we want again t o m ake a nice, valid, full HTML t em plat e, we’d creat e som et hing like:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">


<html lang="en">
<head>
<title>Future time</title>
</head>
<body>
<h1>My helpful timestamp site</h1>
<p>In {{ hour_offset }} hour(s), it will be {{ next_time }}.</p>
<hr>
<p>Thanks for visiting my site.</p>
</body>
</html>

Clearly, we’ve j ust duplicat ed a lot of HTML. I m agine if we had a few st ylesheet s included on every page, m aybe a navigat ion bar,
perhaps som e JavaScript … We’d end up put t ing all sort s of redundant HTML int o each t em plat e.

The server- side include solut ion t o t his problem would be t o fact or out t he com m on bit s in bot h t em plat es and save t hem in
separat e t em plat e snippet s, which would t hen be included in each t em plat e. Perhaps you’d st ore t he t op bit of t he t em plat e in a file
called header.html:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">


<html lang="en">
<head>

And perhaps you’d st ore t he bot t om bit in a file called footer.html:

<hr>
<p>Thanks for visiting my site.</p>
</body>
</html>

Wit h an include- based st rat egy, headers and foot ers are easy. But it ’s t he m iddle ground t hat ’s m essy. I n t his exam ple, bot h pages
file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH4.html (25 of 34)9/28/2007 2:09:26 PM
Chapter 4: The Django template system

feat ure a t it le — <h1>My helpful timestamp site</h1> — but t hat t it le can’t fit int o header.html because t he <title> on bot h
pages is different . I f we included t he <h1> in t he header, we’d have t o include t he <title>, which wouldn’t allow us t o cust om ize it
per page. See where t his is going?

Dj ango’s t em plat e inherit ance syst em solves t hese problem s. You can t hink of it as an “ inside out ” version of server- side includes.
I nst ead of defining t he snippet s t hat are com m on, you define t he snippet s t hat are different .

The first st ep is t o define a ba se t e m pla t e — a skelet on of your page t hat ch ild t e m pla t e s will lat er fill in. Here’s a base t em plat e
for our ongoing exam ple:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">


<html lang="en">
<head>
<title>{% block title %}{% endblock %}</title>
</head>
<body>
<h1>My helpful timestamp site</h1>
{% block content %}{% endblock %}
{% block footer %}
<hr>
<p>Thanks for visiting my site.</p>
{% endblock %}
</body>
</html>

This t em plat e, which we’ll call base.html, defines a sim ple HTML skelet on docum ent t hat we’ll use for all t he pages on t he sit e. I t ’s
t he j ob of child t em plat es t o override, or add t o, or leave alone t he cont ent s of t he blocks. ( I f you’re following along at hom e, save
t his file t o your t em plat e direct ory.)

We’re using a t em plat e t ag here t hat you haven’t seen before — t he {% block %} t ag. All t he {% block %} t ags do is t o t ell t he
t em plat e engine t hat a child t em plat e m ay override t hose port ions of t he t em plat e.

Now t hat we’ve got t his base t em plat e, we can m odify our exist ing current_datetime.html t em plat e t o use it :

{% extends "base.html" %}
{% block title %}The current time{% endblock %}
{% block content %}
<p>It is now {{ current_date }}.</p>
{% endblock %}

While we’re at it , let ’s creat e a t em plat e for t he hours_ahead view from Chapt er 3. ( I f you’re following along wit h code, we’ll leave
it up t o you t o change hours_ahead t o use t he t em plat e syst em .) Here’s what t hat would look like:

{% extends "base.html" %}
file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH4.html (26 of 34)9/28/2007 2:09:26 PM
Chapter 4: The Django template system

{% block title %}Future time{% endblock %}


{% block content %}
<p>In {{ hour_offset }} hour(s), it will be {{ next_time }}.</p>
{% endblock %}

I sn’t t his beaut iful? Each t em plat e cont ains only t he code t hat ’s unique t o t hat t em plat e. No redundancy needed. I f you need t o
m ake a sit ewide design change, j ust m ake t he change t o base.html, and all of t he ot her t em plat es will im m ediat ely reflect t he
change.

Here’s how it works:

● When you load t he t em plat e current_datetime.html, t he t em plat e engine sees t he {% extends %} t ag, not ing t hat t his
t em plat e is a child t em plat e. The engine im m ediat ely loads t he parent t em plat e — in t his case, base.html.

● At t hat point , t he t em plat e engine not ices t he t hree {% block %} t ags in base.html and replaces t hose blocks wit h t he
cont ent s of t he child t em plat e. So, t he t it le we’ve defined in {% block title %} will be used, as will t he {% block content %}
.

Not e t hat since t he child t em plat e doesn’t define t he footer block, t he t em plat e syst em uses t he value from t he parent
t em plat e inst ead. Cont ent wit hin a {% block %} t ag in a parent t em plat e is always used as a fallback.

You can use as m any levels of inherit ance as needed. One com m on way of using inherit ance is t he following t hree- level approach:

● Creat e a base.html t em plat e t hat holds t he m ain look- and- feel of your sit e. This is t he st uff t hat rarely, if ever, changes.
● Creat e a base_SECTION.html t em plat e for each “ sect ion” of your sit e. For exam ple, base_photos.html, base_forum.html.
These t em plat es all ext end base.html and include sect ion- specific st yles/ design.
● Creat e individual t em plat es for each t ype of page, such as a forum page or a phot o gallery. These t em plat es ext end t he
appropriat e sect ion t em plat e.

This approach m axim izes code reuse and m akes it easy t o add it em s t o shared areas, such as sect ion- wide navigat ion.

Here are som e t ips for working wit h t em plat e inherit ance:

● I f you use {% extends %} in a t em plat e, it m ust be t he first t em plat e t ag in t hat t em plat e. Ot herwise, t em plat e inherit ance
won’t work.
● Generally, t he m ore {% block %} t ags in your base t em plat es, t he bet t er. Rem em ber, child t em plat es don’t have t o define all
parent blocks, so you can fill in reasonable default s in a num ber of blocks, t hen only define t he ones you need in t he child
t em plat es. I t ’s bet t er t o have m ore hooks t han fewer hooks.
● I f you find yourself duplicat ing code in a num ber of t em plat es, it probably m eans you should m ove t hat code t o a {% block %}
in a parent t em plat e.
● I f you need t o get t he cont ent of t he block from t he parent t em plat e, t he {{ block.super }} variable will do t he t rick. This is
useful if you want t o add t o t he cont ent s of a parent block inst ead of com plet ely overriding it .
● You m ay not define m ult iple {% block %} t ags wit h t he sam e nam e in t he sam e t em plat e. This lim it at ion exist s because a
block t ag works in “ bot h” direct ions. That is, a block t ag doesn’t j ust provide a hole t o fill — it also defines t he cont ent t hat fills
file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH4.html (27 of 34)9/28/2007 2:09:26 PM
Chapter 4: The Django template system

t he hole in t he parent . I f t here were t wo sim ilarly- nam ed {% block %} t ags in a t em plat e, t hat t em plat e’s parent wouldn’t
know which one of t he blocks’ cont ent t o use.
● The t em plat e nam e you pass t o {% extends %} is loaded using t he sam e m et hod t hat get_template() uses. That is, t he
t em plat e nam e is appended t o your TEMPLATE_DIRS set t ing.
● I n m ost cases, t he argum ent t o {% extends %} will be a st ring, but it can also be a variable, if you don’t know t he nam e of
t he parent t em plat e unt il runt im e. This let s you do som e cool, dynam ic st uff.

Exercises
Here are a few exercises t hat will solidify som e of t he t hings you learned in t his chapt er. ( Hint : Even if you t hink you underst ood
everyt hing, at least give t hese exercises, and t heir respect ive answers, a read. We int roduce a couple of new t ricks here.)

1. You’ve got a list of m usicians and t he genre of m usic each one plays. This dat a is st ored as a list of dict ionaries, which will be
hard- coded in your view m odule. ( Usually we’d use a dat abase for t his, but we haven’t yet covered Dj ango’s dat abase layer.)
The list looks like t his:

MUSICIANS = [
{'name': 'Django Reinhardt', 'genre': 'jazz'},
{'name': 'Jimi Hendrix', 'genre': 'rock'},
{'name': 'Louis Armstrong', 'genre': 'jazz'},
{'name': 'Pete Townsend', 'genre': 'rock'},
{'name': 'Yanni', 'genre': 'new age'},
{'name': 'Ella Fitzgerald', 'genre': 'jazz'},
{'name': 'Wesley Willis', 'genre': 'casio'},
{'name': 'John Lennon', 'genre': 'rock'},
{'name': 'Bono', 'genre': 'rock'},
{'name': 'Garth Brooks', 'genre': 'country'},
{'name': 'Duke Ellington', 'genre': 'jazz'},
{'name': 'William Shatner', 'genre': 'spoken word'},
{'name': 'Madonna', 'genre': 'pop'},
]

Writ e a Dj ango view and corresponding t em plat e( s) t hat display an HTML <table> wit h a row for each m usician in t his list , in
order. Each row should have t wo colum ns: t he m usician’s nam e and t he t ype of m usic he/ she plays.

2. Once you’ve done t hat : For all t he m usicians who play j azz or rock — but not t he ot hers — bold t heir nam es by applying
a style="font-weight: bold;" t o t heir <td> cells.

3. Once you’ve done t hat : For all t he m usicians who have a one- word nam e — but not t he ot hers — display an ast erisk aft er t heir
nam e. Add a foot not e t o t he page t hat says “ * Pret ent ious.” Maint ain t he style="font-weight bold;" from t he previous
exercise.

4. Given t he following t hree t em plat es, devise a t em plat e- inherit ance schem e t hat rem oves as m uch redundancy as possible.

Tem plat e 1:

file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH4.html (28 of 34)9/28/2007 2:09:26 PM


Chapter 4: The Django template system

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">


<html lang="en">
<head>
<link rel="stylesheet" href="default.css" type="text/css">
<title>My to-do list</title>
</head>
<body>
<h1 id="top">Latest tasks</h1>
{% if task_list %}
<ul>
{% for task in task_list %}<li>{{ task }}</li>{% endfor %}
</ul>
{% else %}
<p>You have no tasks.</p>
{% endif %}
<hr>
<p><a href="#top">Back to top</a>.</p>
</body>
</html>

Tem plat e 2:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">


<html lang="en">
<head>
<title>Task: {{ task.title }} | To-do list</title>
<link rel="stylesheet" href="default.css" type="text/css">
</head>
<body>
<h1 id="top">{{ task.title }}</h1>
<p>{{ task.description }}</p>
<hr>
<p><a href="#top">Back to top</a>.</p>
</body>
</html>

Tem plat e 3:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">


<html lang="en">
<head>
<title>Completed tasks | To-do list</title>
<link rel="stylesheet" href="default.css" type="text/css">
<script type="text/javascript" src="completed.js">
</head>
file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH4.html (29 of 34)9/28/2007 2:09:26 PM
Chapter 4: The Django template system

<body>
<h1 id="top">{{ task.title }}</h1>
<p>{{ task.description }}</p>
<hr>
<p><a href="#top">Back to top</a>.</p>
</body>
</html>

Answers t o exercises
1. Here’s one possible im plem ent at ion of t he view:

from django.shortcuts import render_to_response


MUSICIANS = [
# ...
]
def musician_list(request):
return render_to_response('musician_list.html', {'musicians': MUSICIANS})

And here’s t he t em plat e:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">


<html lang="en">
<head>
<title>Musician list</title>
</head>
<body>
<table>
<tr><th>Musician</th><th>Genre</th></tr>
{% for musician in musicians %}
<tr>
<td>{{ musician.name }}</td>
<td>{{ musician.genre }}</td>
</tr>
{% endfor %}
</table>
</body>
</html>

2. A rat her clum sy way t o do t his would be t o use {% ifequal %} in t he t em plat e. The view would st ay t he sam e as in t he
answer t o t he last exercise, and t he t em plat e’s {% for %} loop would change t o som et hing like t his:

file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH4.html (30 of 34)9/28/2007 2:09:26 PM


Chapter 4: The Django template system

{% for musician in musicians %}


<tr>
<td {% ifequal musician.genre 'jazz' %}style="font-weight: bold;"{% endifequal %}
{% ifequal musician.genre 'rock' %}style="font-weight: bold;"{% endifequal %}>
{{ musician.name }}
</td>
<td>{{ musician.genre }}</td>
</tr>
{% endfor %}

This is overly verbose, repet it ive and error- prone — and it illust rat es an im port ant point . A key t o m ast ering Dj ango’s t em plat e
syst em is t o know what t o pass t o t he t em plat e. Because t em plat es do not have t he full program m ing- language power of an
environm ent you m ight be used t o, it ’s m ore im port ant in a Dj ango environm ent t o do as m uch business logic ( as opposed t o
present at ion logic) as possible in your view.

I n t his case, a cleaner way of solving t he problem would be t o have t he view precalculat e whet her a m usician’s nam e is
bolded. Aft er all, t his is business logic, not present at ion logic. The present at ion logic dict at es how t he special- case genres
should be displayed, not which genres are special- cased. This is an im port ant dist inct ion.

Here’s one way t o code t he view:

def musician_list(request):
musicians = []
for m in MUSICIANS:
musicians.append({
'name': m['name'],
'genre': m['genre'],
'is_important': m['genre'] in ('rock', 'jazz'),
})
return render_to_response('musician_list.html', {'musicians': musicians})

And wit h t hat view, you could use t his t em plat e code:

{% for musician in musicians %}


<tr>
<td{% if musician.is_important %} style="font-weight: bold;"{% endif %}>
{{ musician.name }}
</td>
<td>{{ musician.genre }}</td>
</tr>
{% endfor %}

See how m uch cleaner t hat is in t he t em plat e? Even t his is m ore com plex t han it usually will be, because usually you’ll be
dealing wit h dat abase obj ect s, and dat abase obj ect s can have cust om m et hods ( such as is_important()) . We’ll cover
dat abase obj ect s in t he next chapt er.

file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH4.html (31 of 34)9/28/2007 2:09:26 PM


Chapter 4: The Django template system

3. This is a sim ilar problem t o t he previous exercise, and t he solut ion is also sim ilar. The key is t o precalculat e whet her a
m usician deserves an ast erisk next t o his or her nam e. Because t hat ’s business logic, it belongs in t he view.

Here’s one possible view im plem ent at ion:

def musician_list(request):
musicians = []
for m in MUSICIANS:
musicians.append({
'name': m['name'],
'genre': m['genre'],
'is_important': m['genre'] in ('rock', 'jazz'),
'is_pretentious': ' ' not in m['name'],
})
return render_to_response('musician_list.html', {'musicians': musicians})

We’re using t he expression ' ' not in m['name'], which ret urns True if m['name'] doesn’t include a space. You could also
use t he .find() m et hod, like t his:

'is_pretentious': m['name'].find(' ') == -1

Not e t hat we’re calling t his variable is_pretentious rat her t han has_asterisk, because t he fact t hat we’re using ast erisks is
a present at ion decision.

Wit h t he above view, you could use t his t em plat e code:

{% for musician in musicians %}


<tr>
<td{% if musician.is_important %} style="font-weight: bold;"{% endif %}>
{% if musician.is_pretentious %}* {% endif %}{{ musician.name }}
</td>
<td>{{ musician.genre }}</td>
</tr>
{% endfor %}

Don’t forget t he “ * Pret ent ious.” at t he bot t om of t he t em plat e.

For bonus point s, be a perfect ionist and only display t he “ * Pret ent ious” foot not e if t here’s at least one pret ent ious m usician.
Det erm ine t he presence of a pret ent ious m usician in t he view, like so:

def musician_list(request):
musicians = []
has_pretentious = False
for m in MUSICIANS:
file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH4.html (32 of 34)9/28/2007 2:09:26 PM
Chapter 4: The Django template system

if ' ' not in m['name']:


has_pretentious = True
musicians.append({
'name': m['name'],
'genre': m['genre'],
'is_important': m['genre'] in ('rock', 'jazz'),
'is_pretentious': ' ' not in m['name'],
})
return render_to_response('musician_list.html', {
'musicians': musicians,
'has_pretentious': has_pretentious,
})

I n t his im plem ent at ion, we pass an ext ra t em plat e variable, has_pretentious, t o t he t em plat e. Then use it in t he t em plat e
like so:

{% if has_pretentious %}* Pretentious{% endif %}

4. Here’s one way t o writ e t he base t em plat e:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">


<html lang="en">
<head>
<link rel="stylesheet" href="default.css" type="text/css">
<title>{% block title %}{% endblock %}</title>
{% block extrahead %}{% endblock %}
</head>
<body>
<h1 id="top">{% block headline %}{% endblock %}</h1>
{% block content %}{% endblock %}
<hr>
<p><a href="#top">Back to top</a>.</p>
</body>
</html>

And, wit h t hat base t em plat e, here’s how t he child t em plat es m ight look.

Tem plat e 1:

{% extends "base.html" %}
{% block title %}My to-do list{% endblock %}
{% block headline %}Latest tasks{% endblock %}

file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH4.html (33 of 34)9/28/2007 2:09:26 PM


Chapter 4: The Django template system

{% block content %}
{% if task_list %}
<ul>
{% for task in task_list %}<li>{{ task }}</li>{% endfor %}
</ul>
{% else %}
<p>You have no tasks.</p>
{% endif %}
{% endblock %}

Tem plat e 2:

{% extends "base.html" %}
{% block title %}Task: {{ task.title }} | To-do list{% endblock %}
{% block headline %}{{ task.title }}{% endblock %}
{% block content %}<p>{{ task.description }}</p>{% endblock %}

Tem plat e 3:

{% extends "base.html" %}
{% block title %}Completed tasks | To-do list{% endblock %}
{% block extrahead %}<script type="text/javascript" src="completed.js">{% endblock %}
{% block headline %}{{ task.title }}{% endblock %}
{% block content %}<p>{{ task.description }}</p>{% endblock %}

Not e t hat we like t o put an em pt y line of space bet ween {% block %} sect ions, but t hat ’s j ust our personal st yle. Any t ext
out side of {% block %} t ags in child t em plat es will not be rendered.

« previous ◊ t able of cont ent s ◊ next »


Copyright 2006 Adrian Holovat y and Jacob Kaplan- Moss.
This work is licensed under t he GNU Free Docum ent License.

file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH4.html (34 of 34)9/28/2007 2:09:26 PM


Chapter 5: Interacting with a database: models

The Dj ango Book


« previous ◊ t able of cont ent s ◊ next »

Chapt er 5: Int eract ing wit h a dat abase: Models


I n Chapt er 3, we covered t he fundam ent als of building dynam ic Web sit es wit h Dj ango: set t ing up views and URLconfs. As we
explained, a view is responsible for doing som e arbit rary logic, t hen ret urning a response. I n t he exam ple, our arbit rary logic was
t o calculat e t he current dat e and t im e.

I n m odern Web applicat ions, t he arbit rary logic oft en involves int eract ing wit h a dat abase. Behind t he scenes, a da t a ba se -
dr ive n W e b sit e connect s t o a dat abase server, ret rieves som e dat a out of it and displays t hat dat a, nicely form at t ed, on a Web
page. Or, sim ilarly, t he sit e could provide funct ionalit y t hat let s sit e visit ors populat e t he dat abase on t heir own.

Many com plex Web sit es provide som e com binat ion of t he t wo. Am azon.com , for inst ance, is a great exam ple of a dat abase-
driven sit e. Each product page is essent ially an ext ract of Am azon’s product dat abase form at t ed as HTML, and when you post a
cust om er review, it get s insert ed int o t he dat abase of reviews.

Dj ango is very well- suit ed for m aking dat abase- driven Web sit es, as it com es wit h easy yet powerful ways of perform ing dat abase
queries using Pyt hon. This chapt er explains t hat funct ionalit y — Dj ango’s dat abase layer.

( Not e: While it ’s not st rict ly necessary t o know basic dat abase t heory and SQL in order t o use Dj ango’s dat abase layer, it ’s highly
recom m ended. An int roduct ion t o t hose concept s is out of t he scope of t his book, but keep reading even if you’re a dat abase
newbie. You’ll probably be able t o follow along and grasp concept s based on cont ext .)

The “ dumb” way t o do dat abase queries in views


Just as t he previous chapt er det ailed a “ dum b” way t o out put HTML wit hin a view ( by hard- coding HTML direct ly wit hin t he view) ,
t here’s a “ dum b” way t o ret rieve dat a from a dat abase in a view. I t ’s sim ple: Just use any exist ing Pyt hon library t o execut e an
SQL query and do som et hing wit h t he result s.

I n t his exam ple view, we use t he MySQLdb library ( available at ht t p: / / sourceforge.net / proj ect s/ m ysql- pyt hon) t o connect t o a
MySQL dat abase, ret rieve som e records and feed t hem t o a t em plat e for display as a Web page:

from django.shortcuts import render_to_response


import MySQLdb

def book_list(request):
db = MySQLdb.connect(user='me', db='mydb', passwd='secret', host='localhost')
cursor = db.cursor()
cursor.execute('SELECT name FROM books ORDER BY name')
names = [row[0] for row in cursor.fetchall()]
db.close()
return render_to_response('book_list.html', {'names': names})

This approach works, but som e problem s should j um p out at you im m ediat ely:

● We’re hard- coding t he dat abase connect ion param et ers. I deally, t hese param et ers would be st ored in t he Dj ango
configurat ion.
● We’re having t o writ e a fair bit of boilerplat e code: creat ing a connect ion, creat ing a cursor, execut ing a st at em ent and
closing t he connect ion. I deally, all we’d have t o do is specify which result s we want ed.
● I t t ies us t o MySQL. I f, down t he road, we swit ch from MySQL t o Post greSQL, we’ll have t o use a different dat abase adapt er
( e.g., psycopg rat her t han MySQLdb) , alt er t he connect ion param et ers and — depending on t he nat ure of t he SQL st at em ent
— possibly rewrit e t he SQL. I deally, t he dat abase server we’re using would be abst ract ed, so t hat a dat abase server change
could be m ade in a single place.

As you m ight expect , Dj ango’s dat abase layer aim s t o solve t hese problem s. Here’s a sneak preview of how t he above view can
be rewrit t en using Dj ango’s dat abase API :

from django.shortcuts import render_to_response


from mysite.books.models import Book

def book_list(request):
books = Book.objects.order_by('name')
return render_to_response('book_list.html', {'books': books})

We’ll explain t his code a lit t le lat er in t his chapt er. For now, j ust get a feel for how it looks.

The MTV development pat t ern

file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH5.html (1 of 9)9/28/2007 2:09:56 PM


Chapter 5: Interacting with a database: models

Before we delve int o any m ore code, let ’s t ake a m om ent t o consider t he overall design of a dat abase- driven Dj ango Web
applicat ion.

As we’ve m ent ioned in previous chapt ers, Dj ango is designed t o encourage loose coupling and st rict separat ion bet ween pieces of
an applicat ion. I f you follow t his philosophy, it ’s easy t o m ake changes t o one part icular piece of t he applicat ion wit hout affect ing
ot her pieces of t he applicat ion. I n view funct ions, for inst ance, we discussed t he im port ance of separat ing t he business logic from
t he present at ion logic by using a t em plat e syst em . Wit h t he dat abase layer, we’re applying t hat sam e philosophy t o dat a- access
logic.

Those t hree pieces t oget her — dat a- access logic, business logic and present at ion logic — com prise a concept t hat ’s som et im es
called t he “ Model View Cont roller” ( MVC) pat t ern of soft ware archit ect ure. I n t his pat t ern, “ Model” refers t o t he dat a- access layer,
“ View” refers t o t he part of t he syst em t hat select s what t o display and how t o display it , and “ Cont roller” refers t o t he part of t he
syst em t hat decides which view t o use, depending on user input , accessing t he m odel as needed.

W h y t h e a cr on ym ?
MVC? MTV? What ’s t he point of t hese t erm s?

The goal of explicit ly defining pat t erns such as MVC is m ost ly t o st ream line com m unicat ion am ong developers.
I nst ead of having t o t ell your coworkers, “ Let ’s m ake an abst ract ion of t he dat a- access, t hen have a separat e layer
t hat handles dat a display, and let ’s put a layer in t he m iddle t hat regulat es t his,” you can t ake advant age of a
shared vocabulary and say, “ Let ’s use t he MVC pat t ern here.”

Dj ango follows t his MVC pat t ern closely enough t hat it can be called an MVC fram ework. Here’s roughly how t he M, V and C break
down in Dj ango:

● M , t he dat a- access port ion, is handled by Dj ango’s dat abase layer, which is described in t his chapt er.
● V, t he port ion t hat select s which dat a t o display and how t o display it , is handled by views and t em plat es.
● C, t he port ion t hat delegat es t o a view depending on user input , is handled by t he fram ework it self by following your
URLconf and calling t he appropriat e Pyt hon funct ion for t he given URL.

Because t he “ C” is handled by t he fram ework it self and m ost of t he excit em ent in Dj ango happens in m odels, t em plat es and
views, Dj ango has been referred t o as an M TV fr a m e w or k . I n t he MTV developm ent pat t ern,

● “ M” st ands for m odel, t he dat a- access layer. This layer cont ains anyt hing and everyt hing about t he dat a: how t o access it ,
how t o validat e it , which behaviors it has and t he relat ionships bet ween t he dat a.
● “ T” st ands for t em plat e, t he present at ion layer. This layer cont ains present at ion- relat ed decisions: how som et hing should be
displayed on a Web page or ot her t ype of docum ent .
● “ V” st ands for view, t he business- logic layer. This layer cont ains t he logic t hat access t he m odel and defers t o t he
appropriat e t em plat e( s) . You can t hink of it as t he bridge bet ween m odels and t em plat es.

I f you’re fam iliar wit h ot her MVC Web- developm ent fram eworks, such as Ruby on Rails, you m ay consider Dj ango views t o be t he
“ cont rollers” and Dj ango t em plat es t o be t he “ views.” This is an unfort unat e confusion brought about by differing int erpret at ions
of MVC. I n Dj ango’s int erpret at ion of MVC, t he “ view” describes t he dat a t hat get s present ed t o t he user; it ’s not necessarily j ust
how t he dat a looks, but which dat a is present ed. I n cont rast , Ruby on Rails and sim ilar fram eworks suggest t hat t he cont roller’s
j ob includes deciding which dat a get s present ed t o t he user, whereas t he view is st rict ly how t he dat a looks, not which dat a is
present ed.

Neit her int erpret at ion is m ore “ correct ” t han t he ot her. The im port ant t hing is t o underst and t he underlying concept s.

Conf iguring t he dat abase


Wit h all of t hat philosophy in m ind, let ’s st art exploring Dj ango’s dat abase layer. First , we need t o t ake care of som e init ial
configurat ion; we need t o t ell Dj ango which dat abase server t o use and how t o connect t o it .

We’ll assum e you’ve set up a dat abase server, act ivat ed it and creat ed a dat abase wit hin it ( e.g., using a CREATE DATABASE
st at em ent ) . SQLit e is a special case; in t hat case, t here’s no dat abase t o creat e, because SQLit e uses st andalone files on t he
filesyst em t o st ore it s dat a.

As TEMPLATE_DIRS in t he previous chapt er, dat abase configurat ion lives in t he Dj ango set t ings file, called settings.py by default .
Edit t hat file and look for t he dat abase set t ings:

DATABASE_ENGINE = ''
DATABASE_NAME = ''
DATABASE_USER = ''
DATABASE_PASSWORD = ''
DATABASE_HOST = ''
DATABASE_PORT = ''

Here’s a rundown of each set t ing.

● DATABASE_ENGINE t ells Dj ango which dat abase engine t o use. I f you’re using a dat abase wit h Dj ango, DATABASE_ENGINE
m ust be set t o one of t he following st rings:

file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH5.html (2 of 9)9/28/2007 2:09:56 PM


Chapter 5: Interacting with a database: models

Se t t in g D a t a ba se Re qu ir e d a da pt e r
postgresql Post greSQL psycopg version 1.x, ht t p: / / init d.org/ proj ect s/ psycopg1
postgresql_psycopg2 Post greSQL psycopg version 2.x, ht t p: / / init d.org/ proj ect s/ psycopg2
mysql MySQL MySQLdb, ht t p: / / sourceforge.net / proj ect s/ m ysql- pyt hon
No adapt er needed if using Pyt hon 2.5+ .
sqlite3 SQLit e Ot herwise, pysqlite, ht t p: / / init d.org/ t racker/ pysqlit e
ado_mssql Microsoft SQL Server adodbapi version 2.0.1+ , ht t p: / / adodbapi.sourceforge.net /
oracle Oracle cx_Oracle, ht t p: / / www.pyt hon.net / crew/ at uining/ cx_Oracle/

Not e t hat for whichever dat abase backend you use, you’ll need t o download and inst all t he appropriat e dat abase adapt er.
Each one is available for free on t he Web.

● DATABASE_NAME t ells Dj ango what t he nam e of your dat abase is. I f you’re using SQLit e, specify t he full filesyst em pat h t o t he
dat abase file on your filesyst em , e.g., '/home/django/mydata.db'

● DATABASE_USER t ells Dj ango which usernam e t o use when connect ing t o your dat abase. I f you’re using SQLit e, leave t his
blank.

● DATABASE_PASSWORD t ells Dj ango which password t o use when connect ing t o your dat abase. I f you’re using SQLit e or have
an em pt y password, leave t his blank.

● DATABASE_HOST t ells Dj ango which host t o use when connect ing t o your dat abase. I f your dat abase is on t he sam e com put er
as your Dj ango inst allat ion ( i.e., localhost ) , leave t his blank. I f you’re using SQLit e, leave t his blank.

MySQL is a special case here. I f t his value st art s wit h a forward slash ( '/') and you’re using MySQL, MySQL will connect via
a Unix socket t o t he specified socket . For exam ple:

DATABASE_HOST = '/var/run/mysql'

I f you’re using MySQL and t his value doesn’t st art wit h a forward slash, t hen t his value is assum ed t o be t he host .

● DATABASE_PORT t ells Dj ango which port t o use when connect ing t o your dat abase. I f you’re using SQLit e, leave t his blank.
Ot herwise, if you leave t his blank, t he underlying dat abase adapt er will use whichever port is default for your given dat abase
server. I n m ost cases, t he default port is fine, so you can leave t his blank.

Once you’ve ent ered t hose set t ings, t est your configurat ion. First , from wit hin t he mysite proj ect direct ory you creat ed in Chapt er
2, run t he com m and python manage.py shell.

You’ll not ice t his st art s a Pyt hon int eract ive int erpret er. Looks can be deceiving, t hough! There’s an im port ant difference bet ween
running t he com m and python manage.py shell wit hin your Dj ango proj ect direct ory and t he m ore generic python. The lat t er is
t he basic Pyt hon shell, but t he form er t ells Dj ango which set t ings file t o use before it st art s t he shell. This is a key requirem ent
for doing dat abase queries: Dj ango needs t o know which set t ings file t o use in order t o get your dat abase connect ion inform at ion.

Behind t he scenes, python manage.py shell set s t he environm ent variable DJANGO_SETTINGS_MODULE. We’ll cover t he subt let ies
of t his lat er, but for now, j ust know t hat you should use python manage.py shell whenever you need t o drop int o t he Pyt hon
int erpret er t o do Dj ango- specific t inkering.

Once you’ve ent ered t he shell, t ype t hese com m ands t o t est your dat abase configurat ion:

>>> from django.db import connection


>>> cursor = connection.cursor()

I f not hing happens, t hen your dat abase is configured properly. Ot herwise, check t he error m essage for clues about what ’s wrong.
Here are som e com m on errors:

Er r or m e ssa ge Solu t ion


You haven’t set t he DATABASE_ENGI NE set t ing Set t he DATABASE_ENGINE set t ing t o som et hing ot her t han
yet . an em pt y st ring.
Environm ent variable Run t he com m and python manage.py shell rat her
DJANGO_SETTI NGS_MODULE is undefined. t han python.
Error loading _____ m odule: No m odule nam ed You haven’t inst alled t he appropriat e dat abase- specific
_____. adapt er ( e.g. psycopg or MySQLdb) .
Set your DATABASE_ENGINE set t ing t o one of t he valid
_____ isn’t an available dat abase backend.
engine set t ings described above. Perhaps you m ade a t ypo?

file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH5.html (3 of 9)9/28/2007 2:09:56 PM


Chapter 5: Interacting with a database: models

Change t he DATABASE_NAME set t ing t o point t o a dat abase


dat abase _____ does not exist t hat exist s, or execut e t he appropriat e CREATE DATABASE
st at em ent in order t o creat e it .
Change DATABASE_USER set t ing t o point t o a user t hat
role _____ does not exist
exist s, or creat e t he user in your dat abase.
Make sure DATABASE_HOST and DATABASE_PORT are set
could not connect t o server
correct ly, and m ake sure t he server is running.

Your f irst app


Now t hat you’ve verified t he connect ion is working, it ’s t im e t o creat e a D j a n go a pp — a bundle of Dj ango code, including
m odels and views, t hat lives t oget her in a single Pyt hon package and represent s a full Dj ango applicat ion.

I t ’s wort h explaining t he t erm inology here, because t his t ends t o t rip up beginners. We’d already creat ed a proj ect , in Chapt er 2,
so what ’s t he difference bet ween a proj ect and an app? The difference is t hat of configurat ion vs. code:

● A proj ect is an inst ance of a cert ain set of Dj ango apps, plus t he configurat ion for t hose apps.

Technically, t he only requirem ent of a proj ect is t hat it supplies a set t ings file, which defines t he dat abase connect ion
inform at ion, t he list of inst alled apps, t he TEMPLATE_DIRS, et c.

● An app is a port able set of Dj ango funct ionalit y, usually including m odels and views, t hat lives t oget her in a single Pyt hon
package.

For exam ple, Dj ango com es wit h a num ber of apps, such as a com m ent ing syst em and an aut om at ic adm in int erface. A key
t hing t o not e about t hese apps is t hat t hey’re port able and reusable across m ult iple proj ect s.

There are very few hard- and- fast rules about how you fit your Dj ango code int o t his schem e; it ’s flexible. I f you’re building a
sim ple Web sit e, you m ay only use a single app. I f you’re building a com plex Web sit e wit h several rat her unrelat ed pieces such
as an e- com m erce syst em and a m essage board, you’ll probably want t o split t hose int o separat e apps so t hat you’ll be able t o
reuse t hem individually in t he fut ure.

I ndeed, you don’t necessarily need t o creat e apps at all, as evidenced by t he exam ple view funct ions we’ve creat ed so far in t his
book. I n t hose cases, we sim ply creat ed a file called views.py, filled it wit h view funct ions and point ed our URLconf at t hose
funct ions. No “ apps” were needed.

However, t here’s one requirem ent regarding t he app convent ion: I f you’re using Dj ango’s dat abase layer ( m odels) , you m ust
creat e a Dj ango app. Models m ust live wit hin apps. Thus, in order t o st art writ ing our m odels, we’ll need t o creat e a new app.

Wit hin t he mysite proj ect direct ory you creat ed in Chapt er 2, t ype t his com m and t o creat e a new app:

python manage.py startapp books

( Why books? That ’s t he sam ple book app we’ll be building t oget her.)

This com m and does not result in any out put , but it will have creat ed a books direct ory wit hin t he mysite direct ory. Let ’s look at
t he cont ent s of t hat direct ory:

books/
__init__.py
models.py
views.py

These files will cont ain your m odels and views for t his app.

Have a look at models.py and views.py in your favorit e t ext edit or. Bot h files are em pt y, except for an im port in models.py. This
is t he blank slat e for your Dj ango app.

Def ining models in Pyt hon


As we discussed above, t he “ M” in “ MTV” st ands for “ Model.” A Dj ango m odel is a descript ion of t he dat a in your dat abase,
represent ed as Pyt hon code. I t ’s your dat a layout — t he equivalent of your SQL CREATE TABLE st at em ent s — except it ’s in Pyt hon
inst ead of SQL, and it includes m ore t han j ust dat abase definit ions. Dj ango uses a m odel t o execut e SQL code behind t he scenes
and ret urn convenient Pyt hon dat a st ruct ures represent ing t he rows in your dat abase t ables. Dj ango also uses m odels t o
represent higher- level concept s t hat SQL can’t necessarily handle.

I f you’re fam iliar wit h dat abases, your im m ediat e t hought m ight be, “ I sn’t it redundant t o define dat a m odels in Pyt hon and in
SQL?” Dj ango works t he way it does for several reasons:

● I nt rospect ion requires overhead and is im perfect .

I n order t o provide convenient dat a- access API s, Dj ango needs t o know t he dat abase layout som ehow, and t here are t wo

file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH5.html (4 of 9)9/28/2007 2:09:56 PM


Chapter 5: Interacting with a database: models

ways of accom plishing t his. The first way would be t o explicit ly describe t he dat a in Pyt hon, and t he second way would be t o
int rospect t he dat abase at runt im e t o det erm ine t he dat a m odels.

This second way seem s cleaner, because t he m et adat a about your t ables only lives in one place, but it int roduces a few
problem s. First , int rospect ing a dat abase at runt im e obviously requires overhead. I f t he fram ework had t o int rospect t he
dat abase each t im e it processed a request , or even when t he Web server was init ialized, t his would incur an unaccept able
level of overhead. ( While som e believe t hat level of overhead is accept able, Dj ango’s developers aim t o t rim as m uch
fram ework overhead as possible, and t his approach has succeeded in m aking Dj ango fast er t han it s high- level fram ework
com pet it ors in benchm arks.) Second, som e dat abases, not ably older versions of MySQL, do not st ore sufficient m et adat a for
accurat e and com plet e int rospect ion.

● Writ ing Pyt hon is fun, and keeping everyt hing in Pyt hon lim it s t he num ber of t im es your brain has t o do a “ cont ext swit ch.”
I t helps product ivit y if you keep yourself in a single program m ing environm ent / m ent alit y for as long as possible. Having t o
writ e SQL, t hen Pyt hon, t hen SQL again, is disrupt ive.

● Having dat a m odels st ored as code rat her t han in your dat abase m akes it easier t o keep your m odels under version cont rol.
This way, you can easily keep t rack of changes t o your dat a layout s.

● SQL only allows for a cert ain level of m et adat a about a dat a layout . Most dat abase syst em s, for exam ple, do not provide a
specialized dat a t ype for represent ing e- m ail addresses or URLs. Dj ango m odels do. The advant age of higher- level dat a
t ypes is higher product ivit y and m ore reusable code.

● SQL is inconsist ent across dat abase plat form s. I f you’re dist ribut ing a Web applicat ion, for exam ple, it ’s m uch m ore
pragm at ic t o dist ribut e a Pyt hon m odule t hat describes your dat a layout t han separat e set s of CREATE TABLE st at em ent s for
MySQL, Post greSQL and SQLit e.

A drawback of t his approach, however, is t hat it ’s possible for t he Pyt hon code t o get out of sync wit h what ’s act ually in t he
dat abase. I f you m ake changes t o a Dj ango m odel, you’ll need t o m ake t he sam e changes inside your dat abase t o keep your
dat abase consist ent wit h t he m odel. We’ll det ail som e st rat egies for handling t his problem lat er in t his chapt er.

Finally, we should not e t hat Dj ango includes a ut ilit y t hat can generat e m odels by int rospect ing an exist ing dat abase. This is
useful for quickly get t ing up and running wit h legacy dat a.

Your f irst model


As an ongoing exam ple in t his chapt er and t he next chapt er, we’ll focus on a basic book/ aut hor/ publisher dat a layout . We use t his
as our exam ple because t he concept ual relat ionships bet ween books, aut hors and publishers are well- known, and t his is a
com m on dat a layout used in int roduct ory SQL t ext books. You’re also reading a book, writ t en by aut hors, produced by a publisher!

We’ll suppose t he following concept s, fields and relat ionships:

● An aut hor has a salut at ion ( e.g., Mr. or Mrs.) , a first nam e, a last nam e, an e- m ail address and a headshot phot o.
● A publisher has a nam e, a st reet address, a cit y, a st at e/ province, a count ry and a Web sit e.
● A book has a t it le and a publicat ion dat e. I t also has one or m ore aut hors ( a m any- t o- m any relat ionship t o aut hor) and a
single publisher ( a one- t o- m any relat ionship, aka foreign key, t o publisher) .

The first st ep in using t his dat abase layout wit h Dj ango is t o express it as Pyt hon code. I n t he models.py file t hat was creat ed by
t he startapp com m and, ent er t he following:

from django.db import models

class Publisher(models.Model):
name = models.CharField(maxlength=30)
address = models.CharField(maxlength=50)
city = models.CharField(maxlength=60)
state_province = models.CharField(maxlength=30)
country = models.CharField(maxlength=50)
website = models.URLField()

class Author(models.Model):
salutation = models.CharField(maxlength=10)
first_name = models.CharField(maxlength=30)
last_name = models.CharField(maxlength=40)
email = models.EmailField()
headshot = models.ImageField(upload_to='/tmp')

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

We will cover m odel synt ax and opt ions t hroughout t his chapt er, but let ’s quickly exam ine t his code t o cover t he basics. The first
t hing t o not ice is t hat each m odel is represent ed by a Pyt hon class t hat is a subclass of django.db.models.Model. The parent
class, Model, cont ains all t he m achinery necessary t o m ake t hese obj ect s capable of int eract ing wit h a dat abase — and t hat

file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH5.html (5 of 9)9/28/2007 2:09:56 PM


Chapter 5: Interacting with a database: models

leaves our m odels responsible solely for defining t heir fields, in a nice and com pact synt ax. Believe it or not , t his is all t he code
we need t o writ e t o have basic dat a access wit h Dj ango.

Each m odel generally corresponds t o a single dat abase t able, and each at t ribut e on a m odel generally corresponds t o a colum n in
t hat dat abase t able. The at t ribut e nam e corresponds t o t he colum n’s nam e, and t he t ype of field ( e.g., CharField) corresponds
t o t he dat abase colum n t ype ( e.g., varchar) . For exam ple, t he Publisher m odel is equivalent t o t he following t able ( assum ing
Post greSQL CREATE TABLE synt ax) :

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

I ndeed, Dj ango can generat e t hat CREATE TABLE st at em ent it self, as we’ll see in a m om ent .

The except ion t o t he one- class- per- dat abase- t able rule is t he case of m any- t o- m any relat ionships. I n our exam ple m odels, Book
has a ManyToManyField called authors. This designat es t hat a book has one or m any aut hors, but t he Book dat abase t able
doesn’t get an authors colum n. Rat her, Dj ango creat es an addit ional t able — a m any- t o- m any “ j oin t able” — t hat handles t he
m apping of books t o aut hors.

Finally, not e we haven’t explicit ly defined a prim ary key in any of t hese m odels. Unless you inst ruct it ot herwise, Dj ango
aut om at ically gives every m odel an int eger prim ary key field called id. Each Dj ango m odel is required t o have a single- colum n
prim ary key.

Inst alling t he model


We’ve writ t en t he code; now, let ’s creat e t he t ables in our dat abase. I n order t o do t hat , t he first st ep is t o act ivat e t hese m odels
in our Dj ango proj ect . We do t hat by adding t his books app t o t he list of inst alled apps in t he set t ings file.

Edit t he settings.py file again, and look for t he INSTALLED_APPS set t ing. INSTALLED_APPS t ells Dj ango which apps are act ivat ed
for a given proj ect . By default , it looks som et hing like t his:

INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
)

Tem porarily com m ent out all four of t hose st rings by put t ing a hash charact er ( #) in front of t hem . ( They’re included by default as
a com m on- case convenience, but we’ll act ivat e t hem and discuss t hem lat er.) Then, add 'mysite.books' t o t he INSTALLED_APPS
list , so t he set t ing ends up looking like t his:

INSTALLED_APPS = (
#'django.contrib.auth',
#'django.contrib.contenttypes',
#'django.contrib.sessions',
#'django.contrib.sites',
'mysite.books',
)

( As we’re dealing wit h a single- elem ent t uple here, don’t forget t he t railing com m a. By t he way, t his book’s aut hors prefer t o put
a com m a aft er every elem ent of a t uple, regardless of whet her t he t uple has only a single elem ent . This avoids t he issue of
forget t ing com m as, and t here’s no penalt y for using t hat ext ra com m a.)

'mysite.books' refers t o t he books app we’re working on. Each app in INSTALLED_APPS is represent ed by it s full Pyt hon pat h —
t hat is, t he pat h of packages, separat ed by dot s, leading t o t he app package.

Now t hat t he Dj ango app has been act ivat ed in t he set t ings file, we can creat e t he dat abase t ables in our dat abase. First , let ’s
validat e t he m odels by running t his com m and:

python manage.py validate

The validate com m and checks whet her your m odels’ synt ax and logic are correct . I f all is well, you’ll see t he
m essage 0 errors found. I f you don’t , m ake sure you t yped in t he m odel code correct ly. The error out put should give you
helpful inform at ion about what was wrong wit h t he code.

file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH5.html (6 of 9)9/28/2007 2:09:56 PM


Chapter 5: Interacting with a database: models

Any t im e you t hink you have problem s wit h your m odels, run python manage.py validate. I t t ends t o cat ch all t he com m on
m odel problem s.

I f your m odels are valid, run t he following com m and for Dj ango t o generat e CREATE TABLE st at em ent s for your m odels in
t he books app ( wit h colorful synt ax highlight ing available if you’re using Unix) :

python manage.py sqlall books

I n t his com m and, books is t he nam e of t he app. I t ’s what you specified when you ran t he com m and manage.py startapp. When
you run t he com m and, you should see som et hing like t his:

BEGIN;
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
);
CREATE TABLE "books_book" (
"id" serial NOT NULL PRIMARY KEY,
"title" varchar(100) NOT NULL,
"publisher_id" integer NOT NULL REFERENCES "books_publisher" ("id"),
"publication_date" date NOT NULL
);
CREATE TABLE "books_author" (
"id" serial NOT NULL PRIMARY KEY,
"salutation" varchar(10) NOT NULL,
"first_name" varchar(30) NOT NULL,
"last_name" varchar(40) NOT NULL,
"email" varchar(75) NOT NULL,
"headshot" varchar(100) NOT NULL
);
CREATE TABLE "books_book_authors" (
"id" serial NOT NULL PRIMARY KEY,
"book_id" integer NOT NULL REFERENCES "books_book" ("id"),
"author_id" integer NOT NULL REFERENCES "books_author" ("id"),
UNIQUE ("book_id", "author_id")
);
CREATE INDEX books_book_publisher_id ON "books_book" ("publisher_id");
COMMIT;

Not e t he following:

● Table nam es are aut om at ically generat ed by com bining t he nam e of t he app ( books) and t he lowercase nam e of t he m odel
— publisher, book and author. You can override t his behavior, as we’ll see lat er in t his chapt er.
● As we m ent ioned above, Dj ango adds a prim ary key for each t able aut om at ically — t he id fields. You can override t his, t oo.
● By convent ion, Dj ango appends "_id" t o t he foreign key field nam e. As you m ight have guessed, you can override t his
behavior, t oo.
● The foreign key relat ionship is m ade explicit by a REFERENCES st at em ent .
● These CREATE TABLE st at em ent s are t ailored t o t he dat abase you’re using, so dat abase- specific field t ypes such
as auto_increment ( MySQL) , serial ( Post greSQL) , or integer primary key ( SQLit e) are handled for you aut om at ically.
The sam e goes for quot ing of colum n nam es — e.g., using double quot es or single quot es. This exam ple out put is in
Post greSQL synt ax.

The sqlall com m and doesn’t act ually creat e t he t ables or ot herwise t ouch your dat abase — it j ust print s out put t o t he screen so
you can see what SQL Dj ango would execut e if you asked it . I f you want ed t o, you could copy and past e t his SQL int o your
dat abase client , or use Unix pipes t o pass it direct ly. However, Dj ango provides an easier way of com m it t ing t he SQL t o t he
dat abase. Run t he syncdb com m and, like so:

python manage.py syncdb

You’ll see som et hing like t his:

Creating table books_publisher


Creating table books_book
Creating table books_author
Installing index for books.Book model

file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH5.html (7 of 9)9/28/2007 2:09:56 PM


Chapter 5: Interacting with a database: models

The syncdb com m and is a sim ple “ sync” of your m odels t o your dat abase. I t looks at all of t he m odels in each app in
your INSTALLED_APPS set t ing, checks t he dat abase t o see whet her t he appropriat e t ables exist yet , and creat es t he t ables if t hey
don’t yet exist . Not e t hat syncdb does not sync changes in m odels or delet ions of m odels; if you m ake a change t o a m odel or
delet e a m odel, and you want t o updat e t he dat abase, syncdb will not handle t hat . ( More on t his lat er.)

I f you run python manage.py syncdb again, not hing happens, because you haven’t added any m odels t o t he books app, or
added any apps t o INSTALLED_APPS. Ergo, it ’s always safe t o run python manage.py syncdb — it won’t clobber t hings.

I f you’re int erest ed, t ake a m om ent t o dive int o your dat abase server’s com m and- line client and see t he dat abase t ables Dj ango
creat ed. You can m anually run t he com m and- line client — e.g., psql for Post greSQL — or you can run t he
com m and python manage.py dbshell, which will figure out which com m and- line client t o run, depending on
your DATABASE_SERVER set t ing. The lat t er is alm ost always m ore convenient .

Basic dat a access


Once you’ve creat ed a m odel, Dj ango aut om at ically provides a high- level Pyt hon API for working wit h t hose m odels. Try it out by
running python manage.py shell and t yping t he following:

>>> from books.models import Publisher


>>> p = Publisher(name='Apress', address='2560 Ninth St.',
... city='Berkeley', state_province='CA', country='U.S.A.',
... website='http://www.apress.com/')
>>> p.save()
>>> p = Publisher(name="O'Reilly", address='10 Fawcett St.',
... city='Cambridge', state_province='MA', country='U.S.A.',
... website='http://www.oreilly.com/')
>>> p.save()
>>> publisher_list = Publisher.objects.all()
>>> publisher_list
[<Publisher: Publisher object>, <Publisher: Publisher object>]

I n only a few lines of code, t his has accom plished quit e a bit . The highlight s:

● To creat e an obj ect , j ust im port t he appropriat e m odel class and inst ant iat e it by passing in values for each field.
● To save t he obj ect t o t he dat abase, call t he save() m et hod on t he obj ect . Behind t he scenes, Dj ango execut es an
SQL INSERT st at em ent here.
● To ret rieve obj ect s from t he dat abase, use t he at t ribut e Publisher.objects. Fet ch a list of all Publisher obj ect s in t he
dat abase wit h t he st at em ent Publisher.objects.all(). Behind t he scenes, Dj ango execut es an SQL SELECT st at em ent
here.

Nat urally, you can do quit e a lot wit h t he Dj ango dat abase API — but first , let ’s t ake care of a sm all annoyance.

Adding model st ring represent at ions


Above, when we print ed out t he list of publishers, all we got was t his unhelpful display t hat m akes it difficult t o t ell t he Publisher
obj ect s apart :

[<Publisher: Publisher object>, <Publisher: Publisher object>]

We can fix t his easily by adding a m et hod called __str__() t o our Publisher obj ect . A __str__() m et hod t ells Pyt hon how t o
display t he “ st ring” represent at ion of an obj ect . You can see t his in act ion by adding a __str__() m et hod t o t he t hree m odels:

class Publisher(models.Model):
name = models.CharField(maxlength=30)
address = models.CharField(maxlength=50)
city = models.CharField(maxlength=60)
state_province = models.CharField(maxlength=30)
country = models.CharField(maxlength=50)
website = models.URLField()

def __str__(self):
return self.name

class Author(models.Model):
salutation = models.CharField(maxlength=10)
first_name = models.CharField(maxlength=30)
last_name = models.CharField(maxlength=40)
email = models.EmailField()
headshot = models.ImageField(upload_to='/tmp')
def __str__(self):

file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH5.html (8 of 9)9/28/2007 2:09:56 PM


Chapter 5: Interacting with a database: models

return '%s %s' % (self.first_name, self.last_name)

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

def __str__(self):
return self.title

As you can see, a __str__() m et hod can do what ever it needs t o do in order t o ret urn a st ring represent at ion. Here,
t he __str__() m et hods for Publisher and Book sim ply ret urn t he obj ect ’s nam e and t it le, respect ively, but t he __str__()
for Author is slight ly m ore com plex — it pieces t oget her t he first_name and last_name fields. The only requirem ent
for __str__() is t hat it ret urn a st ring. I f __str__() doesn’t ret urn a st ring — if it ret urns, say, an int eger — t hen Pyt hon will
raise a TypeError wit h a m essage like "__str__ returned non-string".

For t he changes t o t ake effect , exit out of t he Pyt hon shell and ent er it again wit h python manage.py shell. ( This is t he easiest
way t o m ake code changes t ake effect .) Now, t he list of Publisher obj ect s is m uch easier t o underst and:

>>> from books.models import Publisher


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

Make sure any m odel you define has a __str__() m et hod — not only for your own convenience when using t he int eract ive
int erpret er, but also because Dj ango uses t he out put of __str__() in several places when it needs t o display obj ect s.

Finally, not e t hat __str__() is a good exam ple of adding behavior t o m odels. A Dj ango m odel describes m ore t han t he dat abase
t able layout for an obj ect ; it also describes any funct ionalit y t hat obj ect knows how t o do. __str__() is one exam ple of such
funct ionalit y — a m odel knows how t o display it self.

Creat ing and modif ying obj ect s


This chapt er is not yet finished.

« previous ◊ t able of cont ent s ◊ next »


Copyright 2006 Adrian Holovat y and Jacob Kaplan- Moss.
This work is licensed under t he GNU Free Docum ent License.

file:///D|/books/computer/programming/python/books/DJANGO BOOK/CH5.html (9 of 9)9/28/2007 2:09:56 PM


Chapter 6: The Django admin site

The Dj ango Book


« previous ◊ t able of cont ent s ◊ next »

Chapt er 6: The Dj ango admin sit e


There’s one part of Web developm ent we’ve always hat ed: writ ing adm inist rat ion int erfaces. Developing t he part s of t he sit e t hat t he
general public sees is always different and int erest ing, but t he bit s t hat t he adm inist rat ors use t o m odify t he sit e are always t he sam e.
You’ve got t o deal wit h aut hent icat ing users, display and handle form s, deal wit h t ricky validat ion issues… I t ’s boring, and it ’s
repet it ive.

Dj ango’s approach t o t his boring, repet it ive t ask? Do it all for you — in j ust a couple of lines of code, no less.

One of t he oldest and m ost powerful part s of Dj ango is t he aut om at ic adm in int erface. I t hooks off of m et adat a in your m odel t o
provide a powerful and product ion- ready int erface t hat cont ent producers can im m ediat ely use t o st art adding cont ent t o t he sit e.

Act ivat ing t he admin int erf ace


We t hink t he adm in int erface is t he coolest part of Dj ango — and m ost Dj angonaut s agree — but since not everyone act ually needs it ,
it ’s an opt ional piece. That m eans t here are t hree st eps you’ll need t o follow t o act ivat e t he adm in int erface:

1. Add adm in m et adat a t o your m odels.

Not all m odels can ( or should) be edit able by adm in users, so you need t o “ m ark” m odels t hat should have an adm in int erface.
You do t hat be adding an inner Admin class t o your m odel ( alongside t he Meta class, if you have one) . So, t o add an adm in
int erface t o our Book m odel from t he previous chapt er:

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

The Admin declarat ion flags t he class as having an adm in int erface. There are a num ber of opt ions t hat you can put beneat h Admin
, but for now we’re st icking wit h all t he default s, so we put pass in t here t o signify t o Pyt hon t hat t he Admin class is em pt y.

I f you’re following t his exam ple wit h your own code, it ’s probably a good idea t o add Admin declarat ions t o t he Publisher
and Author classes at t his point .

2. I nst all t he adm in m odels. Sim ply add "django.contrib.admin" t o your INSTALLED_APPS set t ing and
run python manage.py syncdb t o inst all t he ext ra t ables t he adm in uses.

http://www.djangobook.com/en/beta/chapter06/ (1 of 14)9/28/2007 2:24:34 PM


Chapter 6: The Django admin site

N ot e
When you first ran syncdb, you were probably asked about creat ing a superuser. I f you didn’t t hat t im e, you’ll need
t o run django/contrib/auth/bin/create_superuser.py t o creat e an adm in user. Ot herwise you won’t be able t o
log int o t he adm in int erface.

3. Add t he URL pat t ern t o your urls.py. I f you’re st ill using t he one creat ed by startproject, t he adm in URL pat t ern should be
already t here, but com m ent ed out . Eit her way, your URL pat t erns should look like:

from django.conf.urls.defaults import *

urlpatterns = patterns('',
(r'^admin/', include('django.contrib.admin.urls')),
)

That ’s it . Now run python manage.py runserver t o st art t he developm ent server; you’ll see som et hing like:

Validating models...
0 errors found.

Django version 0.96-pre, using settings 'ch6.settings'


Development server is running at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

Now you can visit t he URL given t o you by Dj ango ( ht t p: / / 127.0.0.1: 8000/ adm in/ in t he exam ple above) , log in, and play around.

Using t he admin int erf ace


The adm in int erface is designed t o be used by non- t echnical users, and as such should be pret t y self- explanat ory. Nevert heless, a few
not es about t he feat ures of t he adm in are in order.

The first t hing you’ll see is a login screen:

http://www.djangobook.com/en/beta/chapter06/ (2 of 14)9/28/2007 2:24:34 PM


Chapter 6: The Django admin site

You’ll use t he usernam e and password you set up when you first ran syncdb here. Once you’ve logged in, you’ll see t hat you can
m anage users, groups, and perm issions in t he adm in; see m ore on t hat below.

Each obj ect given an Admin declarat ion shows up on t he m ain index page. Links t o add and change obj ect s lead t o t wo pages we refer
t o as obj ect “ change list s” and “ edit form s” :
http://www.djangobook.com/en/beta/chapter06/ (3 of 14)9/28/2007 2:24:34 PM
Chapter 6: The Django admin site

Change list s are essent ially index pages of obj ect s in t he syst em :

http://www.djangobook.com/en/beta/chapter06/ (4 of 14)9/28/2007 2:24:34 PM


Chapter 6: The Django admin site

There are a num ber of opt ions t hat can cont rol which fields appear on t hese list s and t he appearance of ext ra feat ures like dat e drill
downs, search fields, and filt er int erfaces. There’s m ore about t hese feat ures below.

Edit form s are used t o edit exist ing obj ect s and creat e new ones. Each field defined in your m odel appears here, and you’ll not ice t hat
fields of different t ypes get different widget s ( i.e. dat e/ t im e fields have calendar cont rols; foreign keys use a select box, et c.) :
http://www.djangobook.com/en/beta/chapter06/ (5 of 14)9/28/2007 2:24:34 PM
Chapter 6: The Django admin site

You’ll not ice t hat t he adm in also handles input validat ion for you; t ry leaving a required field blank, or put t ing an invalid t im e int o a
t im e field and you’ll see t hose errors when you t ry t o save:

http://www.djangobook.com/en/beta/chapter06/ (6 of 14)9/28/2007 2:24:34 PM


Chapter 6: The Django admin site

This validat ion act ually is done wit h a powerful validat ion fram ework which is discussed in Chapt er 7.

When edit ing an exist ing obj ect , you’ll not ice a “ hist ory” link in t he upper- right . Every change m ade t hrough t he adm in is logged, and
you can exam ine t his log by clicking t he hist ory but t on:

http://www.djangobook.com/en/beta/chapter06/ (7 of 14)9/28/2007 2:24:34 PM


Chapter 6: The Django admin site

Delet ions in t he adm in cascade. That is, when delet ing an exist ing obj ect , you’ll see t hat t he adm in asks you t o confirm t he delet e
act ion t o avoid cost ly m ist akes. What m ight not be inst ant ly obvious is t hat t his page will show you all t he relat ed obj ect s t hat will be
delet ed as well:

http://www.djangobook.com/en/beta/chapter06/ (8 of 14)9/28/2007 2:24:34 PM


Chapter 6: The Django admin site

Users, groups, and permissions


Since you’re logged in as a superuser, you have access t o creat e, edit , and delet e any obj ect . However, t he adm in has a user
perm issions syst em t hat you can use t o give ot her users access only t o t he port ions of t he adm in t hey need.
http://www.djangobook.com/en/beta/chapter06/ (9 of 14)9/28/2007 2:24:34 PM
Chapter 6: The Django admin site

You edit t hese users and perm issions t hrough t he adm in j ust like any ot her obj ect ; t he link t o t he User and Group m odels is t here on
t he adm in index along wit h all t he obj ect s you’ve defined yourself.

User obj ect s have t he st andard usernam e, password, em ail, and real nam e fields you m ight expect , along wit h a set of fields t hat
define what t he user is allowed t o do in t he adm in. First , t here’s a set of t hree flags:

● The “ is act ive” flag cont rols whet her t he user is act ive at all. I f t his flag is off, t he user has no access t o any URLs t hat require
login.
● The “ is st aff” flag cont rols whet her t he user is allowed t o log int o t he adm in ( i.e. is considered a “ st aff m em ber” in your
organizat ion) . Since t his sam e user syst em can be used t o cont rol access t o public ( i.e. non- adm in) sit es ( see Chapt er 12) , t his
flag different iat es bet ween public users and adm inist rat ors.
● The “ is superuser” flag gives t he user full unfet t ered access t o every it em in t he adm in; regular perm issions are ignored.

For “ norm al” adm in users — act ive, non- superuser st aff m em bers — t he access t hey are grant ed depends on a set of assigned
perm issions. Each obj ect edit able t hrough t he adm in has t hree perm issions: a “ creat e” perm ission, an “ edit ” perm ission, and a
“ delet e” perm ission. Assigning perm issions t o a user grant s t he user access t o do what is described by t hose perm issions.

N ot e
Not ice t hat access t o edit users and perm issions is also cont rolled by t his perm ission syst em . I f you give a user
perm ission t o edit users, she will be able t o edit her own perm issions, which m ight not be what you want !

You can also assign users t o groups. A group is sim ply a set of perm issions t o apply t o all m em bers of t hat group. Groups are
ext rem ely useful for grant ing a large num ber of users ident ical perm issions.

Cust omizing t he admin int erf ace


There are a num ber of ways t o cust om ize t he way t he adm in int erface looks and behaves. We’ll cover j ust a few of t hem below as t hey
relat e t o our ` ` Book` ` m odel, but Chapt er 12 covers cust om izing t he adm in int erface in det ail.

As it st ands now, t he change list for our books show only t he st ring represent at ion of t he m odel we added t o it s __str__. This works
fine for j ust a few books, but if we had hundreds or t housands of books, it would be very hard t o locat e a single needle in t he
hayst ack. However, we can easily add som e display, searching, and filt ering funct ions t o t his int erface. Change t he Admin declarat ion
t o:

class Book(models.Model):
title = models.CharField(maxlength=100)
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher)
publication_date = models.DateField()
class Admin:
list_display = ('title', 'publisher', 'publication_date')
list_filter = ('publisher', 'publication_date')
ordering = ('-publication_date',)
search_fields = ('title',)
http://www.djangobook.com/en/beta/chapter06/ (10 of 14)9/28/2007 2:24:34 PM
Chapter 6: The Django admin site

These four lines of code dram at ically change our list int erface:

Each of t hose lines inst ruct ed t he adm in t o const ruct a different piece of t his int erface:

http://www.djangobook.com/en/beta/chapter06/ (11 of 14)9/28/2007 2:24:34 PM


Chapter 6: The Django admin site

● The ordering opt ion cont rols what order t he obj ect s are present ed in t he adm in. I t ’s sim ply a list of fields t o order t he result s by;
prefixing a field wit h a m inus sign reverses t he given order. So in t his exam ple, we’re ordering by publicat ion dat e, m ost recent
first .

● The list_display opt ion cont rols which colum ns appear in t he change list t able. By default , only a single colum n wit h t he
obj ect ’s st ring represent at ion appears; here we’ve changed t hat t o show t he t it le, publisher, and publicat ion dat e.

● The list_filter opt ion creat es t he filt ering bar on t he right side of t he list . We’ve allowed filt ering eit her by dat e ( which allows
you t o see only books published in t he last week, m ont h, et c.) , and by publisher.

You can inst ruct t he adm in t o filt er by any field, but foreign keys or any field wit h a choices at t ribut e set work best .

● Finally, t he search_fields opt ion creat es a field t hat allows t ext searches. This allows searches by t he title field ( so you could
t ype “ Dj ango” t o show all books wit h “ Dj ango” in t he t it le) .

Using t hese opt ions — and t he ot her ones described in Chapt er 12 — you can wit h only a few lines of code m ake a very powerful,
product ion- ready int erface for dat a edit ing.

Cust omize t he admin look and f eel


Clearly, having “ Dj ango adm inist rat ion” at t he t op of each adm in page is ridiculous. I t ’s j ust placeholder t ext .

That ’s easy t o change, t hough, using Dj ango’s t em plat e syst em . The Dj ango adm in is powered by Dj ango it self, and it s int erfaces use
Dj ango’s own t em plat e syst em . ( How m et a! )

Open your set t ings file ( mysite/settings.py, rem em ber) and look at t he TEMPLATE_DIRS set t ing. TEMPLATE_DIRS is a t uple of
filesyst em direct ories t o check when loading Dj ango t em plat es. I t ’s a search pat h.

By default , TEMPLATE_DIRS is em pt y. So, let ’s add a line t o it , t o t ell Dj ango where our t em plat es live:

TEMPLATE_DIRS = (
"/home/mytemplates", # Change this to your own directory.
)

N ot e
Make sure t o include t he t railing com m a t here — Pyt hon uses it t o dist inguish bet ween single- elem ent t uples and
parent hesized expressions.

Now copy t he t em plat e admin/base_site.html from wit hin t he default Dj ango adm in t em plat e direct ory
( django/contrib/admin/templates) int o an admin subdirect ory of whichever direct ory you’re using in TEMPLATE_DIRS. For exam ple,
if your TEMPLATE_DIRS includes "/home/mytemplates", as above, t hen
copy django/contrib/admin/templates/admin/base_site.html t o /home/mytemplates/admin/base_site.html. Don’t forget
t hat admin subdirect ory.

Then, j ust edit t he new admin/base_site.html file t o replace t he generic Dj ango t ext wit h your own sit e’s nam e as you see fit .

http://www.djangobook.com/en/beta/chapter06/ (12 of 14)9/28/2007 2:24:34 PM


Chapter 6: The Django admin site

Not e t hat any of Dj ango’s default adm in t em plat es can be overridden. To override a t em plat e, j ust do t he sam e t hing you did
wit h base_site.html — copy it from t he default direct ory int o your cust om direct ory and m ake changes t o t he copy.

Ast ut e readers wonder how, if TEMPLATE_DIRS was em pt y by default , Dj ango was finding t he default adm in t em plat es? The answer is
t hat , by default , Dj ango aut om at ically looks for t em plat es wit hin a templates/ subdirect ory in each app package as a fallback. See
“ Tem plat e loaders” in Chapt er 10 for m ore inform at ion about how t his works.

Cust omize t he admin index page


On a sim ilar not e, you m ight want t o cust om ize t he look and feel of t he Dj ango adm in index page.

By default , it displays all available apps, according t o your INSTALLED_APPS set t ing, sort ed by t he nam e of t he applicat ion. You m ight ,
however, want t o change t his order t o m ake it easier t o find t he apps you’re looking for. Aft er all, t he index is probably t he m ost
im port ant page of t he adm in, so it should be easy t o use.

The t em plat e t o cust om ize is admin/index.html. ( Rem em ber t o copy admin/base_site.html t o your cust om t em plat e direct ory as in
t he previous exam ple.) Edit t he file, and you’ll see it uses a t em plat e t ag called {% get_admin_app_list as app_list %}. That ’s t he
m agic t hat ret rieves every inst alled Dj ango app. I nst ead of using t hat , you can hard- code links t o obj ect - specific adm in pages in
what ever way you t hink is best . I f hard- coding links doesn’t appeal t o you, you can also see Chapt er 10 for det ails on im plem ent ing
your own t em plat e t ags.

Dj ango offers anot her short cut in t his depart m ent . Run t he com m and python manage.py adminindex <app> t o get a chunk of
t em plat e code for inclusion in t he adm in index t em plat e. I t ’s a useful st art ing point .

For full det ails on cust om izing t he look and feel of t he Dj ango adm in sit e in general, see Chapt er 12.

When and why t o use t he admin int erf ace


We t hink Dj ango’s adm in int erface is pret t y spect acular. I n fact , we’d call it one of Dj ango’s “ killer feat ures” . However, we oft en get
asked quest ions about “ use cases” for t he adm in — when do we use it , and why? Over t he years, we’ve discovered a num ber of
pat t erns for using t he adm in int erface t hat we t hink m ight be helpful.

Obviously, it ’s ext rem ely useful for edit ing dat a ( fancy t hat ) . I f you have any sort of dat a ent ry t asks, t he adm in sim ply can’t be beat .
We suspect t hat t he vast m aj orit y of readers of t his book will have a whole host of dat a ent ry t asks.

Dj ango’s adm in especially shines when non- t echnical users need t o be able t o ent er dat a; t hat ’s t he original genesis of t he feat ure. At
t he newspaper where Dj ango was first developed, developm ent of a t ypical online feat ure — a special report on wat er qualit y in t he
m unicipal supply, say — goes som et hing like t his:

● The report er responsible for t he st ory m eet s wit h one of t he developers and goes over t he available dat a.
● The developer designs a m odel around t his dat a, and t hen opens up t he adm in int erface t o t he report er.
● While t he report er ent ers dat a int o Dj ango, t he program m er can focus on developing t he publicly- accessible int erface ( t he fun
part ! )

I n ot her works, t he raison d’êt re of Dj ango’s adm in is facilit at ing t he sim ult aneous work of cont ent producers and program m ers.

However, beyond t he obvious dat a- ent ry t asks, we find t he adm in useful in a few ot her cases:

http://www.djangobook.com/en/beta/chapter06/ (13 of 14)9/28/2007 2:24:34 PM


Chapter 6: The Django admin site

● I nspect ing dat a m odels: t he first t hing we do when we’ve defined a new m odel is t o call it up in t he adm in and ent er som e
dum m y dat a. This is usually when we find any dat a m odeling errors; having a graphical int erface t o a m odel quickly reveals t hose
m ist akes.
● Managing acquired dat a: t here’s lit t le act ual dat a ent ry associat ed wit h a sit e like chicagocrim e.org since m ost of t he dat a com es
from an aut om at ed source. However, when problem s wit h t he aut om at ically acquired dat a crop up, it ’s very useful t o be able t o
go in and edit t hat dat a easily.

What ’ s next ?
So far we’ve creat ed a few m odels and configured a t op- not ch int erface for edit ing t hat dat a. I n t he next chapt er, we’ll m ove ont o t he
real “ m eat and pot at oes” of Web developm ent : form creat ion and processing.

So grab anot her cup of your favorit e beverage and let ’s get st art ed.

« previous ◊ t able of cont ent s ◊ next »


Copyright 2006 Adrian Holovat y and Jacob Kaplan- Moss.
This work is licensed under t he GNU Free Docum ent License.

http://www.djangobook.com/en/beta/chapter06/ (14 of 14)9/28/2007 2:24:34 PM


Chapter 8: Advanced views and URLconfs

The Dj ango Book


« previous ◊ t able of cont ent s ◊ next »

Chapt er 8: Advanced views and URLconf s


I n Chapt er 3, we explained t he basics of Dj ango view funct ions and URLconfs. This chapt er goes int o m ore det ail about advanced
funct ionalit y in t hose t wo pieces of t he fram ework.

URLconf t ricks

Streamlining function imports


Consider t his URLconf, which builds on t he exam ple in Chapt er 3:

from django.conf.urls.defaults import *


from mysite.views import current_datetime, hours_ahead, hours_behind, now_in_chicago,
now_in_london

urlpatterns = patterns('',
(r'^now/$', current_datetime),
(r'^now/plus(\d{1,2})hours/$', hours_ahead),
(r'^now/minus(\d{1,2})hours/$', hours_behind),
(r'^now/in_chicago/$', now_in_chicago),
(r'^now/in_london/$', now_in_london),
)

As explained in Chapt er 3, each ent ry in t he URLconf includes it s associat ed view funct ion, passed direct ly as a funct ion obj ect .
This m eans it ’s necessary t o im port t he view funct ions at t he t op of t he m odule.

But as a Dj ango applicat ion grows in com plexit y, it s URLconf grows, t oo, and keeping t hose im port s can be t edious t o m anage.
( For each new view funct ion, you’ve got t o rem em ber t o im port it , and t he im port st at em ent t ends t o get overly long if you use
t his approach.) I t ’s possible t o avoid t hat t edium by im port ing t he views m odule it self. This exam ple URLconf is equivalent t o t he
previous one:

from django.conf.urls.defaults import *


from mysite import views

urlpatterns = patterns('',
(r'^now/$', views.current_datetime),
(r'^now/plus(\d{1,2})hours/$', views.hours_ahead),
(r'^now/minus(\d{1,2})hours/$', views.hours_behind),
(r'^now/in_chicago/$', views.now_in_chicago),
(r'^now/in_london/$', views.now_in_london),
)

Dj ango offers anot her way of specifying t he view funct ion for a part icular pat t ern in t he URLconf: You can pass a st ring cont aining
t he m odule nam e and funct ion nam e rat her t han t he funct ion obj ect it self. Cont inuing t he ongoing exam ple:

from django.conf.urls.defaults import *

urlpatterns = patterns('',
(r'^now/$', 'mysite.views.current_datetime'),
(r'^now/plus(\d{1,2})hours/$', 'mysite.views.hours_ahead'),
(r'^now/minus(\d{1,2})hours/$', 'mysite.views.hours_behind'),
(r'^now/in_chicago/$', 'mysite.views.now_in_chicago'),
(r'^now/in_london/$', 'mysite.views.now_in_london'),
)

Using t his t echnique, it ’s no longer necessary t o im port t he view funct ions; Dj ango aut om at ically im port s t he appropriat e view
funct ion t he first t im e it ’s needed, according t o t he st ring describing t he nam e and pat h of t he view funct ion.

A furt her short cut you can t ake when using t he st ring t echnique is t o fact or out a com m on “ view prefix.” I n our URLconf exam ple,
each of t he view st rings st art s wit h 'mysite.views', which is redundant t o t ype. We can fact or out t hat com m on prefix and pass
it as t he first argum ent t o patterns(), like t his:

from django.conf.urls.defaults import *

file:///D|/books/computer/programming/python/books/DJANGO BOOK/ch8.html (1 of 11)9/28/2007 2:27:13 PM


Chapter 8: Advanced views and URLconfs

urlpatterns = patterns('mysite.views',
(r'^now/$', 'current_datetime'),
(r'^now/plus(\d{1,2})hours/$', 'hours_ahead'),
(r'^now/minus(\d{1,2})hours/$', 'hours_behind'),
(r'^now/in_chicago/$', 'now_in_chicago'),
(r'^now/in_london/$', 'now_in_london'),
)

Not e t hat you don’t put a t railing dot ( ".") in t he prefix, nor do you put a leading dot in t he view st rings. Dj ango put s t hat in
aut om at ically.

Wit h t hese t wo approaches in m ind, which is bet t er? I t really depends on your personal coding st yle and needs.

Advant ages of t he st ring approach are:

● I t ’s m ore com pact , because it doesn’t require you t o im port t he view funct ions.
● I t result s in m ore readable and m anageable URLconfs if your view funct ions are spread across several different Pyt hon
m odules.

Advant ages of t he funct ion obj ect approach are:

● I t allows for easy “ wrapping” of view funct ions. See “ Wrapping view funct ions” lat er in t his chapt er.
● I t ’s m ore “ Pyt honic” — t hat is, it ’s m ore in line wit h Pyt hon t radit ions, such as passing funct ions as obj ect s.

Bot h approaches are valid, and you can even m ix t hem wit hin t he sam e URLconf. The choice is yours.

Multiple view prefixes


I n pract ice, if you use t he st ring t echnique, you’ll probably end up m ixing views t o t he point where t he views in your URLconf
won’t have a com m on prefix. However, you can st ill t ake advant age of t he view prefix short cut t o rem ove duplicat ion. Just add
m ult iple patterns() obj ect s t oget her, like t his:

Old:

from django.conf.urls.defaults import *

urlpatterns = patterns('',
(r'^/?$', 'mysite.views.archive_index'),
(r'^(\d{4})/([a-z]{3})/$', 'mysite.views.archive_month'),
(r'^tag/(\w+)/$', 'weblog.views.tag'),
)

New:

from django.conf.urls.defaults import *

urlpatterns = patterns('mysite.views',
(r'^/?$', 'archive_index'),
(r'^(\d{4})/([a-z]{3})/$','archive_month'),
)

urlpatterns += patterns('weblog.views',
(r'^tag/(\w+)/$', 'tag'),
)

All t he fram ework cares about is t hat t here’s a m odule- level variable called urlpatterns. This variable can be const ruct ed
dynam ically, as we do in t his exam ple.

Named groups
I n all of our URLconf exam ples so far, we’ve used sim ple, non- nam ed regular- expression groups — i.e., we put parent heses
around part s of t he URL we want ed t o capt ure, and Dj ango passes t hat capt ured t ext t o t he view funct ion as a posit ional
argum ent . I n m ore advanced usage, it ’s possible t o use nam ed regular- expression groups t o capt ure URL bit s and pass t hem as
keyword argum ent s t o a view.

Ke yw or d a r gu m e n t s vs. posit ion a l a r gu m e n t s


A Pyt hon funct ion can be called using keyword argum ent s or posit ional argum ent s — and, in som e cases, bot h at
t he sam e t im e. I n a keyword argum ent call, you specify t he nam es of t he argum ent s along wit h t he values you’re
passing. I n a posit ional argum ent call, you sim ply pass t he argum ent s wit hout explicit ly specifying which argum ent
m at ches which value; t he associat ion is im plicit in t he argum ent s’ order.

file:///D|/books/computer/programming/python/books/DJANGO BOOK/ch8.html (2 of 11)9/28/2007 2:27:13 PM


Chapter 8: Advanced views and URLconfs

For exam ple, consider t his sim ple funct ion:

def sell(item, price, quantity):


print "Selling %s unit(s) of %s at %s" % (quantity, item, price)

To call it wit h posit ional argum ent s, you specify t he argum ent s in t he order in which t hey’re list ed in t he funct ion
definit ion:

sell('Socks', '$2.50', 6)

To call it wit h keyword argum ent s, you specify t he nam es of t he argum ent s along wit h t he values. The following
st at em ent s are equivalent :

sell(item='Socks', price='$2.50', quantity=6)


sell(item='Socks', quantity=6, price='$2.50')
sell(price='$2.50', item='Socks', quantity=6)
sell(price='$2.50', quantity=6, item='Socks')
sell(quantity=6, item='Socks', price='$2.50')
sell(quantity=6, price='$2.50', item='Socks')

I n Pyt hon regular expressions, t he synt ax for nam ed regular- expression groups is (?P<name>pattern), where name is t he nam e
of t he group and pattern is som e pat t ern t o m at ch.

Here’s an exam ple URLconf t hat uses non- nam ed groups:

from django.conf.urls.defaults import *


from mysite import views

urlpatterns = patterns('',
(r'^articles/(\d{4})/$', views.year_archive),
(r'^articles/(\d{4})/(\d{2})/$', views.month_archive),
)

Here’s t he sam e URLconf, rewrit t en t o use nam ed groups:

from django.conf.urls.defaults import *


from mysite import views

urlpatterns = patterns('',
(r'^articles/(?P<year>\d{4})/$', views.year_archive),
(r'^articles/(?P<year>\d{4})/(?P<month>\d{2})/$', views.month_archive),
)

This accom plishes exact ly t he sam e t hing as t he previous exam ple, wit h one subt le difference: The capt ured values are passed t o
view funct ions as keyword argum ent s rat her t han posit ional argum ent s.

For exam ple, wit h non- nam ed groups, a request t o /articles/2006/03/ would result in a funct ion call equivalent t o t his:

month_archive(request, '2006', '03')

Wit h nam ed groups, t hough, t he sam e request would result in t his funct ion call:

month_archive(request, year='2006', month='03')

I n pract ice, using nam ed groups m akes your URLconfs slight ly m ore explicit and less prone t o argum ent - order bugs — and you
can reorder t he argum ent s in your views’ funct ion definit ions. Following t he above exam ple, if we want ed t o change t he URLs t o
include t he m ont h before t he year, and we were using non- nam ed groups, we’d have t o rem em ber t o change t he order of
argum ent s in t he month_archive view. I f we were using nam ed groups, changing t he order of t he capt ured param et ers in t he
URL would have no effect on t he view.

Of course, t he benefit s of nam ed groups com e at t he cost of brevit y; som e developers find t he nam ed- group synt ax ugly and t oo
verbose.

The matching/ grouping algorithm


I f you use bot h nam ed and non- nam ed groups in t he sam e pat t ern in your URLconf, you should be aware of how Dj ango t reat s
t his special case. Here’s t he algorit hm t he URLconf parser follows, wit h respect t o nam ed groups vs. non- nam ed groups in a
regular expression:

file:///D|/books/computer/programming/python/books/DJANGO BOOK/ch8.html (3 of 11)9/28/2007 2:27:13 PM


Chapter 8: Advanced views and URLconfs

● I f t here are any nam ed argum ent s, it will use t hose, ignoring non- nam ed argum ent s.
● Ot herwise, it will pass all non- nam ed argum ent s as posit ional argum ent s.
● I n bot h cases, it will pass any ext ra keyword argum ent s as keyword argum ent s. See “ Passing ext ra opt ions t o view
funct ions” below.

Passing extra options to view functions


Som et im es you’ll find yourself writ ing view funct ions t hat are quit e sim ilar, wit h only a few sm all differences. For exam ple, say
you’ve got t wo views whose cont ent s are ident ical except for t he t em plat e t hey use:

# urls.py

from django.conf.urls.defaults import *


from mysite import views

urlpatterns = patterns('',
(r'^foo/$', views.foo_view),
(r'^bar/$', views.bar_view),
)

# views.py

from django.shortcuts import render_to_response


from mysite.models import MyModel

def foo_view(request):
m_list = MyModel.objects.filter(is_new=True)
return render_to_response('template1.html', {'m_list': m_list})

def bar_view(request):
m_list = MyModel.objects.filter(is_new=True)
return render_to_response('template2.html', {'m_list': m_list})

We’re repeat ing ourselves in t his code, and t hat ’s inelegant . At first , you m ay t hink t o rem ove t he redundancy by using t he sam e
view for bot h URLs, put t ing parent hesis around t he URL t o capt ure it , and checking t he URL wit hin t he view t o det erm ine t he
t em plat e, like so:

# urls.py

from django.conf.urls.defaults import *


from mysite import views

urlpatterns = patterns('',
(r'^(foo)/$', views.foobar_view),
(r'^(bar)/$', views.foobar_view),
)
# views.py
from django.shortcuts import render_to_response
from mysite.models import MyModel

def foobar_view(request, url):


m_list = MyModel.objects.filter(is_new=True)
if url == 'foo':
template_name = 'template1.html'
elif url == 'bar':
template_name = 'template2.html'
return render_to_response(template_name, {'m_list': m_list})

The problem wit h t hat solut ion, t hough, is t hat it couples your URLs t o your code. I f you decide t o renam e /foo/ t o /fooey/,
you’ll have t o rem em ber t o change t he view code.

The elegant solut ion involves a feat ure called ext ra URLconf opt ions. Each pat t ern in a URLconf m ay include a t hird it em — a
dict ionary of keyword argum ent s t o pass t o t he view funct ion.

Wit h t his in m ind, we can rewrit e our ongoing exam ple like t his:

# urls.py

from django.conf.urls.defaults import *

file:///D|/books/computer/programming/python/books/DJANGO BOOK/ch8.html (4 of 11)9/28/2007 2:27:13 PM


Chapter 8: Advanced views and URLconfs

from mysite import views

urlpatterns = patterns('',
(r'^foo/$', views.foobar_view, {'template_name': 'template1.html'}),
(r'^bar/$', views.foobar_view, {'template_name': 'template2.html'}),
)

# views.py

from django.shortcuts import render_to_response


from mysite.models import MyModel
def foobar_view(request, template_name):
m_list = MyModel.objects.filter(is_new=True)
return render_to_response(template_name, {'m_list': m_list})

As you can see, t he URLconf in t his exam ple specifies template_name in t he URLconf. The view funct ion t reat s it as j ust anot her
param et er.

This ext ra URLconf opt ions t echnique is a nice way of sending addit ional inform at ion t o your view funct ions wit h m inim al fuss. As
such, it ’s used by a couple of Dj ango’s bundled applicat ions, m ost not ably it s generic views syst em , which we’ll cover in Chapt er 9.

Here are a couple of ideas on how you can use t he ext ra URLconf opt ions t echnique in your own proj ect s:

Faking captured URLconf values


Say you’ve got a set of views t hat m at ch a pat t ern, along wit h anot her URL t hat doesn’t fit t he pat t ern but whose view logic is t he
sam e. I n t his case, you can “ fake” t he capt uring of URL values by using ext ra URLconf opt ions t o handle t hat ext ra URL wit h t he
sam e view.

For exam ple, you m ight have an applicat ion t hat displays som e dat a for a part icular day, wit h URLs such as t his:

/mydata/jan/01/
/mydata/jan/02/
/mydata/jan/03/
# ...
/mydata/dec/30/
/mydata/dec/31/

This is sim ple enough t o deal wit h; you can capt ure t hose in a URLconf like t his ( using nam ed group synt ax) :

urlpatterns = patterns('',
(r'^mydata/(?P<month>\w{3})/(?P<day>\d\d)/$', views.my_view),
)

And t he view funct ion signat ure would look like t his:

def my_view(request, month, day):


# ....

This is st raight forward — it ’s not hing we haven’t seen before. The t rick com es in when you want t o add anot her URL t hat
uses my_view but whose URL doesn’t include a month and/ or day.

For exam ple, you m ight want t o add anot her URL, /mydata/birthday/, which would be equivalent t o /mydata/jan/06/. We can
t ake advant age of ext ra URLconf opt ions like so:

urlpatterns = patterns('',
(r'^mydata/birthday/$', views.my_view, {'month': 'jan', 'day': '06'}),
(r'^mydata/(?P<month>\w{3})/(?P<day>\d\d)/$', views.my_view),
)

The cool t hing here is t hat we don’t have t o change our view funct ion at all. The view funct ion only cares t hat it get s month
and day param et ers — it doesn’t m at t er whet her t hey com e from t he URL capt uring it self or ext ra param et ers.

Making a view generic


I t ’s good program m ing pract ice t o “ fact or out ” com m onalit ies in code. For exam ple, wit h t hese t wo Pyt hon funct ions:

def say_hello(person_name):
print 'Hello, %s' % person_name

file:///D|/books/computer/programming/python/books/DJANGO BOOK/ch8.html (5 of 11)9/28/2007 2:27:13 PM


Chapter 8: Advanced views and URLconfs

def say_goodbye(person_name):
print 'Goodbye, %s' % person_name

…we can fact or out t he greet ing t o m ake it a param et er:

def greet(person_name, greeting):


print '%s, %s' % (greeting, person_name)

You can apply t his sam e philosophy t o your Dj ango views by using ext ra URLconf param et ers.

Wit h t his in m ind, you can st art m aking higher- level abst ract ions of your views. I nst ead of t hinking t o yourself, “ This view
displays a list of Event obj ect s,” and “ That view displays a list of BlogEntry obj ect s,” realize t hey’re bot h specific cases of “ A view
t hat displays a list of obj ect s, where t he t ype of obj ect is variable.”

Take t his code, for exam ple:

# urls.py

from django.conf.urls.defaults import *


from mysite import views
urlpatterns = patterns('',
(r'^events/$', views.event_list),
(r'^blog/entries/$', views.entry_list),
)

# views.py

from django.shortcuts import render_to_response


from mysite.models import Event, BlogEntry
def event_list(request):
obj_list = Event.objects.all()
return render_to_response('mysite/event_list.html', {'event_list': obj_list})

def entry_list(request):
obj_list = BlogEntry.objects.all()
return render_to_response('mysite/blogentry_list.html', {'entry_list': obj_list})

The t wo views do essent ially t he sam e t hing: t hey display a list of obj ect s. So let ’s fact or out t he t ype of obj ect t hey’re displaying:

# urls.py

from django.conf.urls.defaults import *


from mysite import models, views

urlpatterns = patterns('',
(r'^events/$', views.object_list, {'model': models.Event}),
(r'^blog/entries/$', views.object_list, {'model': models.BlogEntry}),
)

# views.py

from django.shortcuts import render_to_response

def object_list(request, model):


obj_list = model.objects.all()
template_name = 'mysite/%s_list.html' % model.__name__.lower()
return render_to_response(template_name, {'object_list': obj_list})

Wit h t hose sm all changes, we suddenly have a reusable, m odel- agnost ic view! From now on, any t im e we need a view t hat list s a
set of obj ect s, we can sim ply reuse t his object_list view rat her t han writ ing view code. Here are a couple of not es about what
we did:

● We’re passing t he m odel classes direct ly, as t he model param et er. The dict ionary of ext ra URLconf opt ions can pass any t ype
of Pyt hon obj ect — not j ust st rings.

● The model.objects.all() line is an exam ple of duck t yping: “ I f it walks like a duck and t alks like a duck, we can t reat it
like a duck.” Not e t he code doesn’t know what t ype of obj ect model is; t he only requirem ent is t hat model have an objects
at t ribut e, which in t urn has an all() m et hod.

file:///D|/books/computer/programming/python/books/DJANGO BOOK/ch8.html (6 of 11)9/28/2007 2:27:13 PM


Chapter 8: Advanced views and URLconfs

● We’re using model.__name__.lower() in det erm ining t he t em plat e nam e. Every Pyt hon class has a __name__ at t ribut e t hat
ret urns t he class nam e. This feat ure is useful at t im es like t hese, when we don’t know t he t ype of class unt il runt im e.

For exam ple, t he BlogEntry class’ __name__ is t he st ring 'BlogEntry'.

● I n a slight difference bet ween t his exam ple and t he previous exam ple, we’re passing t he generic variable nam e object_list
t o t he t em plat e. We could easily change t his variable nam e t o be blogentry_list or event_list, but we’ve left t hat as an
exercise for t he reader.

Because dat abase- driven Web sit es have several com m on pat t erns, Dj ango com es wit h a set of “ generic views” t hat use t his
exact t echnique t o save you t im e. We’ll cover Dj ango’s built - in generic views in t he next chapt er.

Giving a view configuration options


I f you’re dist ribut ing a Dj ango applicat ion, chances are t hat your users will want som e degree of configurat ion. I n t his case, it ’s a
good idea t o add hooks t o your views for any configurat ion opt ions you t hink people m ay want t o change. You can use ext ra
URLconf param et ers for t his purpose.

A com m on bit of an applicat ion t o m ake configurable is t he t em plat e nam e:

def my_view(request, template_name):


var = do_something()
return render_to_response(template_name, {'var': var})

Precedence of captured values vs. extra options


When t here’s a conflict , ext ra URLconf param et ers get precedence over capt ured param et ers. I n ot her words, if your URLconf
capt ures a nam ed- group variable and an ext ra URLconf param et er includes a variable wit h t he sam e nam e, t he ext ra URLconf
param et er value will be used.

For exam ple, consider t his URLconf:

from django.conf.urls.defaults import *

urlpatterns = patterns('',
(r'^mydata/(?P<id>\d+)/$', views.my_view, {'id': 3}),
)

Here, bot h t he regular expression and t he ext ra dict ionary include an id. The hard- coded id get s precedence. That m eans any
request — e.g., /mydata/2/ or /mydata/432432/ — will be t reat ed as if id is set t o 3, regardless of t he value capt ured in t he URL.

Ast ut e readers will not e t hat in t his case, it ’s a wast e of t im e and t yping t o capt ure t he id in t he regular expression, because it s
value will always be overridden by t he dict ionary’s value. Those ast ut e readers would be correct . We bring t his up only t o help
you avoid m aking t he m ist ake.

Using default view arguments


Anot her convenient t rick is t o specify default param et ers for a view’s argum ent s. This t ells t he view which value t o use for a
param et er by default if none is specified.

For exam ple:

# urls.py
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^blog/$', views.page),
(r'^blog/page(?P<num>\d+)/$', views.page),
)

# views.py

def page(request, num="1"):


# Output the appropriate page of blog entries, according to num.
# ...

Here, bot h URL pat t erns point t o t he sam e view — views.page — but t he first pat t ern doesn’t capt ure anyt hing from t he URL. I f
t he first pat t ern m at ches, t he page() funct ion will use it s default argum ent for num, "1". I f t he second pat t ern m at ches, page()
will use what ever num value was capt ured by t he regex.

I t ’s com m on t o use t his t echnique in conj unct ion wit h configurat ion opt ions, as explained above. This exam ple m akes a slight

file:///D|/books/computer/programming/python/books/DJANGO BOOK/ch8.html (7 of 11)9/28/2007 2:27:13 PM


Chapter 8: Advanced views and URLconfs

im provem ent t o t he exam ple in t he Giving a view configurat ion opt ions sect ion by providing a default value for template_name:

def my_view(request, template_name='mysite/my_view.html'):


var = do_something()
return render_to_response(template_name, {'var': var})

Special-casing views
Som et im es you’ll have a pat t ern in your URLconf t hat handles a large set of URLs but you’ll need t o special- case one of t hem . I n
t his case, t ake advant age of t he linear way a URLconf is processed and put t he special case first .

For exam ple, t he “ add an obj ect ” pages in Dj ango’s adm in sit e are represent ed by t his URLconf line:

urlpatterns = patterns('',
# ...
('^([^/]+)/([^/]+)/add/$', 'django.contrib.admin.views.main.add_stage'),
# ...
)

This m at ches URLs such as /myblog/entries/add/ and /auth/groups/add/. However, t he “ add” page for a user obj ect
( /auth/user/add/) is a special case — it doesn’t display all of t he form fields, it displays t wo password fields, et c. We could solve
t his by special- casing in t he view, like so:

def add_stage(request, app_label, model_name):


if app_label == 'auth' and model_name == 'user':
# do special-case code
else:
# do normal code

…but t hat ’s inelegant for a reason we’ve t ouched on m ult iple t im es in t his chapt er: it put s URL logic in t he view. As a m ore
elegant solut ion, we can t ake advant age of t he fact t hat URLconfs are processed in order from t op t o bot t om :

urlpatterns = patterns('',
# ...
('^auth/user/add/$', 'django.contrib.admin.views.auth.user_add_stage'),
('^([^/]+)/([^/]+)/add/$', 'django.contrib.admin.views.main.add_stage'),
# ...
)

Wit h t his in place, a request t o /auth/user/add/ will be handled by t he user_add_stage view. Alt hough t hat URL m at ches t he
second pat t ern, it m at ches t he t op one first . ( This is short - circuit logic.)

Notes on capturing text in URLs


Each capt ured argum ent is sent t o t he view as a plain Pyt hon st ring, regardless of what sort of m at ch t he regular expression
m akes. For exam ple, in t his URLconf line:

(r'^articles/(?P<year>\d{4})/$', views.year_archive),

…t he year argum ent t o views.year_archive() will be a st ring, not an int eger, even t hough t he \d{4} will only m at ch int eger
st rings.

This is im port ant t o keep in m ind when you’re writ ing view code. Many built - in Pyt hon funct ions are fussy ( and right fully so)
about accept ing only obj ect s of a cert ain t ype. A com m on error is t o at t em pt t o creat e a datetime.date obj ect wit h st ring values
inst ead of int eger values:

>>> import datetime


>>> datetime.date('1993', '7', '9')
Traceback (most recent call last):
...
TypeError: an integer is required
>>> datetime.date(1993, 7, 9)
datetime.date(1993, 7, 9)

Translat ed t o a URLconf and view, t he error looks like t his:

# urls.py

from django.conf.urls.defaults import *

file:///D|/books/computer/programming/python/books/DJANGO BOOK/ch8.html (8 of 11)9/28/2007 2:27:13 PM


Chapter 8: Advanced views and URLconfs

urlpatterns = patterns('',
(r'^articles/(\d{4})/(\d{2})/(\d{2})/$', views.day_archive),
)
# views.py
import datetime
def day_archive(request, year, month, day)
# The following statement raises a TypeError!
date = datetime.date(year, month, day)

I nst ead, day_archive() can be writ t en correct ly like t his:

def day_archive(request, year, month, day)


date = datetime.date(int(year), int(month), int(day))

Not e t hat int() it self raises a ValueError when you pass it a st ring t hat is not com prised solely of digit s, but we’re avoiding t hat
error in t his case because t he regular expression in our URLconf has ensured t hat only st rings cont aining digit s are passed t o t he
view funct ion.

What the URLconf searches against


When a request com es in, Dj ango t ries t o m at ch t he URLconf pat t erns against t he request ed URL, as a norm al Pyt hon st ring ( not
as a Unicode st ring) . This does not include GET or POST param et ers, or t he dom ain nam e. I t also does not include t he leading
slash, because every URL has a leading slash.

For exam ple, in a request t o http://www.example.com/myapp/, Dj ango will t ry t o m at ch myapp/.

I n a request t o http://www.example.com/myapp/?page=3, Dj ango will t ry t o m at ch myapp/.

The request m et hod — e.g., POST, GET, HEAD — is not t aken int o account when t raversing t he URLconf. I n ot her words, all request
m et hods will be rout ed t o t he sam e funct ion for t he sam e URL. I t ’s t he responsibilit y of a view funct ion t o perform branching
based on request m et hod.

Including ot her URLconf s


At any point , your URLconf can “ include” ot her URLconf m odules. This essent ially “ root s” a set of URLs below ot her ones.

For exam ple, t his URLconf includes ot her URLconfs:

from django.conf.urls.defaults import *


urlpatterns = patterns('',
(r'^weblog/', include('mysite.blog.urls')),
(r'^photos/', include('mysite.photos.urls')),
(r'^about/$', 'mysite.views.about'),
)

There’s an im port ant got cha here: The regular expressions in t his exam ple t hat point t o an include() do not have a $ ( end- of-
st ring m at ch charact er) but do include a t railing slash. Whenever Dj ango encount ers include(), it chops off what ever part of t he
URL m at ched up t o t hat point and sends t he rem aining st ring t o t he included URLconf for furt her processing.

Cont inuing t his exam ple, here’s t he URLconf mysite.blog.urls:

from django.conf.urls.defaults import *


urlpatterns = patterns('',
(r'^(\d\d\d\d)/$', 'mysite.blog.views.year_detail'),
(r'^(\d\d\d\d)/(\d\d)/$', 'mysite.blog.views.month_detail'),
)

Wit h t hese t wo URLconfs, here’s how a few sam ple request s would be handled:

● /weblog/2007/ — I n t he first URLconf, t he pat t ern r'^weblog/' m at ches. Because it is an include(), Dj ango st rips all t he
m at ching t ext , which is 'weblog/' in t his case. The rem aining part of t he URL is 2007/, which m at ches t he first line in
t he mysite.blog.urls URLconf.
● /weblog//2007/ — I n t he first URLconf, t he pat t ern r'^weblog/' m at ches. Because it is an include(), Dj ango st rips all t he
m at ching t ext , which is 'weblog/' in t his case. The rem aining part of t he URL is /2007/ ( wit h a leading slash) , which does
not m at ch any of t he lines in t he mysite.blog.urls URLconf.

file:///D|/books/computer/programming/python/books/DJANGO BOOK/ch8.html (9 of 11)9/28/2007 2:27:13 PM


Chapter 8: Advanced views and URLconfs

● /about/ — Mat ches t he view mysite.views.about in t he first URLconf. This dem onst rat es t hat you can m ix include()
pat t erns wit h non- include() pat t erns.

How captured parameters work with include()


An included URLconf receives any capt ured param et ers from parent URLconfs. For exam ple:

# root urls.py

from django.conf.urls.defaults import *

urlpatterns = patterns('',
(r'^(?P<username>\w+)/blog/', include('foo.urls.blog')),
)

# foo/urls/blog.py

from django.conf.urls.defaults import *

urlpatterns = patterns('',
(r'^$', 'foo.views.blog_index'),
(r'^archive/$', 'foo.views.blog_archive'),
)

I n t his exam ple, t he capt ured username variable is passed t o t he included URLconf and, hence, t o every view funct ion wit hin t hat
URLconf.

Not e t hat t he capt ured param et ers will always be passed t o every line in t he included URLconf, regardless of whet her t he line’s
view act ually accept s t hose param et ers as valid. For t his reason, t his t echnique is only useful if you’re cert ain t hat every view in
t he t he included URLconf accept s t he param et ers you’re passing.

How extra URLconf options work with include()


Sim ilarly, you can pass ext ra URLconf opt ions t o include(), j ust as you can pass ext ra URLconf opt ions t o a norm al view — as a
dict ionary. When you do t his, each line in t he included URLconf will be passed t he ext ra opt ions.

For exam ple, t hese t wo URLconf set s are funct ionally ident ical:

Set one:

# urls.py

from django.conf.urls.defaults import *

urlpatterns = patterns('',
(r'^blog/', include('inner'), {'blogid': 3}),
)
# inner.py
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^archive/$', 'mysite.views.archive'),
(r'^about/$', 'mysite.views.about'),
(r'^rss/$', 'mysite.views.rss'),
)

Set t wo:

# urls.py

from django.conf.urls.defaults import *

urlpatterns = patterns('',
(r'^blog/', include('inner')),
)

# inner.py

from django.conf.urls.defaults import *

file:///D|/books/computer/programming/python/books/DJANGO BOOK/ch8.html (10 of 11)9/28/2007 2:27:13 PM


Chapter 8: Advanced views and URLconfs

urlpatterns = patterns('',
(r'^archive/$', 'mysite.views.archive', {'blogid': 3}),
(r'^about/$', 'mysite.views.about', {'blogid': 3}),
(r'^rss/$', 'mysite.views.rss', {'blogid': 3}),
)

Not e t hat ext ra opt ions will always be passed t o every line in t he included URLconf, regardless of whet her t he line’s view act ually
accept s t hose opt ions as valid. For t his reason, t his t echnique is only useful if you’re cert ain t hat every view in t he t he included
URLconf accept s t he ext ra opt ions you’re passing.

View t ricks
This chapt er is not yet finished. What else would you like t o see? Leave a com m ent on t his paragraph and let us know!
I nt eract ion is cool.

« previous ◊ t able of cont ent s ◊ next »


Copyright 2006 Adrian Holovat y and Jacob Kaplan- Moss.
This work is licensed under t he GNU Free Docum ent License.

file:///D|/books/computer/programming/python/books/DJANGO BOOK/ch8.html (11 of 11)9/28/2007 2:27:13 PM


Chapter 9: Generic views

The Dj ango Book


« previous ◊ t able of cont ent s ◊ next »

Chapt er 9: Generic views


Here again is a recurring t hem e of t his book: at it s worst , web developm ent is boring and m onot onous.

So far we’ve covered how Dj ango t ries t o t ake away som e of t hat m onot ony at t he m odel and t em plat e layers, but web
developers also experience t his boredom at t he view level.

Dj ango’s ge n e r ic vie w s were t o developed t o ease t hat pain. They t ake cert ain com m on idiom s and pat t erns in view
developm ent and abst ract t hem so t hat you can quickly writ e com m on views of ont o dat a wit hout having t o writ e t oo m uch code.

I n fact , nearly every view exam ple in t he preceding chapt ers could be re- writ t en wit h t he help of generic views.

Dj ango cont ains generic views t o do t he following:

● Perform com m on “ sim ple” t asks: redirect t o a different page, and render a given t em plat e.
● Display list and det ail pages for a single obj ect . For exam ple, t he Dj ango docum ent at ion index ( ht t p: / / www.dj angoproj ect .
com / docum ent at ion/ ) and individual docum ent pages are built t his way. The crim e index and list of crim es by t ype views
from Chapt er 5 could easily be re- writ t en t o use generic views; we’ll do so below.
● Present dat e- based obj ect s in year/ m ont h/ day archive pages, associat ed det ail and “ lat est ” pages. The Dj ango weblog’s
( ht t p: / / www.dj angoproj ect .com / weblog/ ) year, m ont h, and day archives are built wit h t hese, as are lj world.com ’s news
archives, and a whole host of ot hers.
● Allow users t o creat e, updat e, and delet e obj ect s — wit h or wit hout aut horizat ion.

Taken t oget her, t hese views provide easy int erfaces t o perform t he m ost com m on t asks developers encount er.

Using generic views


All of t hese views are used by creat ing configurat ion dict ionaries in your URLconf files and passing t hose dict ionaries as t he t hird
m em ber of t he URLconf t uple for a given pat t ern.

For exam ple, here’s t he URLconf for t he sim ple weblog app t hat drives t he blog on dj angoproj ect .com :

from django.conf.urls.defaults import *


from django_website.apps.blog.models import Entry

info_dict = {
'queryset': Entry.objects.all(),
'date_field': 'pub_date',
}

urlpatterns = patterns('django.views.generic.date_based',
(r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\w{1,2})/(?P<slug>[-\w]+)/$',
'object_detail', dict(info_dict, slug_field='slug')),
(r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\w{1,2})/$',
'archive_day', info_dict),
(r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/$',
'archive_month', info_dict),
(r'^(?P<year>\d{4})/$',
'archive_year', info_dict),
(r'^/?$',
'archive_index', info_dict),
)

As you can see, t his URLconf defines a few opt ions in info_dict. 'queryset' gives t he generic view a QuerySet of obj ect s t o use
( in t his case, all of t he Entry obj ect s) and t ells t he generic view which m odel is being used. The rem aining argum ent s t o each
generic view are t aken from t he nam ed capt ures in t he URLconf.

This is really all t he “ view” code for Dj ango’s weblog! The only t hing t hat ’s left is writ ing a t em plat e.

Docum ent at ion of each generic view follows, along wit h a list of all keyword argum ent s t hat a generic view expect s. Rem em ber
t hat as in t he exam ple above, argum ent s m ay eit her com e from t he URL pat t ern ( as month, day, year, et c. do above) or from t he
addit ional- inform at ion dict ionary ( as for queryset, date_field, et c.) .

Most generic views require t he queryset key, which is a QuerySet inst ance; see t he dat abase API reference in Appendix 3 for
m ore inform at ion about QuerySet obj ect s.

file:///D|/books/computer/programming/python/books/DJANGO BOOK/09.html (1 of 17)9/28/2007 2:27:38 PM


Chapter 9: Generic views

Most views also t ake an opt ional extra_context dict ionary t hat you can use t o pass any auxiliary inform at ion you wish t o t he
view. The values in t he extra_context dict ionary can be eit her funct ions ( or ot her callables) or ot her obj ect s. Funct ions are
evaluat ed j ust before t hey are passed t o t he t em plat e.

“ Simple” generic views


The django.views.generic.simple m odule cont ains sim ple views t o handle a couple of com m on cases: rendering a t em plat e
when no view logic is needed, and issuing a redirect .

Rendering a template
The funct ion django.views.generic.simple.direct_to_template renders a given t em plat e, passing it a {{ params }}
t em plat e variable, which is a dict ionary of t he param et ers capt ured in t he URL.

Example
Given t he following URL pat t erns:

urlpatterns = patterns('django.views.generic.simple',
(r'^foo/$', 'direct_to_template', {'template': 'foo_index.html'}),
(r'^foo/(?P<id>\d+)/$', 'direct_to_template', {'template': 'foo_detail.html'}),
)

a request t o /foo/ would render t he t em plat e foo_index.html, and a request t o /foo/15/ would render t he foo_detail.html
wit h a cont ext variable {{ params.id }} t hat is set t o 15.

Required arguments
template
The full nam e of a t em plat e t o use.

Redirecting to another URL


django.views.generic.simple.redirect_to redirect s t o anot her URL. The given URL m ay cont ain dict ionary- st yle st ring
form at t ing, which will be int erpolat ed against t he param et ers capt ured in t he URL.

I f t he given URL is None, Dj ango will ret urn an HTTP 410 ( Gone) m essage.

Example
This exam ple redirect s from /foo/<id>/ t o /bar/<id>/:

urlpatterns = patterns('django.views.generic.simple',
('^foo/(?p<id>\d+)/$', 'redirect_to', {'url': '/bar/%(id)s/'}),
)

This exam ple ret urns a 410 HTTP error for request s t o /bar/:

urlpatterns = patterns('django.views.generic.simple',
('^bar/$', 'redirect_to', {'url': None}),
)

Required arguments
url
The URL t o redirect t o, as a st ring. Or None t o ret urn a 410 ( “ gone” ) HTTP response.

More complex generic views


Alt hough t he sim ple generic views cert ainly are useful, t he real power in Dj ango’s generic views com es from t he m ore com plex
views t hat allow you t o build com m on CRUD ( Creat e/ Ret rieve/ Updat e/ Delet e) pages wit h a m inim um am ount of code.

These views break down int o a few different t ypes:

● List / det ail views, which provide flat list s of obj ect s and individual obj ect det ail pages ( for exam ple, a list of places and
individual place inform at ion pages) .
● Dat e- based views, which provide year/ m ont h/ day drill- down pages of dat e- cent ric inform at ion.
● Creat e/ updat e/ delet e views, which allow you t o quickly creat e views t o creat e, m odify, or delet e obj ect s.

file:///D|/books/computer/programming/python/books/DJANGO BOOK/09.html (2 of 17)9/28/2007 2:27:38 PM


Chapter 9: Generic views

Common optional arguments


Most of t hese views t ake a large num ber of opt ional argum ent s t hat can cont rol various bit s of behavior. Many of t hese
argum ent s m ay be given t o any of t hese views, so m any of t he views below refer back t o t his list of opt ional argum ent s:

allow_empty
A boolean specifying whet her t o display t he page if no obj ect s are available. I f t his is False and no obj ect s are available, t he
view will raise a 404 inst ead of displaying an em pt y page. By default , t his is False.

context_processors
A list of t em plat e- cont ext processors t o apply t o t he view’s t em plat e. See Chapt er 10 for inform at ion on t em plat e cont ext
processors.

extra_context
A dict ionary of values t o add t o t he t em plat e cont ext . By default , t his is an em pt y dict ionary. I f a value in t he dict ionary is
callable, t he generic view will call it j ust before rendering t he t em plat e.

mimetype
The MI ME t ype t o use for t he result ing docum ent . Default s t o t he value of t he DEFAULT_MIME_TYPE set t ing.

template_loader
The t em plat e loader t o use when loading t he t em plat e. By default , it ’s django.template.loader. See Chapt er 10 for
inform at ion on t em plat e loaders.

template_name
The full nam e of a t em plat e t o use in rendering t he page. This let s you override t he default t em plat e nam e derived from
t he QuerySet.

template_object_name
Designat es t he nam e of t he t em plat e variable t o use in t he t em plat e cont ext . By default , t his is 'object'. Views list list m ore
t han one obj ec will append '_list' t o t he value of t his param et er.

List / det ail generic views


The list - det ail generic views ( in t he m odule django.views.generic.list_detail) handles t he com m on case of displaying a list
of it em s at one view, and individual “ det ail” views of t hose it em s at anot her.

For t he exam ples in t he rest of t his chapt er, we’ll be working wit h t he sim ple book/ aut hor/ publisher obj ect s from chapt ers 5 and
6:

class Publisher(models.Model):
name = models.CharField(maxlength=30)
address = models.CharField(maxlength=50)
city = models.CharField(maxlength=60)
state_province = models.CharField(maxlength=30)
country = models.CharField(maxlength=50)
website = models.URLField()

class Author(models.Model):
salutation = models.CharField(maxlength=10)
first_name = models.CharField(maxlength=30)
last_name = models.CharField(maxlength=40)
email = models.EmailField()
headshot = models.ImageField()
class Book(models.ModelField):
title = models.CharField(maxlength=100)
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher)
publication_date = models.DateField()

We’ll also be working wit h a URL m odule; if you’re following along, you can st art wit h an skelet on URL config in bookstore.urls:

from django.conf.urls.defaults import *


from django.views.generic import list_detail, date_based, create_update
from bookstore.models import Publisher, Author, Book

urlpatterns = patterns('',
# We'll add URL patterns here.
)

We’ll build t his up wit h generic views as we go.

Lists of obj ects

file:///D|/books/computer/programming/python/books/DJANGO BOOK/09.html (3 of 17)9/28/2007 2:27:38 PM


Chapter 9: Generic views

The view django.views.generic.list_detail.object_list is used t o creat e a page represent ing a list of obj ect s.

Example
We can use t he object_list view t o show a sim ple list of all aut hors in t he bookst ore. First , we’ll need t o const ruct a info
dict ionary for t he generic view. Add t he following t o t he t op of t he bookstore/urls.py file:

author_list_info = {
'queryset' : Author.objects.all(),
'allow_empty': True,
}

Then, we need t o regist er t his view at a cert ain URL. We can do t hat by adding t his URL config piece ( inside t he patterns
direct ive) :

(r'authors/$', list_detail.object_list, author_list_info)

From t here, we j ust need t o m ake a t em plat e for t his generic view t o render. Since we didn’t provide t he template_name
param et er ( see below) , Dj ango will guess t he nam e of t he t em plat e; here it ’ll use bookstore/author_list.html. See below for
m ore det ails on how t his “ guess” is m ade.

Required arguments
queryset
A QuerySet of obj ect s t o list

Optional arguments
paginate_by
An int eger specifying how m any obj ect s should be displayed per page. I f t his is given, t he view will paginat e obj ect s
wit h paginate_by obj ect s per page. The view will expect eit her a page query st ring param et er ( via GET) cont aining a zero-
indexed page num ber, or a page variable specified in t he URLconf. See “ Not es on paginat ion” below.

Addit ionally, t his view m ay t ake any of t hese com m on argum ent s described above:

● allow_empty
● context_processors
● extra_context
● mimetype
● template_loader
● template_name
● template_object_name

Template name
I f template_name isn’t specified, t his view will use t he t em plat e <app_label>/<model_name>_list.html by default . Bot h t he app
label and t he m odel nam e are derived from t he queryset param et er: t he app label is t he nam e of t he app t hat t he m odel is
defined in, and t he m odel nam e is t he lower- cased version of t he nam e of t he m odel class.

So, if we passed Author.objects.all() as t he queryset, t he app label would be bookstore and t he m odel nam e would
be author. This m eans t he default t em plat e would be bookstore/author_list.html.

Template context
I n addit ion t o extra_context, t he t em plat e’s cont ext will cont ain:

object_list
The list of obj ect s. This variable’s nam e depends on t he template_object_name param et er, which is 'object' by default .
I f template_object_name is 'foo', t his variable’s nam e will be foo_list.

is_paginated
A boolean represent ing whet her t he result s are paginat ed. Specifically, t his is set t o False if t he num ber of available obj ect s is
less t han or equal t o paginate_by.

I f t he result s are paginat ed, t he cont ext will cont ain t hese ext ra variables:

results_per_page
The num ber of obj ect s per page. ( Sam e as t he paginate_by param et er.)

has_next

file:///D|/books/computer/programming/python/books/DJANGO BOOK/09.html (4 of 17)9/28/2007 2:27:38 PM


Chapter 9: Generic views

A boolean represent ing whet her t here’s a next page.

has_previous
A boolean represent ing whet her t here’s a previous page.

page
The current page num ber, as an int eger. This is 1- based.

next
The next page num ber, as an int eger. I f t here’s no next page, t his will st ill be an int eger represent ing t he t heoret ical next - page
num ber. This is 1- based.

previous
The previous page num ber, as an int eger. This is 1- based.

pages
The t ot al num ber of pages, as an int eger.

hits
The t ot al num ber of obj ect s across all pages, not j ust t his page.

A n ot e on pa gin a t ion :
I f paginate_by is specified, Dj ango will paginat e t he result s. You can specify t he page num ber in t he URL in one of
t wo ways:

● Use t he page param et er in t he URLconf. For exam ple, t his is what your URLconf m ight look like:

(r'^objects/page(?P<page>[0-9]+)/$', 'object_list', dict(info_dict))

● Pass t he page num ber via t he page query- st ring param et er. For exam ple, a URL would look like t his:

/ obj ect s/ ?page=3

I n bot h cases, page is 1- based, not 0- based, so t he first page would be represent ed as page 1.

Detail views
The django.views.generic.list_detail.object_detail gives a “ det ail” view of a single obj ect .

Example
Ext ending t he exam ple above, we could m ake a det ail view for a given aut hor. Given an info dict like t his:

author_detail_info = {
"queryset" : Author.objects.all(),
"template_object_name" : "author",
}

We could use a urlpat t ern like:

(r'^authors/(?P<object_id>\d+)/$', list_detail.object_detail, author_detail_info),

t o show det ails about a given book, rendered in t he bookstore/author_detail.html t em plat e. I n t hat t em plat e, t he Author
obj ect it self would be put int o t he {{ author }} variable.

Required arguments
queryset `
A QuerySet t hat will be searched for t he obj ect .

Eit her:

object_id
The value of t he prim ary- key field for t he obj ect .

or:

slug
The slug of t he given obj ect . I f you pass t his field, t hen t he slug_field argum ent ( below) is also required.

file:///D|/books/computer/programming/python/books/DJANGO BOOK/09.html (5 of 17)9/28/2007 2:27:38 PM


Chapter 9: Generic views

Optional arguments
slug_field
The nam e of t he field on t he obj ect cont aining t he slug. This is required if you are using t he slug argum ent , but m ust be
absent if you’re using t he object_id argum ent .

template_name_field
The nam e of a field on t he obj ect whose value is t he t em plat e nam e t o use. This let s you st ore t em plat e nam es in your dat a.

I n ot her words, if your obj ect has a field 'the_template' t hat cont ains a st ring 'foo.html', and you set template_name_field
t o 'the_template', t hen t he generic view for t his obj ect will use t he t em plat e 'foo.html'.

I t ’s a bit of a brain- bender, but it ’s useful in som e cases.

This view m ay also t ake t hese com m on argum ent s ( docum ent ed above) :

● context_processors
● extra_context
● mimetype
● template_loader
● template_name
● template_object_name

Template name
I f template_name and template_name_field aren’t specified, t his view will use t he
t em plat e <app_label>/<model_name>_detail.html by default .

Template context
I n addit ion t o extra_context, t he t em plat e’s cont ext will be:

object
The obj ect . This variable’s nam e depends on t he template_object_name param et er, which is 'object' by default .
I f template_object_name is 'foo', t his variable’s nam e will be foo.

Dat e-based generic views


Dat e- based generic views are generally used t o provide a set of “ archive” pages for dat ed m at erial. Think year/ m ont h/ day
archives for a newspaper, or a blog like t he official Dj ango blog described at t he beginning of t his chapt er.

For t he exam ples, we’ll be using t he Book obj ect from above, and build up a way t o browse books by year, m ont h, and day
published. Not ice t hat for each of t hese views, we have t o t ell Dj ango t he nam e of t he dat e field we want t o key off of. We have
t o provide t his inform at ion since m odels could cont ain m ult iple dat e or dat et im e fields.

I n t o t h e fu t u r e …
By default , t hese views ignore obj ect s wit h dat es in t he fut ure.

This m eans t hat if you t ry t o visit an archive page in t he fut ure, Dj ango will aut om at ically show a 404 ( “ not found” )
error, even if t here are obj ect s published t hat day.

Thus, you can publish post - dat ed obj ect s t hat don’t appear publically unt il aft er t heir publicat ion dat e.

However, for different t ypes of dat e- based obj ect s t his isn’t appropriat e ( for exam ple, a calendar of upcom ing
event s) . For t hese views, set t ing t he allow_future opt ion t o True will m ake t he fut ure obj ect s appear ( and allow
users t o visit “ fut ure” archive pages) .

Archive index
The django.views.generic.date_based.archive_index view provides a t op- level index page showing t he “ lat est ” obj ect s, by
dat e.

Example
A t ypical publisher probably want s t o highlight recent ly- published books. We can use t he archive_index view for t his com m on
t ask. Here’s a info dict :

book_info = {

file:///D|/books/computer/programming/python/books/DJANGO BOOK/09.html (6 of 17)9/28/2007 2:27:38 PM


Chapter 9: Generic views

"queryset" : Book.objects.all(),
"date_field" : "publication_date"
}

And t he corresponding urlconf piece ( which root s t his index at t he bot t om level of wherever it ’s included) :

(r'^books/$', date_based.archive_index, book_info),

Required arguments
date_field:
The nam e of t he DateField or DateTimeField in t he QuerySet‘s m odel t hat t he dat e- based archive should use t o det erm ine
t he obj ect s on t he page.

queryset
A QuerySet of obj ect s for which t he archive serves.

Optional arguments
allow_future
A boolean specifying whet her t o include “ fut ure” obj ect s on t his page, as described in t he not e above.

num_latest
The num ber of lat est obj ect s t o send t o t he t em plat e cont ext . By default , it ’s 15.

This view m ay also t ake t hese com m on argum ent s ( docum ent ed above) :

● allow_empty
● context_processors
● extra_context
● mimetype
● template_loader
● template_name

Template name
I f template_name isn’t specified, t his view will use t he t em plat e <app_label>/<model_name>_archive.html by default .

Template context
I n addit ion t o extra_context, t he t em plat e’s cont ext will be:

date_list
A list of datetime.date obj ect s represent ing all years t hat have obj ect s available according t o queryset. These are ordered in
reverse.

For exam ple, if you have blog ent ries from 2003 t hrough 2006, t his list will cont ain four datetime.date obj ect s: one for each
of t hose years.

latest
The num_latest obj ect s in t he syst em , ordered descending by date_field. For exam ple, if num_latest is 10, t hen latest will
be a list of t he lat est 10 obj ect s in queryset.

Year archives
The django.views.generic.date_based.archive_year view provides a yearly archive page showing all available m ont hs in a
given year.

Example
Cont ut ing on wit h our exam ple, we’ll want t o add a way t o view all t he books published in a given year. We can keep using
t he book_info dict ionary from t he above exam ple, but t his t im e we’ll wire it up t o t he archive_year view:

(r'^books/(?P<year>\d{4})/?$', date_based.archive_year, book_info),

Since t here are likely m any, m any books published each year, we won’t display t hem on t his page, j ust a list of years in which
books are available. Convenient ly for us, t his is what Dj ango does by default ; t o change it we could use t he make_object_list
argum ent ; see below.

file:///D|/books/computer/programming/python/books/DJANGO BOOK/09.html (7 of 17)9/28/2007 2:27:38 PM


Chapter 9: Generic views

Required arguments
date_field
As above.

queryset
A QuerySet of obj ect s for which t he archive serves.

year
The four- digit year for which t he archive serves ( usually t aken from URL param et ers) .

Optional arguments
make_object_list
A boolean specifying whet her t o ret rieve t he full list of obj ect s for t his year and pass t hose t o t he t em plat e. I f True, t his list of
obj ect s will be m ade available t o t he t em plat e as object_list. ( The nam e object_list m ay be different ; see t he inform at ion
about object_list in t he “ Tem plat e cont ext ” sect ion below.) By default , t his is False.

allow_future
A boolean specifying whet her t o include “ fut ure” obj ect s on t his page, as described in t he not e above.

This view m ay also t ake t hese com m on argum ent s ( docum ent ed above) :

● allow_empty
● context_processors
● extra_context
● mimetype
● template_loader
● template_name
● template_object_name

Template name
I f template_name isn’t specified, t his view will use t he t em plat e <app_label>/<model_name>_archive_year.html by default .

Template context
I n addit ion t o extra_context, t he t em plat e’s cont ext will be:

date_list
A list of datetime.date obj ect s represent ing all m ont hs t hat have obj ect s available in t he given year, according t o queryset,
in ascending order.

year
The given year, as a four- charact er st ring.

object_list
I f t he make_object_list param et er is True, t his will be set t o a list of obj ect s available for t he given year, ordered by t he dat e
field. This variable’s nam e depends on t he template_object_name param et er, which is 'object' by default .
I f template_object_name is 'foo', t his variable’s nam e will be foo_list.

I f make_object_list is False, object_list will be passed t o t he t em plat e as an em pt y list .

Monthly archives
The django.views.generic.date_based.archive_month views provides a m ont hly archive page showing all obj ect s in a given
m ont h.

Example
Cont inuing on wit h our exam ple, creat ing m ont h views should look m ight y fam iliar:

(r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/$', date_based.archive_month, book_info),

Required arguments
year
The four- digit year for which t he archive serves ( a st ring) .

month
The m ont h for which t he archive serves, form at t ed according t o t he month_format argum ent .

file:///D|/books/computer/programming/python/books/DJANGO BOOK/09.html (8 of 17)9/28/2007 2:27:38 PM


Chapter 9: Generic views

queryset
A QuerySet of obj ect s for which t he archive serves.

date_field
The nam e of t he DateField or DateTimeField in t he QuerySet‘s m odel t hat t he dat e- based archive should use t o det erm ine
t he obj ect s on t he page.

Optional arguments
month_format
A form at st ring t hat regulat es what form at t he month param et er uses. This should be in t he synt ax accept ed by
Pyt hon’s time.strftime. ( See Pyt hon’s st rft im e docs at ht t p: / / www.pyt hon.org/ doc/ current / lib/ m odule- t im e.ht m l# l2h- 1941)
I t ’s set t o "%b" by default , which is a t hree- let t er m ont h abbreviat ion ( i.e. “ j an” , “ feb” , et c.) . To change it t o use num bers,
use "%m".

allow_future
A boolean specifying whet her t o include “ fut ure” obj ect s on t his page, as described in t he not e above.

This view m ay also t ake t hese com m on argum ent s ( docum ent ed above) :

● allow_empty
● context_processors
● extra_context
● mimetype
● template_loader
● template_name
● template_object_name

Template name
I f template_name isn’t specified, t his view will use t he t em plat e <app_label>/<model_name>_archive_month.html by default .

Template context
I n addit ion t o extra_context, t he t em plat e’s cont ext will be:

month
A datetime.date obj ect represent ing t he given m ont h.

next_month
A datetime.date obj ect represent ing t he first day of t he next m ont h. I f t he next m ont h is in t he fut ure, t his will be None.

previous_month
A datetime.date obj ect represent ing t he first day of t he previous m ont h. Unlike next_month, t his will never be None.

object_list
A list of obj ect s available for t he given m ont h. This variable’s nam e depends on t he template_object_name param et er, which
is 'object' by default . I f template_object_name is 'foo', t his variable’s nam e will be foo_list.

Week archives
The django.views.generic.date_based.archive_week view shows all obj ect s in a given week.

N ot e
Dj ango believes t hat weeks st art on Sunday, for t he perfect ly arbit rary reason t hat Pyt hon does, t oo.

Example
Are you st art ing t o see a pat t ern here yet ?

(r'^(?P<year>\d{4})/(?P<week>\d{2})/$', date_based.archive_week, book_info),

Required arguments
year
The four- digit year for which t he archive serves ( a st ring) .

week
The week of t he year for which t he archive serves ( a st ring) .

file:///D|/books/computer/programming/python/books/DJANGO BOOK/09.html (9 of 17)9/28/2007 2:27:38 PM


Chapter 9: Generic views

queryset
A QuerySet of obj ect s for which t he archive serves.

date_field
The nam e of t he DateField or DateTimeField in t he QuerySet‘s m odel t hat t he dat e- based archive should use t o det erm ine
t he obj ect s on t he page.

Opt ional argum ent s

allow_future
A boolean specifying whet her t o include “ fut ure” obj ect s on t his page, as described in t he not e above.

This view m ay also t ake t hese com m on argum ent s ( docum ent ed above) :

● allow_empty
● context_processors
● extra_context
● mimetype
● template_loader
● template_name
● template_object_name

Template name
I f template_name isn’t specified, t his view will use t he t em plat e <app_label>/<model_name>_archive_week.html by default .

Template context
I n addit ion t o extra_context, t he t em plat e’s cont ext will be:

week
A datetime.date obj ect represent ing t he first day of t he given week.

object_list
A list of obj ect s available for t he given week. This variable’s nam e depends on t he template_object_name param et er, which
is 'object' by default . I f template_object_name is 'foo', t his variable’s nam e will be foo_list.

Day archives
The django.views.generic.date_based.archive_day view provides a page showing all obj ect s in a given day.

Example
Keep on keepin’ on:

(r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\d{2})/$', date_based.archive_day, book_info),

Required arguments
year
The four- digit year for which t he archive serves ( a st ring) .

month
The m ont h for which t he archive serves, form at t ed according t o t he month_format argum ent .

day
The day for which t he archive serves, form at t ed according t o t he day_format argum ent .

queryset
A QuerySet of obj ect s for which t he archive serves.

date_field
The nam e of t he DateField or DateTimeField in t he QuerySet‘s m odel t hat t he dat e- based archive should use t o det erm ine
t he obj ect s on t he page.

Optional arguments
month_format
A form at st ring t hat regulat es what form at t he month param et er uses. See t he det ailed explanat ion above.

day_format
Like month_format, but for t he day param et er. I t default s t o "%d" ( day of t he m ont h as a decim al num ber, 01- 31) .

file:///D|/books/computer/programming/python/books/DJANGO BOOK/09.html (10 of 17)9/28/2007 2:27:38 PM


Chapter 9: Generic views

allow_future
A boolean specifying whet her t o include “ fut ure” obj ect s on t his page, as described in t he not e above.

This view m ay also t ake t hese com m on argum ent s ( docum ent ed above) :

● allow_empty
● context_processors
● extra_context
● mimetype
● template_loader
● template_name
● template_object_name

Template name
I f template_name isn’t specified, t his view will use t he t em plat e <app_label>/<model_name>_archive_day.html by default .

Template context
I n addit ion t o extra_context, t he t em plat e’s cont ext will be:

day
A datetime.date obj ect represent ing t he given day.

next_day
A datetime.date obj ect represent ing t he next day. I f t he next day is in t he fut ure, t his will be None.

previous_day
A datetime.date obj ect represent ing t he given day. Unlike next_day, t his will never be None.

object_list
A list of obj ect s available for t he given day. This variable’s nam e depends on t he template_object_name param et er, which
is 'object' by default . I f template_object_name is 'foo', t his variable’s nam e will be foo_list.

Archive for today


The django.views.generic.date_based.archive_today view shows all obj ect s for t oday. This is exact ly t he sam e
as archive_day, except t he year/ month/ day argum ent s are not used, and t oday’s dat e is used inst ead.

Date-based detail pages


The django.views.generic.date_based.object_detail view shows a page represent ing an individual obj ect . This differs from
t he object_detail page in t heir respect ive URLs; t he object_detail view uses URLs like /entries/<slug>/, while t his one
uses URLs like /entries/2006/aug/27/<slug>/.

N ot e
I f you’re using dat e- based det ail pages wit h slugs in t he URLs, you probably also want t o use t he unique_for_date
opt ion on t he slug field t o validat e t hat slugs aren’t duplicat ed in a single day. See Appendix 2 for det ails
on unique_for_date.

Example
This one differs ( slight ly) from all t he ot her exam ples in t hat we need t o eit her provide an obj ect I D or a slug so t hat Dj ango can
look up t he obj ect in quest ion.

Since t he obj ect we’re using doesn’t have a slug field, we’ll use t he slight ly uglyier I D- based URLs. I n pract ice we’d prefer t o use
a slug field, but in t he int erest of sim plicit y we’ll let it go.

We’ll add t he following t o t he URLconf:

(r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\d{2})/(?P<object_id>[\w-]+)/$', date_based.
object_detail, book_info),

Required arguments
year
The obj ect ’s four- digit year ( a st ring) .

month

file:///D|/books/computer/programming/python/books/DJANGO BOOK/09.html (11 of 17)9/28/2007 2:27:38 PM


Chapter 9: Generic views

The obj ect ’s m ont h , form at t ed according t o t he month_format argum ent .

day
The obj ect ’s day , form at t ed according t o t he day_format argum ent .

queryset
A QuerySet t hat cont ains t he obj ect .

date_field
The nam e of t he DateField or DateTimeField in t he QuerySet‘s m odel t hat t he generic view should use t o look up t he obj ect
according t o year, month and day.

Eit her:

object_id
The value of t he prim ary- key field for t he obj ect .

or:

slug
The slug of t he given obj ect . I f you pass t his field, t hen t he slug_field argum ent ( below) is also required.

Optional arguments
allow_future
A boolean specifying whet her t o include “ fut ure” obj ect s on t his page, as described in t he not e above.

day_format
Like month_format, but for t he day param et er. I t default s t o "%d" ( day of t he m ont h as a decim al num ber, 01- 31) .

month_format
A form at st ring t hat regulat es what form at t he month param et er uses. See t he det ailed explanat ion above.

slug_field
The nam e of t he field on t he obj ect cont aining t he slug. This is required if you are using t he slug argum ent , but m ust be
absent if you’re using t he object_id argum ent .

template_name_field
The nam e of a field on t he obj ect whose value is t he t em plat e nam e t o use. This let s you st ore t em plat e nam es in t he dat a. I n
ot her words, if your obj ect has a field 'the_template' t hat cont ains a st ring 'foo.html', and you set template_name_field
t o 'the_template', t hen t he generic view for t his obj ect will use t he t em plat e 'foo.html'.

I t ’s a bit of a brain- bender, but it ’s useful in som e cases.

This view m ay also t ake t hese com m on argum ent s ( docum ent ed above) :

● context_processors
● extra_context
● mimetype
● template_loader
● template_name
● template_object_name

Template name
I f template_name isn’t specified, t his view will use t he t em plat e <app_label>/<model_name>_detail.html by default .

Template context
I n addit ion t o extra_context, t he t em plat e’s cont ext will be:

object
The obj ect . This variable’s nam e depends on t he template_object_name param et er, which is 'object' by default .
I f template_object_name is 'foo', t his variable’s nam e will be foo.

Creat e/ updat e/ delet e generic views

N ot e
These views will change slight ly when Dj ango’s revised form archit ect ure ( current ly under developm ent
as django.newforms) is finalized. This sect ion will be updat ed accordingly.

file:///D|/books/computer/programming/python/books/DJANGO BOOK/09.html (12 of 17)9/28/2007 2:27:38 PM


Chapter 9: Generic views

The django.views.generic.create_update m odule cont ains a set of funct ions for creat ing, edit ing and delet ing obj ect s.

Create obj ect view


The django.views.generic.create_update.create_object view displays a form for creat ing an obj ect , redisplays t he form wit h
validat ion errors ( if t here are any) and saves t he obj ect . This uses t he aut om at ic m anipulat ors t hat com e wit h Dj ango m odels.

These views all present form s if accessed wit h a GET and perform t he request ed act ion ( creat e/ updat e/ delet e) if accessed via POST
.

Not e t hat t hese views all have a very rough idea of securit y. Alt hough t hey t ake a login_required at t ribut e which if given will
rest rict access t o logged- in users, t hat ’s as far as it goes. They won’t , for exam ple, check t hat t he user edit ing an obj ect is t he
sam e user t hat creat ed it , nor will t hey validat e any sort of perm issions.

Much of t he t im e, however, t hose feat ures can be accom plished by writ ing a sm all wrapper around t he generic view; see
“ ext ending generic views” , below, for m ore about t his t opic.

Example
I f we want ed t o allow users t o creat e new books in our dat abase, we could do som et hing like t his:

(r'^books/create/$', create_update.create_object, {'model' : Book}),

Required arguments
model
The Dj ango m odel of t he obj ect t hat t he form will creat e.

N ot e
Not ice t hat t his view t akes t he m odel t o be creat ed, not a QuerySet ( as all t he list / det ail/ dat e- based views above
do) .

Optional arguments
post_save_redirect
A URL t o which t he view will redirect aft er saving t he obj ect . By default , it ’s object.get_absolute_url().

post_save_redirect
May cont ain dict ionary st ring form at t ing, which will be int erpolat ed against t he obj ect ’s field at t ribut es. For exam ple, you could
use post_save_redirect="/polls/%(slug)s/".

login_required
A boolean t hat designat es whet her a user m ust be logged in, in order t o see t he page and save changes. This hooks int o t he
Dj ango aut hent icat ion syst em . By default , t his is False.

I f t his is True, and a non- logged- in user at t em pt s t o visit t his page or save t he form , Dj ango will redirect t he request
t o /accounts/login/.

This view m ay also t ake t hese com m on argum ent s ( docum ent ed above) :

● context_processors
● extra_context
● template_loader
● template_name

Template name
I f template_name isn’t specified, t his view will use t he t em plat e <app_label>/<model_name>_form.html by default .

Template context
I n addit ion t o extra_context, t he t em plat e’s cont ext will be:

form:
A FormWrapper inst ance represent ing t he form for edit ing t he obj ect . This let s you refer t o form fields easily in t he t em plat e
syst em .

For exam ple, if t he m odel has t wo fields, name and address:

file:///D|/books/computer/programming/python/books/DJANGO BOOK/09.html (13 of 17)9/28/2007 2:27:38 PM


Chapter 9: Generic views

<form action="" method="post">


<p><label for="id_name">Name:</label> {{ form.name }}</p>
<p><label for="id_address">Address:</label> {{ form.address }}</p>
</form>

See Chapt er 7 for m ore inform at ion about working wit h form s.

Update obj ect view


The django.views.generic.create_update.update_object view is alm ost ident ical t o t he creat e- obj ect view above, but t his
one allows t he edit ing of an exist ing obj ect inst ead of t he creat ion of a new one.

Example
Following t he above exam ple, we could provide an edit int erface for a single book wit h t his URLconf snippet :

(r'^books/edit/(?P<object_id>\d+)/$', create_update.update_object, {'model' : Book}),

Required arguments
model
The Dj ango m odel t he form will be edit ing.

Eit her:

object_id
The value of t he prim ary- key field for t he obj ect .

or:

slug
The slug of t he given obj ect . I f you pass t his field, t hen t he slug_field argum ent ( below) is also required.

Optional arguments
slug_field
The nam e of t he field on t he obj ect cont aining t he slug. This is required if you are using t he slug argum ent , but m ust be
absent if you’re using t he object_id argum ent .

Addit ionally, t his view t akes all sam e opt ional argum ent s as t he creat ion view ( above) , plus t he template_object_name com m on
argum ent .

Template name
This view uses t he sam e default t em plat e nam e ( <app_label>/<model_name>_form.html) as t he creat ion view.

Template context
I n addit ion t o extra_context, t he t em plat e’s cont ext will be:

form:
A FormWrapper inst ance represent ing t he form for edit ing t he obj ect . See t he creat e obj ect ( above) for m ore about t his value.

object:
The original obj ect being edit ed ( t his variable m ay be nam ed different ly if you’ve provided t he template_object_name
argum ent ) .

Delete obj ect view


The django.views.generic.create_update.delete_object view is also very sim ilar t o t he ot her t wo.

I f t his view is fet ched wit h GET, it will display a confirm at ion page ( i.e. “ do you really want t o delet e t his obj ect ?” ) . I f t he view is
subm it t ed wit h POST, t he obj ect will be delet ed wit hout confirm at ion.

All t he argum ent s are t he sam e as for t he updat e obj ect view, as is t he cont ext ; t he t em plat e nam e for t his view
is <app_label>/<model_name>_confirm_delete.html

Ext ending generic views


There’s no quest ion t hat using generic views can speed up developm ent subst ant ially. I n m ost proj ect s, however, t here com es a

file:///D|/books/computer/programming/python/books/DJANGO BOOK/09.html (14 of 17)9/28/2007 2:27:38 PM


Chapter 9: Generic views

m om ent when t he generic views no longer suffice. I ndeed, t he m ost com m on quest ion asked by new Dj ango developers is about
how t o m ake generic views handle a wider array of sit uat ions.

Luckily, in nearly every one of t hese cases, t here are ways t o sim ply ext end generic views t o handle a larger array of use cases.
These sit uat ions usually fall int o a couple of pat t erns:

Adding extra context


Oft en you sim ply need t o present som e ext ra inform at ion t han t hat provided by t he generic view. For exam ple, t hink of showing a
list of all publishers on a book’s det ail page; t he object_detail generic view provides t he book t o t he cont ext , but it seem s
t here’s no way t o get a list of publishers in t hat t em plat e.

But t here is: all generic views t ake an ext ra opt ional param et er extra_context. This is a dict ionary of ext ra obj ect s which will be
added t o t he t em plat e’s cont ext . So, t o provide t he list of publishers in t he book det ail view, we’d use an info dict like t his:

book_info = {
"queryset" : Book.objects.all(),
"date_field" : "publication_date",
"extra_context" : {
"publisher_list" : Publisher.objects.all(),
}
}

This would populat e a {{ publisher_list }} variable in t he t em plat e cont ext . This pat t ern can be used t o pass any inform at ion
down int o t he t em plat e for t he generic view; it ’s very handy.

More complex filtering with wrapper functions


Anot her com m on need is t o filt er down t he obj ect s given in a list page by som e key in t he URL. For exam ple, let ’s look at
providing an int erface t o browse books by t it le. We’d like t o provide URLs of t he form /books/by-title/a/, /books/by-title/b/
, et c. — one list page for each let t er of t he alphabet .

The problem seem s t o be t hat t he generic view has no concept of reading variables from t he URL; if we wired a URL pat t ern
m at ching t hose URLs up t o t he object_list view, we’d get t went y- six pages displaying all t he books. Alt hough we could writ e
t went y- six different info dict s ( each wit h a different queryset argum ent ) , t hat ’s j ust silly. The right t echnique involves writ ing a
sim ple “ wrapper” funct ion around t he generic view.

I n our alphabet ic- browsing exam ple, we’d st art by adding a sm all bit t o t he URLconf:

from bookstore.views import browse_alphabetically

urlpatterns = patterns('',
# ...
(r'^books/by-title/([a-z])/$', browse_alphabetically)
)

As you can see, t his wires t he set of URLs t o t he browse_alphabetically funct ion, so let ’s t ake a look at how t hat funct ion could
be writ t en:

from bookstore.models. import Book


from django.views.generic import list_detail

def browse_alphabetically(request, letter):


return list_detail.object_list(
request,
queryset = Book.objects.filter(title__istartswith=letter),
template_name = "bookstore/browse_alphabetically.html",
extra_context = {
'letter' : letter,
}
)

That ’s it !

This works because t here’s really not hing special about generic views — t hey’re j ust Pyt hon funct ions. Like any view funct ion,
generic views expect a cert ain set of argum ent s and ret urn HttpResponse obj ect s. Thus, it ’s incredibly easy t o wrap a sm all
funct ion around a generic view t hat does addit ional work before — or aft er; see below — handing t hings off t o t he generic view.

N ot e
Not ice t hat in t he above exam ple we’ve passed t he current let t er being display in t he extra_context. This is usually
a good idea in wrappers of t his nat ure; it let s t he t em plat e know which let t er is current ly being browsed.

file:///D|/books/computer/programming/python/books/DJANGO BOOK/09.html (15 of 17)9/28/2007 2:27:38 PM


Chapter 9: Generic views

Also ( while we’re on t he t opic of t em plat es) not ice t hat we’ve passed in a cust om t em plat e nam e. Wit hout t hat , it
would t ry t o use t he sam e t em plat e as a “ vanilla” object_list, which could conflict wit h ot her generic views.

Performing extra work


The last com m on pat t ern we’ll look at involves doing som e ext ra work before or aft er calling t he generic view.

I m agine we had a last_accessed field on our Author obj ect t hat we were using t o keep t rack of t he last t im e a anybody looked
at t hat aut hor. The generic object_detail view, of course, wouldn’t know anyt hing about t his field, but once again we could
easily writ e a cust om view t o keep t hat field updat ed.

First , we’d need t o m odify t he aut hor det ail bit in t he URLconf t o point t o a cust om view:

from bookstore.views import author_detail

urlpatterns = patterns('',
#...
(r'^authors/(?P<author_id>d+)/$', author_detail),
)

Then we’d writ e our wrapper funct ion:

import datetime
from bookstore.models import Author
from django.views.generic import list_detail
from django.shortcuts import get_object_or_404
def author_detail(request, author_id):
# Look up the Author (and raise a 404 if she's not found)
author = get_object_or_404(Author, pk=author_id)

# Record the last accessed date


author.last_accessed = datetime.datetime.now()
author.save()

# Show the detail page


return list_detail.object_detail(
request,
queryset = Author.objects.all(),
object_id = author_id,
)

N ot e
This code won’t act ually work unless you add t he last_accessed field t o your Author m odel.

We can use a sim ilar idiom t o alt er t he response ret urned by t he generic view. I f we want ed t o provide a downloadable plain- t ext
version of t he list of aut hors, we could use a view like t his:

def author_list_plaintext(request):
response = list_detail.object_list(
queryset = Author.objects.all(),
mimetype = "text/plain",
template_name = "bookstore/author_list.txt"
)
response["Content-Disposition"] = "attachment; filename=authors.txt"
return response

This works because t he generic views ret urn sim ple HttpResponse obj ect s which can be t reat ed like dict ionaries t o set HTTP
headers. This Content-Disposition business, by t he way, inst ruct s t he browser t o download and save t he page inst ead of
displaying it in t he browser.

What ’ s next ?
Unt il now, we’ve t reat ed t he t em plat e engine as a m ost ly st at ic t ool you can use t o render your cont ent . I t ’s t rue t hat m ost of t he
t im e you’ll j ust t reat it in t hat way, but t he t em plat e engine is act ually quit e ext ensible.

I n t he next chapt er we’ll delve deep int o t he inner workings of Dj ango’s t em plat es, showing all t he cool ways it can be ext ended.

file:///D|/books/computer/programming/python/books/DJANGO BOOK/09.html (16 of 17)9/28/2007 2:27:38 PM


Chapter 9: Generic views

Onward, com rades!

« previous ◊ t able of cont ent s ◊ next »


Copyright 2006 Adrian Holovat y and Jacob Kaplan- Moss.
This work is licensed under t he GNU Free Docum ent License.

file:///D|/books/computer/programming/python/books/DJANGO BOOK/09.html (17 of 17)9/28/2007 2:27:38 PM


Chapter 10: Extending the template engine

The Dj ango Book


« previous ◊ t able of cont ent s ◊ next »

Chapt er 10: Inside t he t emplat e engine


Most of your int eract ion wit h Dj ango’s t em plat e language will probably be in t he role of a t em plat e aut hor. This chapt er delves
m uch deeper int o t he gut s of Dj ango’s t em plat e syst em ; read on if you need t o ext end t he t em plat e syst em , or if you’re j ust
curious about how it works int ernally.

I f you’re looking t o use t he Dj ango t em plat e syst em as part of anot her applicat ion — i.e., wit hout t he rest of t he fram ework —
m ake sure t o read t he configurat ion sect ion lat er in t his docum ent .

Basics
A t e m pla t e is a t ext docum ent , or a norm al Pyt hon st ring, t hat is m arked- up using t he Dj ango t em plat e language. A t em plat e
can cont ain block t a gs or va r ia ble s.

A block t a g is a sym bol wit hin a t em plat e t hat does som et hing.

This definit ion is deliberat ely vague. For exam ple, a block t ag can out put cont ent , serve as a cont rol st ruct ure ( an “ if” st at em ent
or “ for” loop) , grab cont ent from a dat abase or enable access t o ot her t em plat e t ags.

Block t ags are surrounded by {% and %}:

{% if is_logged_in %}
Thanks for logging in!
{% else %}
Please log in.
{% endif %}

A va r ia ble is a sym bol wit hin a t em plat e t hat out put s a value.

Variable t ags are surrounded by {{ and }}:

My first name is {{ first_name }}. My last name is {{ last_name }}.

A con t e x t is a “ nam e” - > “ value” m apping ( sim ilar t o a Pyt hon dict ionary) t hat is passed t o a t em plat e.

A t em plat e r e n de r s a cont ext by replacing t he variable “ holes” wit h values from t he cont ext and execut ing all block t ags.

Using t he t emplat e obj ect


At it s lowest level, using t he t em plat e syst em in Pyt hon is a t wo- st ep process:

● First , you com pile t he raw t em plat e code int o a Template obj ect .
● Then, you call t he render() m et hod of t he Template obj ect wit h a given cont ext .

Compiling a string
The easiest way t o creat e a Template obj ect is by inst ant iat ing it direct ly. The const ruct or t akes one argum ent — t he raw
t em plat e code:

>>> from django.template import Template


>>> t = Template("My name is {{ my_name }}.")
>>> print t
<django.template.Template object at 0x1150c70>

Be h in d t h e sce n e s
The syst em only parses your raw t em plat e code once — when you creat e t he Template obj ect . From t hen on, it ’s
st ored int ernally as a “ node” st ruct ure for perform ance.

Even t he parsing it self is quit e fast . Most of t he parsing happens via a single call t o a single, short , regular
expression.

file:///D|/books/computer/programming/python/books/DJANGO BOOK/10.html (1 of 15)9/28/2007 2:28:08 PM


Chapter 10: Extending the template engine

Rendering a context
Once you have a com piled Template obj ect , you can render a cont ext — or m ult iple cont ext s — wit h it . The Context const ruct or
t akes one ( opt ional) argum ent : a dict ionary m apping variable nam es t o variable values.

Call t he Template obj ect ’s render() m et hod wit h t he cont ext t o “ fill” t he t em plat e:

>>> from django.template import Context, Template


>>> t = Template("My name is {{ my_name }}.")

>>> c = Context({"my_name": "Adrian"})


>>> t.render(c)
"My name is Adrian."

>>> c = Context({"my_name": "Dolores"})


>>> t.render(c)
"My name is Dolores."

Variable nam es m ust consist of any let t er ( A- Z) , any digit ( 0- 9) , an underscore or a dot .

Dot s have a special m eaning in t em plat e rendering. A dot in a variable nam e signifies look u p. Specifically, when t he t em plat e
syst em encount ers a dot in a variable nam e, it t ries a num ber of possible opt ions. For exam ple, t he variable {{ foo.bar }} could
expand t o any of t he following:

● Dict ionary lookup: foo["bar"]


● At t ribut e lookup: foo.bar
● Met hod call: foo.bar()
● List - index lookup: foo[bar]

The t em plat e syst em uses t he first lookup t ype t hat works; it ’s short - circuit logic.

Here are a few exam ples:

>>> from django.template import Context, Template


>>> t = Template("My name is {{ person.first_name }}.")

>>> d = {"person": {"first_name": "Joe", "last_name": "Johnson"}}


>>> t.render(Context(d))
"My name is Joe."

>>> class Person:


... def __init__(self, first_name, last_name):
... self.first_name, self.last_name = first_name, last_name
...
>>> p = Person("Ron", "Nasty")
>>> t.render(Context({"person": p}))
"My name is Ron."

>>> class Person2:


... def first_name(self):
... return "Samantha"
...
>>> p = Person2()
>>> t.render(Context({"person": p}))
"My name is Samantha."

>>> t = Template("The first stooge in the list is {{ stooges.0 }}.")


>>> c = Context({"stooges": ["Larry", "Curly", "Moe"]})
>>> t.render(c)
"The first stooge in the list is Larry."

Met hod lookups are slight ly m ore com plex t han t he ot her lookup t ypes. Here are som e t hings t o keep in m ind:

● I f, during t he m et hod lookup, a m et hod raises an except ion, t he except ion will be propagat ed unless t he except ion has an
at t ribut e silent_variable_failure whose value is True.

I f t he except ion does have such an at t ribut e, t he variable will render as an em pt y st ring.

For exam ple:

>>> t = Template("My name is {{ person.first_name }}.")

file:///D|/books/computer/programming/python/books/DJANGO BOOK/10.html (2 of 15)9/28/2007 2:28:08 PM


Chapter 10: Extending the template engine

>>> class Person3:


... def first_name(self):
... raise AssertionError("foo")
...
>>> p = Person3()
>>> t.render(Context({"person": p}))
Traceback (most recent call last):
...
AssertionError: foo

>>> class SilentAssertionError(AssertionError):


... silent_variable_failure = True
...
>>> class Person4:
... def first_name(self):
... raise SilentAssertionError("foo")
...
>>> p = PersonClass4()
>>> t.render(Context({"person": p}))
"My name is ."

Not e t hat django.core.exceptions.ObjectDoesNotExist, which is t he base class for all Dj ango dat abase API DoesNotExist
except ions, has silent_variable_failure = True. So if you’re using Dj ango t em plat es wit h Dj ango m odel obj ect s,
any DoesNotExist except ion will fail silent ly.

● A m et hod call will only work if t he m et hod has no required argum ent s. Ot herwise, t he syst em will m ove t o t he next lookup
t ype ( list - index lookup) .

● Obviously, som e m et hods have side effect s, and it ’d be eit her foolish or a securit y hole t o allow t he t em plat e syst em t o
access t hem .

A good exam ple is t he delete() m et hod on each Dj ango m odel obj ect . The t em plat e syst em shouldn’t be allowed t o do
som et hing like t his:

I will now delete this valuable data. {{ data.delete }}

To prevent t his, set a funct ion at t ribut e alters_data on t he m et hod. The t em plat e syst em won’t execut e a m et hod if t he
m et hod has alters_data=True set :

def sensitive_function(self):
self.database_record.delete()
sensitive_function.alters_data = True

The dynam ically- generat ed delete() and save() m et hods on Dj ango m odel obj ect s get alters_data=True aut om at ically,
for exam ple.

How invalid variables are handled


Generally, if a variable doesn’t exist , t he t em plat e syst em insert s t he value of t he TEMPLATE_STRING_IF_INVALID set t ing, which is
set t o t he em pt y st ring by default .

Filt ers t hat are applied t o an invalid variable will only be applied if TEMPLATE_STRING_IF_INVALID is set t o it s default value.
I f TEMPLATE_STRING_IF_INVALID is set t o any ot her value, variable filt ers will be ignored.

This behavior is slight ly different for t he if, for and regroup t em plat e t ags. I f an invalid variable is provided t o one of t hese
t em plat e t ags, t he variable will be int erpret ed as None. Filt ers are always applied t o invalid variables wit hin t hese t em plat e t ags.

Playing with Context obj ects


Most of t he t im e, you’ll inst ant iat e Context obj ect s by passing in a fully- populat ed dict ionary t o Context(). But you can add and
delet e it em s from a Context obj ect once it ’s been inst ant iat ed, t oo, using st andard dict ionary synt ax:

>>> c = Context({"foo": "bar"})


>>> c['foo']
'bar'
>>> del c['foo']
>>> c['foo']
''
>>> c['newvariable'] = 'hello'
>>> c['newvariable']
'hello'

file:///D|/books/computer/programming/python/books/DJANGO BOOK/10.html (3 of 15)9/28/2007 2:28:08 PM


Chapter 10: Extending the template engine

Furt herm ore, a Context obj ect act s like a st ack. That is, you can push() and pop() addit ional cont ext s ont o t he st ack. All set t ing
operat ions happen t o t he t op- m ost cont ext on t he st ack, but get operat ions search t he st ack ( t op- down) unt il a value is found.

I f you pop() t oo m uch, you’ll get a django.template.ContextPopException.

Here’s an exam ple of how t hese m ult iple levels m ight work:

# Create a new blank context and set a simple value:


>>> c = Context()
>>> c['foo'] = 'first level'
# Push a new context onto the stack:
>>> c.push()
>>> c['foo'] = 'second level'

# The value of "foo" is now what we set at the second level:


>>> c['foo']
'second level'
# After popping a layer off, the old value is still there:
>>> c.pop()
>>> c['foo']
'first level'

# If we don't push() again, we'll overwrite existing values:


>>> c['foo'] = 'overwritten'
>>> c['foo']
'overwritten'

# There's only one context on the stack, so pop()ing will fail:


>>> c.pop()
Traceback (most recent call last):
...
django.template.ContextPopException

Using a Context as a st ack com es in handy in som e cust om t em plat e t ags, as you’ll see below.

RequestContext and context processors


Dj ango com es wit h a special Context class, django.template.RequestContext, t hat act s slight ly different ly t han t he
norm al django.template.Context. The first difference is t hat t akes an HttpRequest obj ect ( see Chapt er XXX) as it s first
argum ent :

c = RequestContext(request, {
'foo': 'bar',
}

The second difference is t hat it aut om at ically populat es t he cont ext wit h a few variables, according t o
your TEMPLATE_CONTEXT_PROCESSORS set t ing.

The TEMPLATE_CONTEXT_PROCESSORS set t ing is a t uple of callables called con t e x t pr oce ssor s t hat t ake a request obj ect as t heir
argum ent and ret urn a dict ionary of it em s t o be m erged int o t he cont ext . By default , TEMPLATE_CONTEXT_PROCESSORS is set t o:

("django.core.context_processors.auth",
"django.core.context_processors.debug",
"django.core.context_processors.i18n")

Each processor is applied in order. That is, if one processor adds a variable t o t he cont ext and a second processor adds a variable
wit h t he sam e nam e, t he second will override t he first . The default processors are explained below.

Also, you can give RequestContext a list of addit ional processors, using t he opt ional, t hird argum ent , processors. I n t his
exam ple, t he RequestContext inst ance get s a ip_address variable:

def ip_address_processor(request):
return {'ip_address': request.META['REMOTE_ADDR']}
def some_view(request):
# ...
return RequestContext(request, {

file:///D|/books/computer/programming/python/books/DJANGO BOOK/10.html (4 of 15)9/28/2007 2:28:08 PM


Chapter 10: Extending the template engine

'foo': 'bar',
}, processors=[ip_address_processor])

Here’s what each of t he default processors does:

django.core.context_processors.auth
I f TEMPLATE_CONTEXT_PROCESSORS cont ains t his processor, every RequestContext will cont ain t hese t hree variables:

user
A djangol.contrib.auth.models.User inst ance represent ing t he current ly logged- in user ( or an AnonymousUser inst ance, if
t he client isn’t logged in) .

messages
A list of m essages ( as st rings) for t he current ly logged- in user. Behind t he scenes, t his
calls request.user.get_and_delete_messages() for every request . That m et hod collect s t he user’s m essages and delet es
t hem from t he dat abase.

Not e t hat m essages are set wit h user.add_message().

perms
An inst ance of django.core.context_processors.PermWrapper, represent ing t he perm issions t hat t he current ly logged- in
user has.

See Chapt er XXX for m ore on users, perm issions, and m essages.

django.core.context_processors.debug
This processor pushed debugging inform at ion down t o t he t em plat e layer. I f it is enabled, it will only act ually operat e if:

● t he DEBUG set t ing is True, and


● t he request cam e from an I P address in t he INTERNAL_IPS set t ing.

I f t hose condit ions are m et , t he following variables will be set :

debug
Set t o True; you can use t his in t em plat es t o t est whet her you’re in DEBUG m ode.

sql_queries
A list of {'sql': ..., 'time': ...} dict ionaries, represent ing every SQL query t hat has happened so far during t he request
and how long it t ook. The list is in order by query.

django.core.context_processors.i18n
I f t his processor is enabled t his processor, every RequestContext will cont ain t hese t wo variables:

LANGUAGES
The value of t he LANGUAGES set t ing.

LANGUAGE_CODE
request.LANGUAGE_CODE, if it exist s. Ot herwise, t he value of t he LANGUAGE_CODE set t ing

Appendix XXX has m ore inform at ion about t hese t wo set t ings.

django.core.context_processors.request
I f enabled, every RequestContext will cont ain a variable request, which is t he current HttpRequest obj ect . Not e t hat t his
processor is not enabled by default ; you’ll have t o act ivat e it .

Loading templates
Generally, you’ll st ore t em plat es in files on your filesyst em ( or in ot her places if you’ve writ t en cust om t em plat e loaders) rat her
t han using t he low- level Template API yourself.

Dj ango searches for t em plat e direct ories in a num ber of places, depending on your t em plat e- loader set t ings ( see “ Loader t ypes”
below) , but t he m ost basic way of specifying t em plat e direct ories is by using t he TEMPLATE_DIRS set t ing.

This should be set t o a list or t uple of st rings t hat cont ain full pat hs t o your t em plat e direct ory( ies) :

TEMPLATE_DIRS = (
"/home/html/templates/lawrence.com",
"/home/html/templates/default",
)

file:///D|/books/computer/programming/python/books/DJANGO BOOK/10.html (5 of 15)9/28/2007 2:28:08 PM


Chapter 10: Extending the template engine

Your t em plat es can go anywhere you want , as long as t he direct ories and t em plat es are readable by t he Web server. They can
have any ext ension you want , such as .html or .txt, or t hey can have no ext ension at all.

Not e t hat t hese pat hs should use Unix- st yle forward slashes, even on Windows.

The Python API


Dj ango has t wo ways t o load t em plat es from files:

django.template.loader.get_template(template_name)
get_template ret urns t he com piled t em plat e ( a Template obj ect ) for t he t em plat e wit h t he given nam e. I f t he t em plat e
doesn’t exist , it raises django.template.TemplateDoesNotExist.

django.template.loader.select_template(template_name_list)
select_template is j ust like get_template, except it t akes a list of t em plat e nam es. Of t he list , it ret urns t he first t em plat e
t hat exist s.

For exam ple, if you call get_template('story_detail.html') and have t he above TEMPLATE_DIRS set t ing, here are t he files
Dj ango will look for, in order:

● /home/html/templates/lawrence.com/story_detail.html
● /home/html/templates/default/story_detail.html

I f you call select_template(['story_253_detail.html', 'story_detail.html']), here’s what Dj ango will look for:

● /home/html/templates/lawrence.com/story_253_detail.html
● /home/html/templates/default/story_253_detail.html
● /home/html/templates/lawrence.com/story_detail.html
● /home/html/templates/default/story_detail.html

When Dj ango finds a t em plat e t hat exist s, it st ops looking.

Tip
You can use select_template() for super- flexible “ t em plat abilit y.” For exam ple, if you’ve writ t en a news st ory and
want som e st ories t o have cust om t em plat es, use som et hing
like select_template(['story_%s_detail.html' % story.id, 'story_detail.html']). That ’ll allow you t o use
a cust om t em plat e for an individual st ory, wit h a fallback t em plat e for st ories t hat don’t have cust om t em plat es.

Using subdirectories
I t ’s possible — and preferable — t o organize t em plat es in subdirect ories of t he t em plat e direct ory. The convent ion is t o m ake a
subdirect ory for each Dj ango app, wit h subdirect ories wit hin t hose subdirect ories as needed.

Do t his for your own sanit y. St oring all t em plat es in t he root level of a single direct ory get s m essy.

To load a t em plat e t hat ’s wit hin a subdirect ory, j ust use a slash, like so:

get_template('news/story_detail.html')

Using t he sam e TEMPLATE_DIRS set t ing from above, t his exam ple get_template() call will at t em pt t o load t he following
t em plat es:

● /home/html/templates/lawrence.com/news/story_detail.html
● /home/html/templates/default/news/story_detail.html

Again, use UNI X- st yle forward slashes, even on Windows.

Template loaders
By default , Dj ango loads t em plat es from t he filesyst em , but Dj ango com es wit h a few ot her t e m pla t e loa de r s which know how
t o load t em plat es from ot her sources.

Som e of t hese ot her loaders are disabled by default , but you can act ivat e t hem by edit ing your TEMPLATE_LOADERS
set t ing. TEMPLATE_LOADERS should be a t uple of st rings, where each st ring represent s a t em plat e loader. These t em plat e loaders
ship wit h Dj ango:

django.template.loaders.filesystem.load_template_source

file:///D|/books/computer/programming/python/books/DJANGO BOOK/10.html (6 of 15)9/28/2007 2:28:08 PM


Chapter 10: Extending the template engine

Loads t em plat es from t he filesyst em , according t o TEMPLATE_DIRS.

This loader is enabled by default .

django.template.loaders.app_directories.load_template_source
Loads t em plat es from Dj ango apps on t he filesyst em . For each app in INSTALLED_APPS, t he loader looks for a templates
subdirect ory. I f t he direct ory exist s, Dj ango looks for t em plat es in t here.

This m eans you can st ore t em plat es wit h your individual apps. This also m akes it easy t o dist ribut e Dj ango apps wit h default
t em plat es.

For exam ple, if INSTALLED_APPS cont ains ('myproject.polls', 'myproject.music'), t hen get_template('foo.html') will
look for t em plat es in in t his order:

❍ /path/to/myproject/polls/templates/foo.html
❍ /path/to/myproject/music/templates/foo.html

Not e t hat t he loader perform s an opt im izat ion when it is first im port ed: I t caches a list of which INSTALLED_APPS packages
have a templates subdirect ory.

This loader is enabled by default .

django.template.loaders.eggs.load_template_source
Just like app_directories above, but it loads t em plat es from Pyt hon eggs rat her t han from t he filesyst em .

This loader is disabled by default ; you’ll need t o enable it if you’re using eggs t o dist ribut e your app.

Dj ango uses t he t em plat e loaders in order according t o t he TEMPLATE_LOADERS set t ing. I t uses each loader unt il a loader finds a
m at ch.

Ext ending t he t emplat e syst em


Alt hough t he Dj ango t em plat e language com es wit h several default t ags and filt ers, you m ight want t o writ e your own, and it ’s
easy t o do.

First , creat e a templatetags package in t he appropriat e Dj ango app’s package. I t should be on t he sam e level as models.py
, views.py, et c. For exam ple:

polls/
models.py
templatetags/
views.py

Add t wo files t o t he templatetags package: an __init__.py file ( t o indicat e t o Pyt hon t hat t his is a m odule cont aining Pyt hon
code) and a file t hat will cont ain your cust om t ag/ filt er definit ions.

The nam e of t he lat t er file is t he nam e you’ll use t o load t he t ags lat er. For exam ple, if your cust om t ags/ filt ers are in a file
called poll_extras.py, you’d do t he following in a t em plat e:

{% load poll_extras %}

The {% load %} t ag looks at your INSTALLED_APPS set t ing and only allows t he loading of t em plat e libraries wit hin inst alled
Dj ango apps. This is a securit y feat ure: I t allows you t o host Pyt hon code for m any t em plat e libraries on a single com put er
wit hout enabling access t o all of t hem for every Dj ango inst allat ion.

I f you writ e a t em plat e library t hat isn’t t ied t o any part icular m odels/ views, it ’s perfect ly OK t o have a Dj ango app package t hat
only cont ains a templatetags package.

There’s no lim it on how m any m odules you put in t he templatetags package. Just keep in m ind t hat a {% load %} st at em ent will
load t ags/ filt ers for t he given Pyt hon m odule nam e, not t he nam e of t he app.

Once you’ve creat ed t hat Pyt hon m odule, you’ll j ust have t o writ e a bit of Pyt hon code, depending on whet her you’re writ ing
filt ers or t ags.

To be a valid t ag library, t he m odule cont ain a m odule- level variable nam ed register t hat is a template.Library inst ance, in
which all t he t ags and filt ers are regist ered. So, near t he t op of your m odule, put t he following:

from django import template

register = template.Library()

file:///D|/books/computer/programming/python/books/DJANGO BOOK/10.html (7 of 15)9/28/2007 2:28:08 PM


Chapter 10: Extending the template engine

Be h in d t h e sce n e s
For a t on of exam ples, read t he source code for Dj ango’s default filt ers and t ags. They’re
in django/template/defaultfilters.py and django/template/defaulttags.py, respect ively.

The apps in django.contrib also cont ain num erous exam ples.

Writing custom template filters


Cust om filt ers are j ust Pyt hon funct ions t hat t ake one or t wo argum ent s:

● The value of t he variable ( input ) .


● The value of t he argum ent , which t his can have a default value, or be left out alt oget her.

For exam ple, in t he filt er {{ var|foo:"bar" }}, t he filt er foo would be passed t he variable var and t he argum ent "bar".

Filt er funct ions should always ret urn som et hing. They shouldn’t raise except ions and should fail silent ly. I f t here’s an error, t hey
should ret urn eit her t he original input or an em pt y st ring — whichever m akes m ore sense.

Here’s an exam ple filt er definit ion:

def cut(value, arg):


"Removes all values of arg from the given string"
return value.replace(arg, '')

And here’s an exam ple of how t hat filt er would be used:

{{ somevariable|cut:"0" }}

Most filt ers don’t t ake argum ent s. I n t his case, j ust leave t he argum ent out of your funct ion:

def lower(value): # Only one argument.


"Converts a string into all lowercase"
return value.lower()

When you’ve writ t en your filt er definit ion, you need t o regist er it wit h your Library inst ance, t o m ake it available t o Dj ango’s
t em plat e language:

register.filter('cut', cut)
register.filter('lower', lower)

The Library.filter() m et hod t akes t wo argum ent s:

1. The nam e of t he filt er ( a st ring) .


2. The com pilat ion funct ion ( a Pyt hon funct ion, not t he nam e of t he funct ion) .
I f you’re using Pyt hon 2.4 or above, you can use register.filter() as a decorat or inst ead:

@register.filter(name='cut')
def cut(value, arg):
return value.replace(arg, '')

@register.filter
def lower(value):
return value.lower()

I f you leave off t he name argum ent , as in t he second exam ple above, Dj ango will use t he funct ion’s nam e as t he filt er nam e.

Writing custom template tags


Tags are m ore com plex t han filt ers, because t ags can do nearly anyt hing.

A quick overview
Above, t his chapt er describes how t he t em plat e syst em works in a t wo- st ep process: com piling and rendering. To define a cust om
t em plat e t ag, you need t o t ell Dj ango how t o m anage bot h st eps when it get s t o your t ag.

file:///D|/books/computer/programming/python/books/DJANGO BOOK/10.html (8 of 15)9/28/2007 2:28:08 PM


Chapter 10: Extending the template engine

When Dj ango com piles a t em plat e, it split s t he raw t em plat e t ext int o ‘’nodes’‘. Each node is an inst ance
of django.template.Node and has a render() m et hod. Thus, a com piled t em plat e is sim ply a list of Node obj ect s.

When you call render() on a com piled t em plat e, t he t em plat e calls render() on each Node in it s node list , wit h t he given
cont ext . The result s are all concat enat ed t oget her t o form t he out put of t he t em plat e.

Thus, t o define a cust om t em plat e t ag, you specify how t he raw t em plat e t ag is convert ed int o a Node ( t he com pilat ion funct ion) ,
and what t he node’s render() m et hod does.

Writing the compilation function


For each t em plat e t ag t he t em plat e parser encount ers, it calls a Pyt hon funct ion wit h t he t ag cont ent s and t he parser obj ect it self.
This funct ion is responsible for ret urning a Node inst ance based on t he cont ent s of t he t ag.

For exam ple, let ’s writ e a t em plat e t ag, {% current_time %}, t hat displays t he current dat e/ t im e, form at t ed according t o a
param et er given in t he t ag, in strftime synt ax ( see ht t p: / / www.pyt hon.org/ doc/ current / lib/ m odule- t im e.ht m l# l2h- 1941) . I t ’s a
good idea t o decide t he t ag synt ax before anyt hing else. I n our case, let ’s say t he t ag should be used like t his:

<p>The time is {% current_time "%Y-%m-%d %I:%M %p" %}.</p>

N ot e
Yes, t his t em plat e t ag is redundant ; Dj ango’s default {% now %} t ag does t he sam e t ask wit h sim pler synt ax. This
one’s j ust for an exam ple.

The parser for t his funct ion should grab t he param et er and creat e a Node obj ect :

from django import template

def do_current_time(parser, token):


try:
# split_contents() knows not to split quoted strings.
tag_name, format_string = token.split_contents()
except ValueError:
raise template.TemplateSyntaxError("%r tag requires a single argument" % token.
contents[0])
return CurrentTimeNode(format_string[1:-1])

There’s act ually a lot going here:

● parser is t he t em plat e parser obj ect . We don’t need it in t his exam ple.
● token.contents is a st ring of t he raw cont ent s of t he t ag. I n our exam ple, it ’s 'current_time "%Y-%m-%d %I:%M %p"'.
● The token.split_contents() m et hod separat es t he argum ent s on spaces while keeping quot ed st rings t oget her. The m ore
st raight forward token.contents.split() wouldn’t be as robust , as it would naively split on all spaces, including t hose
wit hin quot ed st rings. I t ’s a good idea t o always use token.split_contents().
● This funct ion is responsible for raising django.template.TemplateSyntaxError, wit h helpful m essages, for any synt ax
error.
● Don’t hard- code t he t ag’s nam e in your error m essages, because t hat couples t he t ag’s nam e t o your
funct ion. token.contents.split()[0] will ‘’always’’ be t he nam e of your t ag — even when t he t ag has no argum ent s.
● The funct ion ret urns a CurrentTimeNode ( which we’ll creat e below) cont aining everyt hing t he node needs t o know about t his
t ag. I n t his case, it j ust passes t he argum ent — "%Y-%m-%d %I:%M %p". The leading and t railing quot es from t he t em plat e
t ag are rem oved wit h format_string[1:-1].
● Tem plat e t ag com pilat ion funct ions m u st ret urn a Node subclass; any ot her ret urn value is an error.
● The parsing is very low- level. We’ve experim ent ed wit h writ ing sm all fram eworks on t op of t his parsing syst em ( using
t echniques such as EBNF gram m ars) but t hose experim ent s m ade t he t em plat e engine t oo slow. Low level is fast .

Writing the template node


The second st ep in writ ing cust om t ags is t o define a Node subclass t hat has a render() m et hod. Cont inuing t he above exam ple,
we need t o define CurrentTimeNode:

import datetime
class CurrentTimeNode(template.Node):
def __init__(self, format_string):
self.format_string = format_string

def render(self, context):

file:///D|/books/computer/programming/python/books/DJANGO BOOK/10.html (9 of 15)9/28/2007 2:28:08 PM


Chapter 10: Extending the template engine

return datetime.datetime.now().strftime(self.format_string)

These t wo funct ions ( __init__ and render) m ap direct ly t o t he t wo st eps in t em plat e processing ( com pilat ion and rendering) .
Thus, t he init ializat ion funct ion only needs t o st ore t he form at st ring for lat er use, and t he render() funct ion does t he real work.

Like t em plat e filt ers, t hese rendering funct ions should fail silent ly inst ead of raising errors. The only t im e t hat t em plat e t ags are
allowed t o raise errors is at com pilat ion t im e.

Registering the tag


Finally, you need t o regist er t he t ag wit h your m odule’s Library inst ance, as explained in “ Writ ing cust om t em plat e filt ers” above:

register.tag('current_time', do_current_time)

The tag() m et hod t akes t wo argum ent s:

1. The nam e of t he t em plat e t ag ( st ring) . I f t his is left out , t he nam e of t he com pilat ion funct ion will be used.
2. The com pilat ion funct ion.
As wit h filt er regist rat ion, it is also possible t o use t his as a decorat or in Pyt hon 2.4 and above:

@register.tag(name="current_time")
def do_current_time(parser, token):
# ...

@register.tag
def shout(parser, token):
# ...

I f you leave off t he name argum ent , as in t he second exam ple above, Dj ango will use t he funct ion’s nam e as t he t ag nam e.

Setting a variable in the context


The above exam ple sim ply out put a value. Oft en it ’s useful t o set t em plat e variables inst ead of out put t ing values. That way,
t em plat e aut hors can sim ply use t he values t hat your t em plat e t ags creat e.

To set a variable in t he cont ext , j ust use dict ionary assignm ent on t he cont ext obj ect in t he render() m et hod. Here’s an updat ed
version of CurrentTimeNode t hat set s a t em plat e variable current_time inst ead of out put t ing it :

class CurrentTimeNode2(template.Node):

def __init__(self, format_string):


self.format_string = format_string

def render(self, context):


context['current_time'] = datetime.datetime.now().strftime(self.format_string)
return ''

Not e t hat render() ret urns t he em pt y st ring; render() should always ret urn st ring out put , so if all t he t em plat e t ag does is set a
variable, render() should ret urn an em pt y st ring.

Here’s how you’d use t his new version of t he t ag:

{% current_time "%Y-%M-%d %I:%M %p" %}


<p>The time is {{ current_time }}.</p>

But , t here’s a problem wit h CurrentTimeNode2: t he variable nam e current_time is hard- coded. This m eans you’ll need t o m ake
sure your t em plat e doesn’t use {{ current_time }} anywhere else, because t he {% current_time %} will blindly overwrit e t hat
variable’s value.

A cleaner solut ion is t o m ake t he t em plat e t ag specify t he nam e of t he out put variable, like so:

{% get_current_time "%Y-%M-%d %I:%M %p" as my_current_time %}


<p>The current time is {{ my_current_time }}.</p>

To do t hat , you’ll need t o refact or bot h t he com pilat ion funct ion and t he Node class, like so:

import re

file:///D|/books/computer/programming/python/books/DJANGO BOOK/10.html (10 of 15)9/28/2007 2:28:08 PM


Chapter 10: Extending the template engine

class CurrentTimeNode3(template.Node):

def __init__(self, format_string, var_name):


self.format_string = format_string
self.var_name = var_name

def render(self, context):


context[self.var_name] = datetime.datetime.now().strftime(self.format_string)
return ''

def do_current_time(parser, token):


# This version uses a regular expression to parse tag contents.
try:
# Splitting by None == splitting by spaces.
tag_name, arg = token.contents.split(None, 1)
except ValueError:
raise template.TemplateSyntaxError("%r tag requires arguments" % token.contents[0])

m = re.search(r'(.*?) as (\w+)', arg)


if m:
format_string, var_name = m.groups()
else:
raise template.TemplateSyntaxError("%r tag had invalid arguments" % tag_name)

if not (format_string[0] == format_string[-1] and format_string[0] in ('"', "'")):


raise template.TemplateSyntaxError("%r tag's argument should be in quotes" % tag_name)

return CurrentTimeNode3(format_string[1:-1], var_name)

Now, do_current_time() grabs t he form at st ring and t he variable nam e, passing bot h t o CurrentTimeNode3.

Parsing until another block tag


Tem plat e t ags can work as blocks cont aining ot her t ags. For exam ple, t he st andard {% comment %} t ag hides everyt hing
unt il {% endcomment %}.

To creat e a t em plat e t ag like t his, use parser.parse() in your com pilat ion funct ion.

Here’s how t he st andard {% comment %} t ag is im plem ent ed:

def do_comment(parser, token):


nodelist = parser.parse(('endcomment',))
parser.delete_first_token()
return CommentNode()

class CommentNode(template.Node):
def render(self, context):
return ''

parser.parse() t akes a t uple of nam es of block t ags t o parse unt il. I t ret urns an inst ance of django.template.NodeList, which
is a list of all Node obj ect s t hat t he parser encount ered before it encount ered any of t he t ags nam ed in t he t uple.

So in t he above exam ple, nodelist is a list of all nodes bet ween t he {% comment %} and {% endcomment %}, not
count ing {% comment %} and {% endcomment %} t hem selves.

Aft er parser.parse() is called, t he parser hasn’t yet “ consum ed” t he {% endcomment %} t ag, so t he code needs t o explicit ly
call parser.delete_first_token() t o prevent t hat t ag from being processed t wice.

Then, CommentNode.render() sim ply ret urns an em pt y st ring. Anyt hing bet ween {% comment %} and {% endcomment %} is
ignored.

Parsing until another block tag and saving contents


I n t he previous exam ple, do_comment() discarded everyt hing bet ween {% comment %} and {% endcomment %}. I nst ead of doing
t hat , it ’s possible t o do som et hing wit h t he code bet ween block t ags.

For exam ple, here’s a cust om t em plat e t ag, {% upper %}, t hat capit alizes everyt hing bet ween it self and {% endupper %}:

{% upper %}
This will appear in uppercase, {{ your_name }}.
{% endupper %}

file:///D|/books/computer/programming/python/books/DJANGO BOOK/10.html (11 of 15)9/28/2007 2:28:08 PM


Chapter 10: Extending the template engine

As in t he previous exam ple, we’ll use parser.parse(). This t im e, we pass t he result ing nodelist t o t he Node:

@register.tag
def do_upper(parser, token):
nodelist = parser.parse(('endupper',))
parser.delete_first_token()
return UpperNode(nodelist)

class UpperNode(template.Node):

def __init__(self, nodelist):


self.nodelist = nodelist

def render(self, context):


output = self.nodelist.render(context)
return output.upper()

The only new concept here is t he self.nodelist.render(context) in UpperNode.render().

For m ore exam ples of com plex rendering, see t he source code for {% if %}, {% for %}, {% ifequal %} and {% ifchanged %}.
They live in django/template/defaulttags.py.

Shortcut for simple tags


Many t em plat e t ags t ake a single argum ent — a st ring or a t em plat e variable reference — and ret urn a st ring aft er doing som e
processing based solely on t he input argum ent and som e ext ernal inform at ion. For exam ple, t he current_time t ag we wrot e
above is of t his variet y: we give it a form at st ring, it ret urns t he t im e as a st ring.

To ease t he creat ion of t he t ypes of t ags, Dj ango provides a helper funct ion, simple_tag. This funct ion, which is a m et hod
of django.template.Library, t akes a funct ion t hat accept s one argum ent , wraps it in a render funct ion and t he ot her necessary
bit s m ent ioned above and regist ers it wit h t he t em plat e syst em .

Our earlier current_time funct ion could t hus be writ t en like t his:

def current_time(format_string):
return datetime.datetime.now().strftime(format_string)
register.simple_tag(current_time)

I n Pyt hon 2.4, t he decorat or synt ax also works:

@register.simple_tag
def current_time(token):
...

A couple of t hings t o not ice about t he simple_tag helper funct ion:

● Only t he ( single) argum ent is passed int o our funct ion.


● Checking for t he required num ber of argum ent s has already been done by t he t im e our funct ion is called, so we don’t need
t o do t hat .
● The quot es around t he argum ent ( if any) have already been st ripped away, so we j ust receive a plain st ring.

Inclusion tags
Anot her com m on t ype of t em plat e t ag is t he t ype t hat displays som e dat a by rendering anot her t em plat e.

For exam ple, Dj ango’s adm in int erface uses cust om t em plat e t ags t o display t he but t ons along t he bot t om of t he “ add/ change”
form pages. Those but t ons always look t he sam e, but t he link t arget s change depending on t he obj ect being edit ed. They’re a
perfect case for using a sm all t em plat e t hat is filled wit h det ails from t he current obj ect .

These sort s of t ags are called in clu sion t a gs.

Writ ing inclusion t ags is probably best dem onst rat ed by exam ple. Let ’s writ e a t ag t hat out put s a list of choices for a sim ple
m ult iple- choice Poll obj ect . We’ll use t he t ag like t his:

{% show_results poll %}

…and t he out put will be som et hing like t his:

<ul>

file:///D|/books/computer/programming/python/books/DJANGO BOOK/10.html (12 of 15)9/28/2007 2:28:08 PM


Chapter 10: Extending the template engine

<li>First choice</li>
<li>Second choice</li>
<li>Third choice</li>
</ul>

First , we define t he funct ion t hat t akes t he argum ent and produces a dict ionary of dat a for t he result . Not ice t hat we only need t o
ret urn a dict ionary, not anyt hing m ore com plex. This will be used as t he cont ext for t he t em plat e fragm ent :

def show_results(poll):
choices = poll.choice_set.all()
return {'choices': choices}

Next , we creat e t he t em plat e used t o render t he t ag’s out put . Following our exam ple, t he t em plat e is very sim ple:

<ul>
{% for choice in choices %}
<li> {{ choice }} </li>
{% endfor %}
</ul>

Finally, we creat e and regist er t he inclusion t ag by calling t he inclusion_tag() m et hod on a Library obj ect .

Following our exam ple, if t he above t em plat e is in a file called polls/result_snippet.html, we’d regist er t he t ag like t his:

register.inclusion_tag('polls/result_snippet.html')(show_results)

As always, Pyt hon 2.4 decorat or synt ax works as well, so we could have inst ead writ t en:

@register.inclusion_tag('results.html')
def show_results(poll):
...

Som et im es, your inclusion t ags need access t o t he cont ext in t he parent t em plat e.

To solve t his, Dj ango provides a takes_context opt ion for inclusion t ags. I f you specify takes_context in creat ing a t em plat e
t ag, t he t ag will have no required argum ent s, and t he underlying Pyt hon funct ion will have one argum ent — t he t em plat e cont ext
as of when t he t ag was called.

For exam ple, say you’re writ ing an inclusion t ag t hat will always be used in a cont ext t hat cont ains home_link and home_title
variables t hat point back t o t he m ain page. Here’s what t he Pyt hon funct ion would look like:

@register.inclusion_tag('link.html', takes_context=True)
def jump_link(context):
return {
'link': context['home_link'],
'title': context['home_title'],
}

N ot e
The first param et er t o t he funct ion m ust be called context.

The t em plat e link.html m ight cont ain:

Jump directly to <a href="{{ link }}">{{ title }}</a>.

Then, any t im e you want t o use t hat cust om t ag, load it s library and call it wit hout any argum ent s, like so:

{% jump_link %}

Not e t hat when you’re using takes_context=True, t here’s no need t o pass argum ent s t o t he t em plat e t ag. I t aut om at ically get s
access t o t he cont ext .

Writing custom template loaders


Dj ango’s built - in t em plat e loaders will usually cover all your t em plat e- loading needs, but it ’s pret t y easy t o writ e your own if you

file:///D|/books/computer/programming/python/books/DJANGO BOOK/10.html (13 of 15)9/28/2007 2:28:08 PM


Chapter 10: Extending the template engine

need special loading logic.

A t em plat e loader — t hat is, each ent ry in t he TEMPLATE_LOADERS set t ings — is expect ed t o be a callable wit h t his int erface:

load_template_source(template_name, template_dirs=None)

The template_name argum ent is t he nam e of t he t em plat e t o load ( as passed t o loader.get_template()


or loader.select_template()) , and template_dirs is an opt ional list of direct ories t o search inst ead of TEMPLATE_DIRS.

I f a loader is able t o successfully load a t em plat e, it should ret urn a t uple: (template_source, template_path).
Here, template_source is t he t em plat e st ring which will be com piled by t he t em plat e engine, and template_path is t he pat h t he
t em plat e was loaded from . That pat h m ight be shown t o t he user for debugging purposes, so it should quickly ident ify where t he
t em plat e was loaded from .

I f t he loader is unable t o load a t em plat e, it should raise django.template.TemplateDoesNotExist.

Each loader funct ion should also have an is_usable funct ion at t ribut e. This is a boolean t hat inform s t he t em plat e engine
whet her or not t his loader is available in t he current Pyt hon inst allat ion.

For exam ple, t he eggs loader ( which is capable of loading t em plat es from Pyt hon eggs) set s is_usable t o False if
t he pkg_resources m odule isn’t inst alled, because pkg_resources is necessary t o read dat a from eggs.

An exam ple should help clarify all of t his. Here’s a t em plat e loader funct ion t hat can load t em plat es from a ZI P file. I t uses a
cust om set t ing, TEMPLATE_ZIP_FILES as a search pat h inst ead of TEMPLATE_DIRS, and expect s each it em on t hat pat h t o be a ZI P
file cont aining t em plat es:

import zipfile
from django.conf import settings
from django.template import TemplateDoesNotExist
def load_template_source(template_name, template_dirs=None):
"""Template loader that loads templates from a ZIP file."""

# Lookup ZIP file list from settings if it's not already given.
if template_zipfiles is None:
template_zipfiles = getattr(settings, "TEMPLATE_ZIP_FILES", [])

# Try each ZIP file in TEMPLATE_ZIP_FILES.


for fname in template_zipfiles:
try:
z = zipfile.ZipFile(fname)
source = z.read(template_name)
except (IOError, KeyError):
continue

# We found a template, so return the source.


template_path = "%s:%s" % (fname, template_name)
return (source, template_path)
# If we reach here, the template couldn't be loaded
raise TemplateDoesNotExist(template_name)

# This loader is always usable (since zipfile is a Python standard library function)
load_template_source.is_usable = True

The only st ep left if we want ed t o use t his loader is t o add it t o t he TEMPLATE_LOADERS set t ing. I f we put t his code in a m odule
called myproject.zip_loader, t hen we’d add myproject.zip_loader.load_template_source t o TEMPLATE_LOADERS.

Using the built-in template reference


Dj ango’s adm in int erface includes a com plet e reference of all t em plat e t ags and filt ers available for a given sit e. I t ’s designed t o
be a t ool t hat Dj ango program m ers give t o t em plat e developers. To see it , go t o your adm in int erface and click t he
“ Docum ent at ion” link in t he upper right of t he page.

The reference is divided int o 4 sect ions: t ags, filt ers, m odels, and views.

The t a gs and filt e r s sect ions describe all t he built - in t ags ( in fact , t he t ag and filt er references below com e direct ly from t hose
pages) as well as any cust om t ag or filt er libraries available.

The vie w s page is t he m ost valuable. Each URL in your sit e has a separat e ent ry here, and clicking on a URL will show you:

● The nam e of t he view funct ion t hat generat es t hat view.


● A short descript ion of what t he view does.

file:///D|/books/computer/programming/python/books/DJANGO BOOK/10.html (14 of 15)9/28/2007 2:28:08 PM


Chapter 10: Extending the template engine

● The con t e x t , or a list of variables available in t he view’s t em plat e.


● The nam e of t he t em plat e or t em plat es t hat are used for t hat view.

Each view docum ent at ion page also has a bookm arklet t hat you can use t o j um p from any page t o t he docum ent at ion page for
t hat view.

Because Dj ango- powered sit es usually use dat abase obj ect s, t he m ode ls sect ion of t he docum ent at ion page describes each t ype
of obj ect in t he syst em along wit h all t he fields available on t hat obj ect .

Taken t oget her, t he docum ent at ion pages should t ell you every t ag, filt er, variable and obj ect available t o you in a given t em plat e.

Conf iguring t he t emplat e syst em in st andalone mode

N ot e
This sect ion is only of int erest t o people t rying t o use t he t em plat e syst em as an out put com ponent in anot her
applicat ion. I f you are using t he t em plat e syst em as part of a Dj ango applicat ion, not hing here applies t o you.

Norm ally, Dj ango will load all t he configurat ion inform at ion it needs from it s own default configurat ion file, com bined wit h t he
set t ings in t he m odule given in t he DJANGO_SETTINGS_MODULE environm ent variable. But if you’re using t he t em plat e syst em
independent ly of t he rest of Dj ango, t he environm ent variable approach isn’t very convenient , because you probably want t o
configure t he t em plat e syst em in line wit h t he rest of your applicat ion rat her t han dealing wit h set t ings files and point ing t o t hem
via environm ent variables.

To solve t his problem , you need t o use t he m anual configurat ion opt ion described in Appendix XXX.

Sim ply im port t he appropriat e pieces of t he t em plat e syst em and t hen, before you call any of t he t em plat e funct ions,
call django.conf.settings.configure() wit h any set t ings you wish t o specify.

You m ight want t o consider set t ing at least TEMPLATE_DIRS ( if you are going t o use t em plat e loaders) , DEFAULT_CHARSET
( alt hough t he default of utf-8 is probably fine) and TEMPLATE_DEBUG. All available set t ings are described in t he Chapt er XXX, and
any set t ing st art ing wit h TEMPLATE_ is of obvious int erest .

« previous ◊ t able of cont ent s ◊ next »


Copyright 2006 Adrian Holovat y and Jacob Kaplan- Moss.
This work is licensed under t he GNU Free Docum ent License.

file:///D|/books/computer/programming/python/books/DJANGO BOOK/10.html (15 of 15)9/28/2007 2:28:08 PM


Chapter 11: Outputting non-HTML content

The Dj ango Book


« previous ◊ t able of cont ent s ◊ next »

Chapt er 11: Generat ing non-HTML cont ent


Usually when we t alk about developing web sit es, we’re t alking about producing som e flavor of HTML. Of course, t here’s a lot
m ore t o t he web t han HTML, t hough; we use t he web t o dist ribut e all kinds of cont ent , not j ust HTML.

Unt il t his point , we’ve focused j ust on t he com m on case of HTML product ion, but in t his chapt er we’ll t ake a det our and look at
using Dj ango t o produce ot her t ypes of cont ent .

Dj ango has convenient built - in t ools t hat you can use t o produce som e com m on non- HTML cont ent :

● RSS/ At om syndicat ion feeds.


● Sit em aps — consum ed by Google, Yahoo and Microsoft ’s search engines.
● JSON and XML serialized represent at ions of m odels ( usually used for AJAX funct ions) .

We’ll cover each of t hose t ools a lit t le lat er on, but first , som e basics.

The basics
Rem em ber t his from Chapt er 3?

A view f unct ion, or view f or short , is simply a Pyt hon f unct ion t hat t akes a Web request and ret urns a Web
response. This response can be t he HTML cont ent s of a Web page, or a redirect , or a 404 error, or an XML
document , or an image…or anyt hing, really.

More form ally, a Dj ango view funct ion m ust :

● Accept an HttpRequest inst ance as it s first argum ent , and


● ret urn an HttpResponse inst ance.

The key t o ret urning non- HTML cont ent from a view lies in t he HttpResponse class, and specifically t he mimetype const ruct or
argum ent . By t weaking t he m im e- t ype, we can indicat e t o t he browser t hat we’ve ret urned an obj ect of a different t ype.

For a very sim ple exam ple, let ’s look at a view t hat ret urns a PNG im age. To keep t hings sim ple, we’ll j ust read t he file off t he
disk:

from django.http import HttpResponse

def my_image(request):
image_data = open("/path/to/my/image.png", "rb").read()
return HttpResponse(image_data, mimetype="image/png")

That ’s it ! I f you replace t he im age pat h in t he open() call wit h a pat h t o a real im age, you can use t his very sim ple view t o serve
an im age, and t he browser will display it correct ly.

The ot her im port ant t hing t o keep in m ind is t hat HttpResponse obj ect s im plem ent Pyt hon’s st andard file API . This m eans t hat
you can pass in an HttpResponse inst ance t o any place Pyt hon ( or a t hird- part y library) expect s a file.

For an exam ple of how t hat works, let ’s t ake a look at producing CSV wit h Dj ango.

Producing CSV
CSV is a sim ple dat a form at usually used by spreadsheet soft ware. I t ’s basically a series of t able rows, wit h each cell in t he row
separat ed by com m as ( CSV st ands for “ Com m a Separat ed Values” ) . For exam ple, here’s a list of t he num ber of “ unruly” airline
passengers over t he last 10 years, as com piled by t he FAA:

Year,Unruly Airline Passengers


1995,146
1996,184
1997,235
1998,200
1999,226
2000,251
2001,299

file:///D|/books/computer/programming/python/books/DJANGO BOOK/11.html (1 of 12)9/28/2007 2:28:28 PM


Chapter 11: Outputting non-HTML content

2002,273
2003,281
2004,304
2005,203

N ot e
See ht t p: / / www.faa.gov/ dat a_st at ist ics/ passengers_cargo/ unruly_passengers/ for t he source of t his dat a.

Unfort unat ely, CSV I t ’s not a form at t hat ’s ever been form ally defined; different pieces of soft ware produce and consum e different
variant s of CSV, m aking it a bit t ricky t o use. Luckily, Pyt hon com es wit h a st andard CSV library, csv, t hat is pret t y m uch
bullet proof.

The key t o using t his library wit h Dj ango is t hat t he csv m odule’s CSV- creat ion capabilit y act s on file- like obj ect s, and
Dj ango’s HttpResponse obj ect s are file- like obj ect s:

import csv
from django.http import HttpResponse
# Number of unruly passengers each year 1995 - 2005
UNRULY_PASSENGERS = [146,184,235,200,226,251,299,273,281,304,203]

def unruly_passengers_csv(request):
# Create the HttpResponse object with the appropriate CSV header.
response = HttpResponse(mimetype='text/csv')
response['Content-Disposition'] = 'attachment; filename=unruly.csv'
# Create the CSV writer using the HttpResponse as the "file"
writer = csv.writer(response)
writer.writerow(['Year', 'Unruly Airline Passengers'])
for (year, num) in zip(range(1995, 2006), UNRULY_PASSENGERS):
writer.writerow([year, num])

return response

The code and com m ent s should be pret t y clear, but a few t hings deserve a m ent ion:

● The response is given t he text/csv m im e- t ype. This t ells browsers t hat t he docum ent is a CSV file, rat her t han an HTML file.
● The response get s an addit ional Content-Disposition header, which cont ains t he nam e of t he CSV file. This header ( well,
t he “ at t achm ent ” part ) will inst ruct t he browser t o prom pt for a locat ion t o save t he file ( inst ead of j ust displaying it ) . This
filenam e is arbit rary; call it what ever you want . I t ’ll be used by browsers in t he “ Save as…” dialogue
● Hooking int o t he CSV- generat ion API is easy: Just pass response as t he first argum ent t o csv.writer. The csv.writer
funct ion expect s a file- like obj ect , and HttpResponse obj ect s fit t he bill.
● For each row in your CSV file, call writer.writerow, passing it an it erable obj ect such as a list or t uple.
● The CSV m odule t akes care of quot ing for you, so you don’t have t o worry about escaping st rings wit h quot es or com m as in
t hem . Just pass inform at ion t o writerow(), and it ’ll do t he right t hing.

You’ll usually repeat t his pat t ern — creat e an HttpResponse response obj ect ( wit h a special m im e- t ype) , pass it t o som et hing
expect ing a file, t hen ret urn t he response — any t im e you generat e non- HTML cont ent .

Let ’s look at a few m ore exam ples:

Generat ing PDFs


PDF ( Port able Docum ent Form at ) is a form at developed by Adobe t hat ’s used t o represent print able docum ent s, com plet e wit h
pixel- perfect form at t ing, em bedded font s, and 2D vect or graphics. You can t hink of a PDF docum ent as t he digit al equivalent of a
print ed docum ent ; indeed, PDFs are usually used when you need t o give a docum ent t o som eone else t o print .

You can easily generat e PDFs wit h Pyt hon and Dj ango t hanks t o t he excellent excellent open- source Report Lab library ( ht t p: / /
www.report lab.org/ rl_t oolkit .ht m l) .

The advant age of generat ing PDF files dynam ically is t hat you can creat e cust om ized PDFs for different purposes — say, for
different users or different pieces of cont ent .

For exam ple, we used Dj ango and Report Lab at KUSport s.com t o generat e cust om ized, print er- ready NCAA t ournam ent bracket s
for people part icipat ing in a March Madness ( college basket ball) cont est .

Installing ReportLab
Before you do any PDF generat ion, however, you’ll need t o inst all Report Lab. I t ’s usually pret t y sim ple: j ust download and inst all

file:///D|/books/computer/programming/python/books/DJANGO BOOK/11.html (2 of 12)9/28/2007 2:28:28 PM


Chapter 11: Outputting non-HTML content

t he library from ht t p: / / www.report lab.org/ downloads.ht m l.

The user guide ( not coincident ally, a PDF file) at ht t p: / / www.report lab.org/ rsrc/ userguide.pdf has addit ional help on inst allat ion.

N ot e
I f you’re using a m odern Linux dist ribut ion, you m ight want t o check your package m anagem ent ut ilit y before
inst alling Report Lab by hand; m ost package reposit ories have added Report Lab.

For exam ple, if you’re using t he ( excellent ) Ubunt u dist ribut ion, a sim ple aptitude install python-reportlab will
do t he t rick nicely.

Test your inst allat ion by im port ing it in t he Pyt hon int eract ive int erpret er:

>>> import reportlab

I f t hat com m and doesn’t raise any errors, t he inst allat ion worked.

Writing your view


Again, key t o generat ing PDFs dynam ically wit h Dj ango is t hat t he Report Lab API act s on file- like obj ect s, and
Dj ango’s HttpResponse obj ect s are file- like obj ect s.

Here’s a “ Hello World” exam ple:

from reportlab.pdfgen import canvas


from django.http import HttpResponse
def hello_pdf(request):
# Create the HttpResponse object with the appropriate PDF headers.
response = HttpResponse(mimetype='application/pdf')
response['Content-Disposition'] = 'attachment; filename=hello.pdf'

# Create the PDF object, using the response object as its "file."
p = canvas.Canvas(response)

# Draw things on the PDF. Here's where the PDF generation happens.
# See the ReportLab documentation for the full list of functionality.
p.drawString(100, 100, "Hello world.")

# Close the PDF object cleanly, and we're done.


p.showPage()
p.save()
return response

Like above, a few not es are in order:

● Here we use t he application/pdf m im e- t ype. This t ells browsers t hat t he docum ent is a PDF file, rat her t han an HTML file.
I f you leave t his off, browsers will probably int erpret t he out put as HTML, which will result in scary gobbledygook in t he
browser window.
● Hooking int o t he Report Lab API is easy: Just pass response as t he first argum ent t o canvas.Canvas. The Canvas class
expect s a file- like obj ect , and HttpResponse obj ect s fit t he bill.
● All subsequent PDF- generat ion m et hods are called on t he PDF obj ect ( in t his case, p) — not on response.
● Finally, it ’s im port ant t o call showPage() and save() on t he PDF file ( or else you’ll end up wit h a corrupt ed PDF file) .

Complex PDFs
I f you’re creat ing a com plex PDF docum ent wit h Report Lab, consider using t he cStringIO library as a t em porary holding place for
your PDF file. The cStringIO library provides a file- like obj ect int erface t hat is part icularly efficient ( m uch m ore so t han t he
naive HttpResponse- as- file im plem ent at ion) .

Here’s t he above “ Hello World” exam ple rewrit t en t o use cStringIO:

from cStringIO import StringIO


from reportlab.pdfgen import canvas
from django.http import HttpResponse

def hello_pdf(request):
# Create the HttpResponse object with the appropriate PDF headers.

file:///D|/books/computer/programming/python/books/DJANGO BOOK/11.html (3 of 12)9/28/2007 2:28:28 PM


Chapter 11: Outputting non-HTML content

response = HttpResponse(mimetype='application/pdf')
response['Content-Disposition'] = 'attachment; filename=hello.pdf'

buffer = StringIO()

# Create the PDF object, using the StringIO object as its "file."
p = canvas.Canvas(buffer)

# Draw things on the PDF. Here's where the PDF generation happens.
# See the ReportLab documentation for the full list of functionality.
p.drawString(100, 100, "Hello world.")
# Close the PDF object cleanly.
p.showPage()
p.save()

# Get the value of the StringIO buffer and write it to the response.
response.write(buffer.getvalue())
return response

Ot her possibilit ies


There’s a whole world of ot her t ypes of cont ent you can generat e in Pyt hon. Here are a few m ore ideas, and som e point ers t o
libraries you could use t o im plem ent t hem :

● Ge n e r a t in g ZI P file s: Pyt hon’s st andard library ships wit h t he zipfile m odule, which can bot h read and writ e com pressed
ZI P files. You could use it t o provide on- dem and archives of a bunch of files, or perhaps com press large docum ent s when
request ed. You could sim ilarly produce TAR files using t he st andard library tarfile m odule.

● D yn a m ic im a ge ge n e r a t ion : t he Pyt hon I m aging Library ( ht t p: / / www.pyt honware.com / product s/ pil/ ) is a fant ast ic t oolkit
for producing im ages ( PNG, JPEG, GI F, and a whole lot m ore) . You could use it t o aut om at ically scale down im ages int o
t hum bnails, com posit e m ult iple im ages int o a single fram e, or even do web- based im age processing.

● Plot s a n d ch a r t s: t here are a num ber of incredibly powerful Pyt hon plot t ing and chart ing libraries you could use t o produce
on- dem and m aps, chart s, plot s, and graphs. We can’t possibly list t hem all, so here are a couple of t he highlight s:

❍ matplotlib ( ht t p: / / m at plot lib.sourceforge.net / ) , which can be used t o produce t he t ype of high- qualit y plot s usually
generat ed wit h Mat Lab or Mat hem at ica.
❍ pygraphviz ( ht t ps: / / net workx.lanl.gov/ wiki/ pygraphviz) , an int erface t o t he Graphviz graph layout t oolkit ( ht t p: / /
graphviz.org/ ) , used for generat ing st ruct ured diagram s of graphs and net works.

I n general, any Pyt hon library capable of writ ing t o a file can be hooked int o Dj ango; t he possibilit ies really are endless.

Now t hat we’ve looked at t he basics of generat ing non- HTML cont ent , let ’s st ep up a level of abst ract ion. Dj ango ships wit h som e
pret t y nift y built - in t ools for generat ing som e com m on t ypes of non- HTML cont ent .

The syndicat ion f eed f ramework


Dj ango com es wit h a high- level syndicat ion- feed- generat ing fram ework t hat m akes creat ing RSS and At om feeds easy.

W h a t ’s RSS? W h a t ’s At om ?
RSS and At om are bot h XML- based form at s you can use t o provide aut om at ically updat ing “ feeds” of your sit e’s
cont ent . Read m ore about RSS at ht t p: / / www.what isrss.com / , and m ore about At om at ht t p: / / www.at om enabled.
org/ .

To creat e any syndicat ion feed, all you have t o do is writ e a short Pyt hon class. You can creat e as m any feeds as you want .

Dj ango also com es wit h a lower- level feed- generat ing API . Use t his if you want t o generat e feeds out side of a Web cont ext , or in
som e ot her lower- level way.

The high-level framework

Overview
The high- level feed- generat ing fram ework is a view t hat ’s hooked t o /feeds/ by default . Dj ango uses t he rem ainder of t he URL
( everyt hing aft er /feeds/) t o det erm ine which feed t o out put .

To creat e a feed, j ust writ e a Feed class and point t o it in your URLconf ( see Chapt ers 3 and 8 fore m ore about URLconfs) .

file:///D|/books/computer/programming/python/books/DJANGO BOOK/11.html (4 of 12)9/28/2007 2:28:28 PM


Chapter 11: Outputting non-HTML content

Initialization
To act ivat e syndicat ion feeds on your Dj ango sit e, add t his line t o your URLconf:

(r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed', {'feed_dict': feeds}),

This t ells Dj ango t o use t he RSS fram ework t o handle all URLs st art ing wit h "feeds/". ( You can change t hat "feeds/" prefix t o fit
your own needs.)

This URLconf line has an ext ra argum ent : {'feed_dict': feeds}. Use t his ext ra argum ent t o pass t he syndicat ion fram ework
t he feeds t hat should be published under t hat URL.

Specifically, feed_dict should be a dict ionary t hat m aps a feed’s slug ( short URL label) t o it s Feed class.

You can define t he feed_dict in t he URLconf it self. Here’s a full exam ple URLconf:

from django.conf.urls.defaults import *


from myproject.feeds import LatestEntries, LatestEntriesByCategory
feeds = {
'latest': LatestEntries,
'categories': LatestEntriesByCategory,
}

urlpatterns = patterns('',
# ...
(r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed',
{'feed_dict': feeds}),
# ...
)

The above exam ple regist ers t wo feeds:

● The feed represent ed by LatestEntries will live at feeds/latest/.


● The feed represent ed by LatestEntriesByCategory will live at feeds/categories/.

Once t hat ’s set up, you j ust need t o define t he Feed classes t hem selves.

Feed classes
A Feed class is a sim ple Pyt hon class t hat represent s a syndicat ion feed. A feed can be sim ple ( e.g., a “ sit e news” feed, or a basic
feed displaying t he lat est ent ries of a blog) or m ore com plex ( e.g., a feed displaying all t he blog ent ries in a part icular cat egory,
where t he cat egory is variable) .

Feed classes m ust subclass django.contrib.syndication.feeds.Feed. They can live anywhere in your code t ree.

A simple example
This sim ple exam ple, t aken from chicagocrim e.org, describes a feed of t he lat est five news it em s:

from django.contrib.syndication.feeds import Feed


from chicagocrime.models import NewsItem

class LatestEntries(Feed):
title = "Chicagocrime.org site news"
link = "/sitenews/"
description = "Updates on changes and additions to chicagocrime.org."

def items(self):
return NewsItem.objects.order_by('-pub_date')[:5]

The im port ant t hings t o not ice here:

● The class subclasses django.contrib.syndication.feeds.Feed.

● title, link and description correspond t o t he st andard RSS <title>, <link> and <description> elem ent s, respect ively.

● items() is sim ply a m et hod t hat ret urns a list of obj ect s t hat should be included in t he feed as <item> elem ent s. Alt hough
t his exam ple ret urns NewsItem obj ect s using Dj ango’s dat abase API , items() doesn’t have t o ret urn m odel inst ances.

You do get a few bit s of funct ionalit y “ for free” by using Dj ango m odels, but items() can ret urn any t ype of obj ect you want .

file:///D|/books/computer/programming/python/books/DJANGO BOOK/11.html (5 of 12)9/28/2007 2:28:28 PM


Chapter 11: Outputting non-HTML content

There’s j ust one m ore st ep. I n an RSS feed, each <item> has a <title>, <link> and <description>. We need t o t ell t he
fram ework what dat a t o put int o t hose elem ent s.

● To specify t he cont ent s of <title> and <description>, creat e Dj ango t em plat es ( see Chapt er 4)
called feeds/latest_title.html and feeds/latest_description.html, where latest is t he slug specified in t he URLconf
for t he given feed.

Not e t hat t he .html ext ension is required.

The RSS syst em renders t hat t em plat e for each it em , passing it t wo t em plat e cont ext variables:

obj
The current obj ect ( one of whichever obj ect s you ret urned in items()) .

site
A django.models.core.sites.Site obj ect represent ing t he current sit e. This is useful for {{ site.domain }}
or {{ site.name }}.

I f you don’t creat e a t em plat e for eit her t he t it le or descript ion, t he fram ework will use t he t em plat e "{{ obj }}" by default
— t hat is, t he norm al st ring represent at ion of t he obj ect .

You can also change t he nam es of t hese t wo t em plat es by specifying title_template and description_template as
at t ribut es of your Feed class.

● To specify t he cont ent s of <link>, you have t wo opt ions. For each it em in items(), Dj ango first t ries execut ing
a get_absolute_url() m et hod on t hat obj ect . I f t hat m et hod doesn’t exist , it t ries calling a m et hod item_link() in t he Feed
class, passing it a single param et er, item, which is t he obj ect it self.

Bot h get_absolute_url() and item_link() should ret urn t he it em ’s URL as a norm al Pyt hon st ring.

● For t he LatestEntries exam ple above, we could have very sim ple feed t em plat es. latest_title.html cont ains:

{{ obj.title }}

and latest_description.html cont ains:

{{ obj.description }}

I t ’s alm ost t oo easy…

A complex example
The fram ework also support s m ore com plex feeds, via param et ers.

For exam ple, chicagocrim e.org offers an RSS feed of recent crim es for every police beat in Chicago. I t ’d be silly t o creat e a
separat e Feed class for each police beat ; t hat would violat e t he DRY ( Don’t Repeat Yourself) principle and would couple dat a t o
program m ing logic.

I nst ead, t he syndicat ion fram ework let s you m ake generic feeds t hat out put it em s based on inform at ion in t he feed’s URL.

On chicagocrim e.org, t he police- beat feeds are accessible via URLs like t his:

● /rss/beats/0613/ — Ret urns recent crim es for beat 0613.


● /rss/beats/1424/ — Ret urns recent crim es for beat 1424.

The slug here is "beats". The syndicat ion fram ework sees t he ext ra URL bit s aft er t he slug — 0613 and 1424 — and gives you a
hook t o t ell it what t hose URL bit s m ean, and how t hey should influence which it em s get published in t he feed.

An exam ple m akes t his clear. Here’s t he code for t hese beat - specific feeds:

from django.core.exceptions import ObjectDoesNotExist

class BeatFeed(Feed):
def get_object(self, bits):
# In case of "/rss/beats/0613/foo/bar/baz/", or other such
# clutter, check that bits has only one member.
if len(bits) != 1:
raise ObjectDoesNotExist
return Beat.objects.get(beat__exact=bits[0])

file:///D|/books/computer/programming/python/books/DJANGO BOOK/11.html (6 of 12)9/28/2007 2:28:28 PM


Chapter 11: Outputting non-HTML content

def title(self, obj):


return "Chicagocrime.org: Crimes for beat %s" % obj.beat
def link(self, obj):
return obj.get_absolute_url()

def description(self, obj):


return "Crimes recently reported in police beat %s" % obj.beat

def items(self, obj):


crimes = Crime.objects.filter(beat__id__exact=obj.id)
return crimes.order_by('-crime_date')[:30]

Here’s t he basic algorit hm t he RSS fram ework follows, given t his class and a request t o t he URL /rss/beats/0613/:

1. The fram ework get s t he URL /rss/beats/0613/ and not ices t here’s an ext ra bit of URL aft er t he slug. I t split s t hat
rem aining st ring by t he slash charact er ( "/") and calls t he Feed class’ get_object() m et hod, passing it t he bit s.

I n t his case, bit s is ['0613']. For a request t o /rss/beats/0613/foo/bar/, bit s would be ['0613', 'foo', 'bar'].

2. get_object() is responsible for ret rieving t he given beat , from t he given bits.

I n t his case, it uses t he Dj ango dat abase API t o ret rieve t he beat . Not e t hat get_object() should
raise django.core.exceptions.ObjectDoesNotExist if given invalid param et ers. There’s no try/ except around
t he Beat.objects.get() call, because it ’s not necessary; t hat funct ion raises Beat.DoesNotExist on failure,
and Beat.DoesNotExist is a subclass of ObjectDoesNotExist. Raising ObjectDoesNotExist in get_object() t ells Dj ango
t o produce a 404 error for t hat request .

3. To generat e t he feed’s <title>, <link> and <description>, Dj ango uses t he title(), link() and description()
m et hods. I n t he previous exam ple, t hey were sim ple st ring class at t ribut es, but t his exam ple illust rat es t hat t hey can be
eit her st rings or m et hods. For each of title, link and description, Dj ango follows t his algorit hm :

1. First , it t ries t o call a m et hod, passing t he obj argum ent , where obj is t he obj ect ret urned by get_object().
2. Failing t hat , it t ries t o call a m et hod wit h no argum ent s.
3. Failing t hat , it uses t he class at t ribut e.
4. Finally, not e t hat items() in t his exam ple also t akes t he obj argum ent . The algorit hm for items is t he sam e as described in
t he previous st ep — first , it t ries items(obj), t hen items(), t hen finally an items class at t ribut e ( which should be a list ) .
Full docum ent at ion on all t he m et hods and at t ribut es of Feed classes is always available from t he official Dj ango docum ent at ion;
see ht t p: / / www.dj angoproj ect .com / docum ent at ion/ syndicat ion/ .

Specifying the type of feed


By default , feeds produced in by fram ework use RSS 2.0.

To change t hat , add a feed_type at t ribut e t o your Feed class:

from django.utils.feedgenerator import Atom1Feed

class MyFeed(Feed):
feed_type = Atom1Feed

Not e t hat you set feed_type t o a class obj ect , not an inst ance. Current ly available feed t ypes are:

Fe e d cla ss For m a t
django.utils.feedgenerator.Rss201rev2Feed RSS 2.01 ( default ) .
django.utils.feedgenerator.RssUserland091Feed RSS 0.91.
django.utils.feedgenerator.Atom1Feed At om 1.0.

Enclosures
To specify enclosures, such as t hose used in creat ing podcast feeds, use t he item_enclosure_url, item_enclosure_length
and item_enclosure_mime_type hooks. For exam ple:

from myproject.models import Song


class MyFeedWithEnclosures(MyFeed):
title = "Example feed with enclosures"
link = "/feeds/example-with-enclosures/"

file:///D|/books/computer/programming/python/books/DJANGO BOOK/11.html (7 of 12)9/28/2007 2:28:28 PM


Chapter 11: Outputting non-HTML content

def items(self):
return Song.objects.all()[:30]
def item_enclosure_url(self, item):
return item.song_url

def item_enclosure_length(self, item):


return item.song_length

item_enclosure_mime_type = "audio/mpeg"

This assum es, of course, you’ve creat ed a Song obj ect wit h song_url and song_length ( i.e. t he size in byt es) fields.

Language
Feeds creat ed by t he syndicat ion fram ework aut om at ically include t he appropriat e <language> t ag ( RSS 2.0) or xml:lang
at t ribut e ( At om ) . This com es direct ly from your LANGUAGE_CODE set t ing.

URLs
The link m et hod/ at t ribut e can ret urn eit her an absolut e URL ( e.g. "/blog/") or a URL wit h t he fully- qualified dom ain and
prot ocol ( e.g. "http://www.example.com/blog/") . I f link doesn’t ret urn t he dom ain, t he syndicat ion fram ework will insert t he
dom ain of t he current sit e, according t o your SITE_ID set t ing.

At om feeds require a <link rel="self"> t hat defines t he feed’s current locat ion. The syndicat ion fram ework populat es t his
aut om at ically, using t he dom ain of t he current sit e according t o t he SITE_ID set t ing.

Publishing Atom and RSS feeds in tandem


Som e developers like t o m ake available bot h At om and RSS versions of t heir feeds. That ’s easy t o do wit h Dj ango: Just creat e a
subclass of your feed class and set t he feed_type t o som et hing different . Then updat e your URLconf t o add t he ext ra versions.

Here’s a full exam ple:

from django.contrib.syndication.feeds import Feed


from chicagocrime.models import NewsItem
from django.utils.feedgenerator import Atom1Feed

class RssSiteNewsFeed(Feed):
title = "Chicagocrime.org site news"
link = "/sitenews/"
description = "Updates on changes and additions to chicagocrime.org."

def items(self):
return NewsItem.objects.order_by('-pub_date')[:5]
class AtomSiteNewsFeed(RssSiteNewsFeed):
feed_type = Atom1Feed

And t he accom panying URLconf:

from django.conf.urls.defaults import *


from myproject.feeds import RssSiteNewsFeed, AtomSiteNewsFeed

feeds = {
'rss': RssSiteNewsFeed,
'atom': AtomSiteNewsFeed,
}

urlpatterns = patterns('',
# ...
(r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed',
{'feed_dict': feeds}),
# ...
)

The sit emap f ramework


Dj ango also com es wit h a high- level Sit em ap generat ing fram ework t hat ’s sim ilar t o t he syndicat ion fram ework.

file:///D|/books/computer/programming/python/books/DJANGO BOOK/11.html (8 of 12)9/28/2007 2:28:28 PM


Chapter 11: Outputting non-HTML content

A Sit em ap is an XML file on your Web sit e t hat t ells search- engine indexers how frequent ly your pages change and how
“ im port ant ” cert ain pages are in relat ion t o ot her pages on your sit e. This inform at ion helps search engines index your sit e.

For m ore on Sit em aps, see ht t p: / / www.sit em aps.org/ .

The Dj ango sit em ap fram ework aut om at es t he creat ion of t his XML file by let t ing you express t his inform at ion in Pyt hon code. To
creat e a sit em ap, you j ust need t o writ e a Sitemap class and point t o it in your URLconf.

Installation
To inst all t he sit em ap app, follow t hese st eps:

1. Add 'django.contrib.sitemaps' t o your INSTALLED_APPS set t ing.


2. Make sure 'django.template.loaders.app_directories.load_template_source' is in your TEMPLATE_LOADERS set t ing.
I t ’s in t here by default , so you’ll only need t o change t his if you’ve changed t hat set t ing.
3. Make sure you’ve inst alled t he sit es fram ework ( see Chapt er 15) .

N ot e
The sit em ap applicat ion doesn’t inst all any dat abase t ables. The only reason it needs t o go int o INSTALLED_APPS is
so t hat t he load_template_source t em plat e loader can find t he default t em plat es.

Initialization
To act ivat e sit em ap generat ion on your Dj ango sit e, add t his line t o your URLconf:

(r'^sitemap.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps})

This t ells Dj ango t o build a sit em ap when a client accesses /sitemap.xml.

The nam e of t he sit em ap file is not im port ant , but t he locat ion is. Search engines will only index links in your sit em ap for t he
current URL level and below. For inst ance, if sitemap.xml lives in your root direct ory, it m ay reference any URL in your sit e.
However, if your sit em ap lives at /content/sitemap.xml, it m ay only reference URLs t hat begin wit h /content/.

The sit em ap view t akes an ext ra, required argum ent : {'sitemaps': sitemaps}. sitemaps should be a dict ionary t hat m aps a
short sect ion label ( e.g., blog or news) t o it s Sitemap class ( e.g., BlogSitemap or NewsSitemap) . I t m ay also m ap t o an inst ance
of a Sitemap class ( e.g., BlogSitemap(some_var)) .

Sitemap classes
A Sitemap class is a sim ple Pyt hon class t hat represent s a “ sect ion” of ent ries in your sit em ap. For exam ple, one Sitemap class
could represent all t he ent ries of your weblog, while anot her could represent all of t he event s in your event s calendar.

I n t he sim plest case, all t hese sect ions get lum ped t oget her int o one sitemap.xml, but it ’s also possible t o use t he fram ework t o
generat e a sit em ap index t hat references individual sit em ap files, one per sect ion. ( See below.)

Sitemap classes m ust subclass django.contrib.sitemaps.Sitemap. They can live anywhere in your code t ree.

For exam ple, let ’s assum e you have a blog syst em , wit h an Entry m odel, and you want your sit em ap t o include all t he links t o
your individual blog ent ries. Here’s how your sit em ap class m ight look:

from django.contrib.sitemaps import Sitemap


from mysite.blog.models import Entry

class BlogSitemap(Sitemap):
changefreq = "never"
priority = 0.5

def items(self):
return Entry.objects.filter(is_draft=False)
def lastmod(self, obj):
return obj.pub_date

Aft er looking at t he syndicat ion fram ework t his should look pret t y fam iliar:

● changefreq and priority are class at t ribut es corresponding t o <changefreq> and <priority> elem ent s, respect ively. They
can be m ade callable as funct ions, as lastmod was in t he exam ple.
● items() is sim ply a m et hod t hat ret urns a list of obj ect s. The obj ect s ret urned will get passed t o any callable m et hods
corresponding t o a sit em ap propert y ( location, lastmod, changefreq, and priority) .

file:///D|/books/computer/programming/python/books/DJANGO BOOK/11.html (9 of 12)9/28/2007 2:28:28 PM


Chapter 11: Outputting non-HTML content

● lastmod should ret urn a Pyt hon datetime obj ect .


● There is no location m et hod in t his exam ple, but you can provide it in order t o specify t he URL for your obj ect . By
default , location() calls get_absolute_url() on each obj ect and ret urns t he result .

Sitemap methods/ attributes


Like Feed classes, Sitemap m em bers can be eit her m et hods or at t ribut es; see t he st eps under “ A com plex exam ple” , above, for
m ore about how t his works.

A Sitemap class can define t he following m et hods/ at t ribut es:

items ( r e qu ir e d)
Provides list of obj ect s. The fram ework doesn’t care what t ype of obj ect s t hey are; all t hat m at t ers is t hat t hese obj ect s get
passed t o t he location(), lastmod(), changefreq() and priority() m et hods.

location ( opt ion a l)


Gives he absolut e URL for a given obj ect ,

Here, “ absolut e URL” m eans a URL t hat doesn’t include t he prot ocol or dom ain. Exam ples:

❍ Good: '/foo/bar/'
❍ Bad: 'example.com/foo/bar/'
❍ Bad: 'http://example.com/foo/bar/'

I f location isn’t provided, t he fram ework will call t he get_absolute_url() m et hod on each obj ect as ret urned by items().

lastmod ( opt ion a l)


The obj ect ’s “ last m odificat ion” dat e, as a Pyt hon datetime obj ect .

changefreq ( opt ion a l)


How oft en t he obj ect changes. Possible values ( as given by t he Sit em aps spec) are:

❍ 'always'
❍ 'hourly'
❍ 'daily'
❍ 'weekly'
❍ 'monthly'
❍ 'yearly'
❍ 'never'

priority ( opt ion a l)


A suggest ed indexing priorit y, bet ween 0.0 and 1.0. The default priorit y of a page is 0.5; see t he sit em aps.org docum ent at ion
for m ore about how priority works.

Shortcuts
The sit em ap fram ework provides a couple convenience classes for com m on cases:

FlatPageSitemap
The django.contrib.sitemaps.FlatPageSitemap class looks at all flat pages defined for t he current sit e and creat es an ent ry in
t he sit em ap. These ent ries include only t he location at t ribut e — not lastmod, changefreq or priority.

See Chapt er 15 for m ore about flat pages.

GenericSitemap
The GenericSitemap class works wit h any generic views ( see Chapt er 9) you already have.

To use it , creat e an inst ance, passing in t he sam e info_dict you pass t o t he generic views. The only requirem ent is t hat t he
dict ionary have a queryset ent ry. I t m ay also have a date_field ent ry t hat specifies a dat e field for obj ect s ret rieved from
t he queryset. This will be used for t he lastmod at t ribut e in t he generat ed sit em ap. You m ay also pass priority and changefreq
keyword argum ent s t o t he GenericSitemap const ruct or t o specify t hese at t ribut es for all URLs.

Here’s an exam ple of a URLconf using bot h FlatPageSitemap and GenericSiteMap ( wit h t he hypot het ical Entry obj ect from
above) :

from django.conf.urls.defaults import *


from django.contrib.sitemaps import FlatPageSitemap, GenericSitemap

file:///D|/books/computer/programming/python/books/DJANGO BOOK/11.html (10 of 12)9/28/2007 2:28:28 PM


Chapter 11: Outputting non-HTML content

from mysite.blog.models import Entry

info_dict = {
'queryset': Entry.objects.all(),
'date_field': 'pub_date',
}

sitemaps = {
'flatpages': FlatPageSitemap,
'blog': GenericSitemap(info_dict, priority=0.6),
}
urlpatterns = patterns('',
# some generic view using info_dict
# ...

# the sitemap
(r'^sitemap.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps})
)

Creating a sitemap index


The sit em ap fram ework also has t he abilit y t o creat e a sit em ap index t hat references individual sit em ap files, one per each
sect ion defined in your sitemaps dict ionary. The only differences in usage are:

● You use t wo views in your URLconf: django.contrib.sitemaps.views.index


and django.contrib.sitemaps.views.sitemap.
● The django.contrib.sitemaps.views.sitemap view should t ake a section keyword argum ent .

Here is what t he relevant URLconf lines would look like for t he exam ple above:

(r'^sitemap.xml$', 'django.contrib.sitemaps.views.index', {'sitemaps': sitemaps})


(r'^sitemap-(?P<section>.+).xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps':
sitemaps})

This will aut om at ically generat e a sitemap.xml file t hat references bot h sitemap-flatpages.xml and sitemap-blog.xml.
The Sitemap classes and t he sitemaps dict ionary don’t change at all.

Pinging Google
You m ay want t o “ ping” Google when your sit em ap changes, t o let it know t o reindex your sit e. The fram ework provides a
funct ion t o do j ust t hat : django.contrib.sitemaps.ping_google().

N ot e
At t he t im e t his book was writ t en, only Google responded t o sit em ap pings. However, it ’s quit e likely t hat Yahoo and/
or Microsoft will soon support t hese pings as well.

At t hat t im e, we’ll likely change t he nam e of ping_google() t o som et hing like ping_search_engines(), so m ake
sure t o check t he lat est sit em ap docum ent at ion at ht t p: / / www.dj angoproj ect .com / docum ent at ion/ sit em aps/ .

ping_google() t akes an opt ional argum ent , sitemap_url, which should be t he absolut e URL of your sit e’s sit em ap ( e.
g., '/sitemap.xml') . I f t his argum ent isn’t provided, ping_google() will at t em pt t o figure out your sit em ap by perform ing a
reverse looking in your URLconf.

ping_google() raises t he except ion django.contrib.sitemaps.SitemapNotFound if it cannot det erm ine your sit em ap URL.

One useful way t o call ping_google() is from a m odel’s save() m et hod:

from django.contrib.sitemaps import ping_google

class Entry(models.Model):
# ...
def save(self):
super(Entry, self).save()
try:
ping_google()
except Exception:
# Bare 'except' because we could get a variety
# of HTTP-related exceptions.

file:///D|/books/computer/programming/python/books/DJANGO BOOK/11.html (11 of 12)9/28/2007 2:28:28 PM


Chapter 11: Outputting non-HTML content

pass

A m ore efficient solut ion, however, would be t o call ping_google() from a cron script , or som e ot her scheduled t ask. The
funct ion m akes an HTTP request t o Google’s servers, so you m ay not want t o int roduce t hat net work overhead each t im e you
call save().

What ’ s next ?
Next , we’ll cont inue t o dig deeper int o all t he nift y built - in t ools Dj ango gives you. Chapt er 12 looks at all t he t ools you need t o
provide user- cust om ized sit es: sessions, users, and aut hent icat ion.

Onwards!

« previous ◊ t able of cont ent s ◊ next »


Copyright 2006 Adrian Holovat y and Jacob Kaplan- Moss.
This work is licensed under t he GNU Free Docum ent License.

file:///D|/books/computer/programming/python/books/DJANGO BOOK/11.html (12 of 12)9/28/2007 2:28:28 PM


Chapter 12: Sessions, users, and registration

The Dj ango Book


« previous ◊ t able of cont ent s ◊ next »

Chapt er 12: Sessions, users, and regist rat ion


I t ’s t im e for a confession: we’ve been deliberat ely ignoring an incredibly im port ant aspect of web developm ent prior t o t his point .
So far, we’ve t hought of t he t raffic visit ing our sit es as som e faceless, anonym ous m ass hurt ling it self against our carefully
designed pages.

This isn’t t rue, of course; t he browsers hit t ing our sit es have real hum ans behind t hem ( som e of t he t im e, at least ) . That ’s a big
t hing t o ignore: t he I nt ernet is at it s best when it serves t o connect people, not m achines. I f we’re going t o develop t ruly
com pelling sit es, event ually we’re going t o have t o deal wit h t he bodies behind t he browsers.

Unfort unat ely, it ’s not all t hat easy. HTTP is designed t o be st a t e le ss; t hat is, each and every request happens in a vacuum .
There’s no persist ence bet ween one request and t he next , and we can’t count on any aspect s of a request ( I P address, user-
agent , et c.) t o consist ent ly indicat e successive request s from t he sam e person.

Browser developers long ago recognized t hat HTTP’s st at elessness poses a huge problem for web developers, and t hus cook ie s
were born. A cookie is a sm all piece of inform at ion t hat browsers st ore on behalf of web servers; every t im e a browser request s a
page from a cert ain server, it gives back t he cookie t hat it init ially received.

Cookies
Let ’s t ake a look how t his m ight work. When you open your browser and t ype in google.com, your browser sends an HTTP
request t o Google t hat st art s som et hing like t his:

GET / HTTP/1.1
Host: google.com
...

When Google replies, t he HTTP response looks som et hing like:

HTTP/1.1 200 OK
Content-Type: text/html
Set-Cookie: PREF=ID=5b14f22bdaf1e81c:TM=1167000671:LM=1167000671;
expires=Sun, 17-Jan-2038 19:14:07 GMT;
path=/; domain=.google.com
Server: GWS/2.1
...

Not ice t he Set-Cookie header. Your browser will st ore t hat cookie value
( PREF=ID=5b14f22bdaf1e81c:TM=1167000671:LM=1167000671) and serve it back t o Google every t im e you access t he sit e. So
t he next t im e you access Google, your browser is going t o send a request like t his:

GET / HTTP/1.1
Host: google.com
Cookie: PREF=ID=5b14f22bdaf1e81c:TM=1167000671:LM=1167000671
...

Google t hen can use t hat Cookie value t o know t hat you’re t he sam e person who accessed t he sit e earlier. This value m ight , for
exam ple, be a key int o a dat abase t hat st ores user inform at ion; Google could ( and does) use it t o display your nam e on t he page.

Getting and setting cookies


When dealing wit h persist ence in Dj ango, m ost of t he t im e you’ll want t o use t he higher- level session and/ or user fram eworks
discussed a lit t le lat er on. However, we’ll pause and look at how t o read and writ e cookies in Dj ango anyway. I t should help you
underst and how t he rest of t he pieces discussed in t he chapt er act ually work, and it ’ll com e in handy if you ever need t o play wit h
cookies direct ly.

Reading cookies t hat are already set is incredibly sim ple: every request obj ect has a COOKIES obj ect t hat act s like a dict ionary;
you can use it t o read any cookies t hat t he browser has sent t o t he view:

def show_color(request):
if "favorite_color" in request.COOKIES:
return HttpResponse("Your favorite color is %s" % \
request.COOKIES["favorite_color"])
else:
return HttpResponse("You don't have a favorite color.")

file:///D|/books/computer/programming/python/books/DJANGO BOOK/12.html (1 of 15)9/28/2007 2:29:08 PM


Chapter 12: Sessions, users, and registration

Writ ing cookies is slight ly m ore com plicat ed; you need t o use t he set_cookie() m et hod on a HttpResponse obj ect . Here’s an
exam ple t hat set s t he favorite_color cookie based on a GET param et er:

def set_color(request):
if "favorite_color" in request.GET:
# Create an HttpResponse object...
response = HttpResponse("Your favorite color is now %s" % \
request.GET["favorite_color"])

# ... and set a cookie on the response


response.set_cookie("favorite_color",
request.GET["favorite_color"])
else:
return HttpResponse("You didn't give a favorite color.")

You can also pass a num ber of opt ional argum ent s t o request.set_cookie() t hat cont rol aspect s of t he cookie:

Pa r a m e t e r D e fa u lt D e scr ipt ion


max_age None Age ( in seconds) t hat t he cookie should last . I f None, t he cookie will last only unt il t he
browser is closed.
The act ual dat e/ t im e when t he cookie should expire. Needs t o be in t he
expires None form at "Wdy, DD-Mth-YY HH:MM:SS GMT". I f given, t his overrides t he max_age
param et er.
The pat h prefix t hat t his cookie is valid for. Browsers will only pass t he cookie back t o
pages below t his pat h prefix, so you can use t his t o prevent cookies from being sent t o
path "/" ot her sect ions of your sit e.
This is especially useful when you don’t cont rol t he t op level of your sit e’s dom ain.
The dom ain t hat t his cookie is valid for. You can use t his t o set a cross- dom ain cookie.
For exam ple, dom ain= ” .exam ple.com ” will set a cookie t hat is readable by t he
domain None dom ains www.example.com, www2.example.com and an.other.sub.domain.example.com
.
I f set t o None, a cookie will only be readable by t he dom ain t hat set it .

secure False I f set t o True, t his inst ruct s t he browser t o only ret urn t his cookie t o pages accessed
over HTTPS.

The mixed blessing of cookies


You m ight not ice a num ber of pot ent ial problem s wit h t he way cookies work. Let ’s look at som e of t he m ore im port ant ones:

● Cookies are essent ially volunt ary; browser don’t guarant ee st orage of cookies. I n fact , every browser on t he planet will let
you cont rol your browser’s policy for accept ing cookies. I f you want t o see j ust how vit al cookies are t o t he web, t ry t urning
on your browser’s “ prom pt t o accept every cookie” opt ion. Even a big blue m onst er would fill up on all t hose cookies!

This m eans, of course, t hat cookies are t he definit ion of unreliabilit y; developers should check t hat a user act ually accept s
cookies before relying on t hem .

More im port ant ly, you should never st ore im port ant dat a in cookies. The web is filled wit h horror st ories of developers
who’ve st ored unrecoverable inform at ion in browser cookies only t o have t hat dat a purged by t he browser for one reason or
anot her.

● Cookies are not in any way secure. Because HTTP dat a is sent in cleart ext , cookies are ext rem ely vulnerable t o snooping
at t acks. That is, an at t acker snooping on t he wire can int ercept a cookie and read it . This m eans you should never st ore
sensit ive inform at ion in a cookie.

There’s an even m ore insidious at t ack known as a “ m an in t he m iddle” at t ack, wherein an at t acker int ercept s a cookie and
uses it t o pose as anot her user. Chapt er 20 discusses at t acks of t his nat ure in dept h, as well as ways t o prevent it .

● Cookies aren’t even secure from t heir int ended recipient s. Most browsers provide easy ways t o edit t he cont ent of individual
cookies, and resourceful users can always use t ools like m echanize t o const ruct HTTP request s by hand.

So you can’t st ore dat a in cookies t hat m ight be sensit ive t o t am pering. The canonical m ist ake in t his scenario is st oring
som et hing like IsLoggedIn=1 in a cookie when a user logs in. You’d be am azed at t he num ber of sit es t hat m ake m ist akes of
t his nat ure; it t akes only a second t o fool t hese sit es’ “ securit y” syst em s.

Dj ango’ s session f ramework

file:///D|/books/computer/programming/python/books/DJANGO BOOK/12.html (2 of 15)9/28/2007 2:29:08 PM


Chapter 12: Sessions, users, and registration

Wit h all of t hese lim it at ions and pot ent ial securit y holes, it ’s obvious t hat cookies and persist ent sessions are anot her of t hose
“ pain point s” in web developm ent . Of course, Dj ango’s goal is t o be an effect ive painkiller, so Dj ango com es wit h a session
fram ework designed t o sm oot h over t hese difficult ies for you.

This session fram ework let s you st ore and ret rieve arbit rary dat a on a per- sit e- visit or basis. I t st ores dat a on t he server side and
abst ract s t he sending and receiving of cookies. Cookies use only a hashed session I D — not t he dat a it self — t hus prot ect ing you
from m ost of t he com m on cookie problem s.

Enabling sessions
Sessions are im plem ent ed via a piece of m iddleware ( see Chapt er 16) and a Dj ango m odel. To enable sessions, you’ll need t o:

1. Edit your MIDDLEWARE_CLASSES set t ing and m ake sure MIDDLEWARE_CLASSES


cont ains 'django.contrib.sessions.middleware.SessionMiddleware'.
2. Make sure 'django.contrib.sessions' is in your INSTALLED_APPS set t ing ( and run manage.py syncdb if you have t o add
it ) .
The default skelet on set t ings creat ed by startproject has bot h of t hese bit s already inst alled, so unless you’ve rem oved t hem ,
you probably don’t have t o change anyt hing t o get sessions t o work.

I f you don’t want t o use sessions, you m ight want t o rem ove t he SessionMiddleware line from MIDDLEWARE_CLASSES
and 'django.contrib.sessions' from your INSTALLED_APPS. I t ’ll only save you a very sm all am ount of overhead, but every
lit t le bit count s.

Using sessions in views


When SessionMiddleware is act ivat ed, each HttpRequest obj ect — t he first argum ent t o any Dj ango view funct ion — will have
a session at t ribut e, which is a dict ionary- like obj ect . You can read it and writ e t o it in t he sam e way you’d use a norm al
dict ionary. For exam ple, in a view you could do st uff like t his:

# Set a session value:


request.session["fav_color"] = "blue"

# Get a session value -- this could be called in a different view,


# or many requests later (or both):
fav_color = request.session["fav_color"]

# Clear an item from the session:


del request.session["fav_color"]

# Check if the session has a given key:


if "fav_color" in request.session:
...

You can also use ot her m apping m et hods like keys() and items() on request.session.

There are a couple of sim ple rules for using Dj ango’s sessions effect ively:

● Use norm al Pyt hon st rings as dict ionary keys on request.session ( as opposed t o int egers, obj ect s, et c.) . This is m ore of a
convent ion t han a hard- and- fast rule, but it ’s wort h following.
● Session dict ionary keys t hat begin wit h an underscore are reserved for int ernal use by Dj ango. I n pract ice t he fram ework
only uses a very sm all num ber of underscore- prefixed session variables, but unless you know what t hey all are ( and are
willing t o keep up wit h any changes in Dj ango it self) , st aying away from underscore prefixes will keep Dj ango from
int erfering wit h your app.
● Don’t override request.session wit h a new obj ect , and don’t access or set it s at t ribut es. Use it like a Pyt hon dict ionary.

Let ’s t ake a look at a few quick exam ples. This sim plist ic view set s a has_commented variable t o True aft er a user post s a
com m ent . I t doesn’t let a user post a com m ent m ore t han once:

def post_comment(request, new_comment):


if request.session.get('has_commented', False):
return HttpResponse("You've already commented.")
c = comments.Comment(comment=new_comment)
c.save()
request.session['has_commented'] = True
return HttpResponse('Thanks for your comment!')

This sim plist ic view logs in a “ m em ber” of t he sit e:

def login(request):
m = members.get_object(username__exact=request.POST['username'])
if m.password == request.POST['password']:

file:///D|/books/computer/programming/python/books/DJANGO BOOK/12.html (3 of 15)9/28/2007 2:29:08 PM


Chapter 12: Sessions, users, and registration

request.session['member_id'] = m.id
return HttpResponse("You're logged in.")
else:
return HttpResponse("Your username and password didn't match.")

And t his one logs a m em ber out , according t o login() above:

def logout(request):
try:
del request.session['member_id']
except KeyError:
pass
return HttpResponse("You're logged out.")

N ot e
I n pract ice, t his is a lousy way of logging users in. The aut hent icat ion fram ework discussed below handles t his for
you in a m uch m ore robust and useful m anner; t hese exam ples are j ust here t o provide easily underst ood exam ples.

Setting test cookies


As m ent ioned above, you can’t rely on every browser accept ing cookies. So, as a convenience, Dj ango provides an easy way t o
t est whet her t he user’s browser accept s cookies. You j ust need t o call request.session.set_test_cookie() in a view, and
check request.session.test_cookie_worked() in a subsequent view — not in t he sam e view call.

This awkward split bet ween set_test_cookie() and test_cookie_worked() is necessary due t o t he way cookies work. When
you set a cookie, you can’t act ually t ell whet her a browser accept ed it unt il t he browser’s next request .

I t ’s good pract ice t o use delete_test_cookie() t o clean up aft er yourself. Do t his aft er you’ve verified t hat t he t est cookie
worked.

Here’s a t ypical usage exam ple:

def login(request):

# If we submitted the form...


if request.method == 'POST':

# Check that the test cookie worked (we set it below):


if request.session.test_cookie_worked():

# The test cookie worked, so delete it.


request.session.delete_test_cookie()
# In practice, we'd need some logic to check username/password
# here, but since this is an example...
return HttpResponse("You're logged in.")

# The test cookie failed, so display an error message. If this


# was a real site we'd want to display a more friendly message.
else:
return HttpResponse("Please enable cookies and try again.")

# If we didn't post, send the test cookie along with the login form.
request.session.set_test_cookie()
return render_to_response('foo/login_form.html')

N ot e
Again, t he built - in login and logout funct ions handle t his check for you.

Using sessions outside of views


I nt ernally, each session is j ust a norm al Dj ango m odel defined in django.contrib.sessions.models. Because it ’s a norm al
m odel, you can access sessions using t he norm al Dj ango dat abase API :

>>> from django.contrib.sessions.models import Session


>>> s = Session.objects.get_object(pk='2b1189a188b44ad18c35e113ac6ceead')

file:///D|/books/computer/programming/python/books/DJANGO BOOK/12.html (4 of 15)9/28/2007 2:29:09 PM


Chapter 12: Sessions, users, and registration

>>> s.expire_date
datetime.datetime(2005, 8, 20, 13, 35, 12)

You’ll need t o call get_decoded() t o get t he act ual session dat a. This is necessary because t he dict ionary is st ored in an encoded
form at :

>>> s.session_data
'KGRwMQpTJ19hdXRoX3VzZXJfaWQnCnAyCkkxCnMuMTExY2ZjODI2Yj...'
>>> s.get_decoded()
{'user_id': 42}

When sessions are saved


By default , Dj ango only saves t o t he session dat abase when t he session has been m odified — t hat is if any of it s dict ionary values
have been assigned or delet ed:

# Session is modified.
request.session['foo'] = 'bar'

# Session is modified.
del request.session['foo']

# Session is modified.
request.session['foo'] = {}

# Gotcha: Session is NOT modified, because this alters


# request.session['foo'] instead of request.session.
request.session['foo']['bar'] = 'baz'

To change t his default behavior, set t he SESSION_SAVE_EVERY_REQUEST set t ing t o True. I f SESSION_SAVE_EVERY_REQUEST is True,
Dj ango will save t he session t o t he dat abase on every single request , even if it wasn’t changed.

Not e t hat t he session cookie is only sent when a session has been creat ed or m odified. I f SESSION_SAVE_EVERY_REQUEST is True,
t he session cookie will be sent on every request .

Sim ilarly, t he expires part of a session cookie is updat ed each t im e t he session cookie is sent .

Browser-length sessions vs. persistent sessions


You m ight have not iced above t hat t he cookie Google sent use cont ained expires=Sun, 17-Jan-2038 19:14:07 GMT;. Cookies
can opt ionally cont ain an expirat ion dat e which advises t he browser on when t o rem ove t he cookie. I f a cookie doesn’t cont ain an
expirat ion value, t he browser will expire it when t he user closes her browser window. You can cont rol t he session fram ework’s
behavior in t his regard wit h t he SESSION_EXPIRE_AT_BROWSER_CLOSE set t ing.

By default , SESSION_EXPIRE_AT_BROWSER_CLOSE is set t o False, which m eans session cookies will be st ored in users’ browsers
for SESSION_COOKIE_AGE seconds ( which default s t o 2 weeks — 1209600 seconds) . Use t his if you don’t want people t o have t o
log in every t im e t hey open a browser.

I f SESSION_EXPIRE_AT_BROWSER_CLOSE is set t o True, Dj ango will use browser- lengt h cookies.

Other session settings


Besides t he set t ings already m ent ioned, t here are a few ot her set t ings t hat influence how Dj ango’s session fram ework uses
cookies:

Se t t in g Ex pla n a t ion D e fa u lt
The dom ain t o use for session cookies. Set t his t o a st ring such
SESSION_COOKIE_DOMAIN as ".lawrence.com" for cross- dom ain cookies, or use None for a None
st andard cookie.
SESSION_COOKIE_NAME The nam e of t he cookie t o use for sessions. This can be any st ring. "sessionid"
Whet her t o use a “ secure” cookie for t he session cookie. I f t his is set
SESSION_COOKIE_SECURE t o True, t he cookie will be m arked as “ secure,” which m eans t hat False
browsers will ensure t hat t he cookie is only sent via HTTPS.

Te ch n ica l de t a ils
For t he curious, here are a few t echnical not es about t he inner workings of t he session fram ework:

● The session dict ionary accept s any pick le a ble Pyt hon obj ect . See t he docum ent at ion for Pyt hon’s built -
in pickle m odule for m ore inform at ion about how t his works.

file:///D|/books/computer/programming/python/books/DJANGO BOOK/12.html (5 of 15)9/28/2007 2:29:09 PM


Chapter 12: Sessions, users, and registration

● Session dat a is st ored in a dat abase t able nam ed django_session.

● Session dat a is ready “ lazily” : if you never access request.session, Dj ango won’t hit t hat dat abase t able.

● Dj ango only sends a cookie if it needs t o. I f you don’t set any session dat a, it won’t send a session cookie
( unless SESSION_SAVE_EVERY_REQUEST is set t o True) .

● The Dj ango sessions fram ework is ent irely, and solely, cookie- based. I t does not fall back t o put t ing session
I Ds in URLs as a last resort , as som e ot her t ools ( PHP, JSP) do.

This is an int ent ional design decision. Put t ing sessions in URLs don’t j ust m ake URLs ugly, t hey m akes your sit e
vulnerable t o a cert ain form of session- I D t heft via t he “ Referer” header.

I f you’re st ill curious, t he source is pret t y st raight forward; look in django.contrib.sessions for t he st reight dope.

Users and aut hent icat ion


So now we’re halfway t o linking browsers direct ly t o Real People. Sessions give us a way of persist ing dat a bet ween m ult iple
browser request s; t he second part of t he equat ion is using t hose sessions t o for user login. Of course, we can’t j ust t rust t hat
users are who t hey say t hey are, so we’ll need t o aut hent icat e t hem along t he way.

Nat urally, Dj ango provides t ools t o handle t his com m on t ask ( and m any ot hers) . Dj ango’s user aut hent icat ion syst em handles
user account s, groups, perm issions and cookie- based user sessions. This syst em is oft en referred t o as an “ aut h/ aut h” syst em —
aut hent icat ion and aut horizat ion. That nam e recognizes t hat dealing wit h users is oft en a t wo st ep process; we need t o

1. verify ( a u t h e n t ica t e ) t hat a user is who she claim s t o be ( usually by checking a usernam e and password against a dat abase
of users) , and t hen
2. verify t hat t he user is a u t h or ize d t o perform som e given operat ion ( usually checking against a t able of perm issions) .
Following t hese needs, Dj ango’s aut h/ aut h syst em consist s of a num ber of part s:

● Users
● Perm issions: binary ( yes/ no) flags designat ing whet her a user m ay perform a cert ain t ask.
● Groups: a generic way of applying labels and perm issions t o m ore t han one user.
● Messages: a sim ple way t o queue and display syst em m essages t o users.
● Profiles: a m echanism t o ext end t he user obj ect wit h cust om fields.

I f you’ve used t he adm in t ool ( Chapt er 6) , you’ve already seen m any of t hese t ools, and if you’ve edit ed users or groups in t he
adm in you’ve act ually been edit ing dat a in t he aut h syst em ’s dat abase t ables.

Installation
Like t he session t ools, aut hent icat ion support is bundled as a Dj ango applicat ion in django.contrib which needs t o be inst alled.
Like t he session syst em it ’s also inst alled by default , but if you’ve rem oved it you’ll need t o follow t hese st eps t o inst all it :

1. Make sure t he session fram ework is inst alled ( see above) . Keeping t rack of users obviously requires cookies, and t hus builds
on t he session fram ework.
2. Put 'django.contrib.auth' in your INSTALLED_APPS set t ing and run manage.py syncdb.
3. Make sure t hat 'django.contrib.auth.middleware.AuthenticationMiddleware' is in your MIDDLEWARE_CLASSES set t ing
— aft er SessionMiddleware.
Wit h t hat inst allat ion out of t he way, we’re ready t o deal wit h users in view funct ions. The m ain int erface you’ll use t o access
users wit hin a view is request.user; t his is an obj ect t hat represent s t he current ly logged- in user. I f t he user isn’t logged in, t his
will inst ead be an AnonymousUser obj ect ( see below for m ore det ails) .

You can easily t ell if a user is logged in wit h t he is_authenticated() m et hod:

if request.user.is_authenticated():
# Do something for authenticated users.
else:
# Do something for anonymous users.

Using users
Once you’ve got ahold of a user — oft en from request.user, but possibly t hrough one of t he ot her m et hods discussed below —
you’ve got a num ber of fields m et hods available on t hat obj ect . AnonymousUser obj ect s em ulat e som e of t hese fields and
m et hods, but not all of t hem , so you should always check user.is_authenticated() before assum ing you’re dealing wit h a bona-
fide user obj ect .

Fie lds on Userobj e ct s

file:///D|/books/computer/programming/python/books/DJANGO BOOK/12.html (6 of 15)9/28/2007 2:29:09 PM


Chapter 12: Sessions, users, and registration

Fie ld D e scr ipt ion


username Required. 30 charact ers or fewer. Alphanum eric charact ers only ( let t ers, digit s and
underscores) .
first_name Opt ional. 30 charact ers or fewer.
last_name Opt ional. 30 charact ers or fewer.
email Opt ional. E- m ail address.
password Required. A hash of, and m et adat a about , t he password ( Dj ango doesn’t st ore t he raw
password) . See t he “ Passwords” sect ion below for m ore about t his value
is_staff Boolean. Designat es whet her t his user can access t he adm in sit e.
is_active Boolean. Designat es whet her t his account can be used t o log in. Set t his flag t o False inst ead
of delet ing account s.
is_superuser Boolean. Designat es t hat t his user has all perm issions wit hout explicit ly assigning t hem .
last_login A dat et im e of t he user’s last login. I s set t o t he current dat e/ t im e by default .
date_joined A dat et im e designat ing when t he account was creat ed. I s set t o t he current dat e/ t im e by
default when t he account is creat ed.

M e t h ods on Userobj e ct s
M e t h od D e scr ipt ion
Always ret urns True for “ real” User obj ect s. This is a way t o t ell if t he user has
is_authenticated() been aut hent icat ed. This does not im ply any perm issions, and doesn’t check if
t he user is act ive - it only indicat es t hat t he user has sucessfully aut hent icat ed.
Ret urns True only for AnonymousUser obj ect s ( and False for “ real” User
is_anonymous() obj ect s) . Generally, you should prefer using is_authenticated() t o t his
m et hod.
get_full_name() Ret urns t he first_name plus t he last_name, wit h a space in bet ween.
set_password(passwd) Set s t he user’s password t o t he given raw st ring, t aking care of t he password
hashing. This doesn’t act ually save t he User obj ect .
check_password(passwd) Ret urns True if t he given raw st ring is t he correct password for t he user. This
t akes care of t he password hashing in m aking t he com parison.
get_group_permissions() Ret urns a list of perm ission st rings t hat t he user has t hrough t he groups she
belongs t o.
get_all_permissions() Ret urns a list of perm ission st rings t hat t he user has, bot h t hrough group and
user perm issions.
Ret urns True if t he user has t he specified perm ission, where perm is in t he
has_perm(perm) form at "package.codename". I f t he user is inact ive, t his m et hod will always
ret urn False.
Ret urns True if t he user has all of t he specified perm issions, I f t he user is
has_perms(perm_list)
inact ive, t his m et hod will always ret urn False.
has_module_perms(appname) Ret urns True if t he user has any perm issions in t he given appname I f t he user is
inact ive, t his m et hod will always ret urn False.
get_and_delete_messages() Ret urns a list of Message obj ect s in t he user’s queue and delet es t he m essages
from t he queue.
Sends an e- m ail t o t he user. This em ail is sent from t he DEFAULT_FROM_EMAIL
email_user(subj, msg) set t ing. You can also pass a t hird argum ent , from_email, t o override t he from
address on t he em ail.
get_profile() Ret urns a sit e- specific profile for t his user; see t he sect ion on profiles, below,
for m ore on t his m et hod

Finally, User obj ect s have t wo m any- t o- m any fields: groups and permissions. User obj ect s can access t heir relat ed obj ect s in
t he sam e way as any ot her m any- t o- m any field:

# Set a users groups:


myuser.groups = group_list

# Add a user to some groups:


myuser.groups.add(group1, group2,...)

# Remove a user from some groups:


myuser.groups.remove(group1, group2,...)

# Remove a user from all groups:

file:///D|/books/computer/programming/python/books/DJANGO BOOK/12.html (7 of 15)9/28/2007 2:29:09 PM


Chapter 12: Sessions, users, and registration

myuser.groups.clear()

# Permissions work the same way


myuser.permissions = permission_list
myuser.permissions.add(permission1, permission2, ...)
myuser.permissions.remove(permission1, permission2, ...)
myuser.permissions.clear()

Logging in and out


Dj ango provides built - in view funct ions for handling logging in and out ( and a few ot her nift y t ricks) , but before we get t o t hose
let ’s t ake a look at how t o log users in and out “ by hand” . Dj ango provides t wo funct ions t o perform t hese act ions
in django.contrib.auth: authenticate() and login().

To aut hent icat e a given usernam e and password, use authenticate(). I t t akes t wo keyword argum ent s, username and password
, and it ret urns a User obj ect if t he password is valid for t he given usernam e. I f t he password is invalid, authenticate()
ret urns None:

>>> from django.contrib import auth authenticate


>>> user = auth.authenticate(username='john', password='secret')
>>> if user is not None:
... print "Correct!"
... else:
... print "Oops, that's wrong!"
Oops, that's wrong!

To log a user in, in a view, use login(). I t t akes an HttpRequest obj ect and a User obj ect and saves t he user’s I D in t he session,
using Dj ango’s session fram ework.

This exam ple shows how you m ight use bot h authenticate() and login() wit hin a view funct ion:

from django.contrib import auth

def login(request):
username = request.POST['username']
password = request.POST['password']
user = auth.authenticate(username=username, password=password)
if user is not None and user.is_active:
# Correct password, and the user is marked "active"
auth.login(request, user)
# Redirect to a success page.
return HttpResponseRedirect("/account/loggedin/")
else:
# Show an error page
return HttpResponseRedirect("/account/invalid/")

To log out a user who has been logged, use django.contrib.auth.logout() wit hin your view. I t t akes an HttpRequest obj ect
and has no ret urn value:

from django.contrib import auth


def logout(request):
auth.logout(request)
# Redirect to a success page.
return HttpResponseRedirect("/account/loggedout/")

Not e t hat logout() doesn’t t hrow any errors if t he user wasn’t logged in.

Logging in and out, the easy way


I n pract ice, you’ll usually not need t o writ e your own login/ logout funct ions; t he aut h syst em com es wit h a set of views for
generically handling logging in and out .

The first st ep in using t he aut hent icat ion views is t o wire ‘em up in your URLconf. You’ll need t o add t his snippet :

from django.contrib.auth.views import login, logout

urlpatterns = patterns('',
# existing patterns here...
(r'^accounts/login/$', login)
(r'^accounts/logout/$', logout)

file:///D|/books/computer/programming/python/books/DJANGO BOOK/12.html (8 of 15)9/28/2007 2:29:09 PM


Chapter 12: Sessions, users, and registration

/accounts/login/ and /accounts/logout/ are t he default URLs t hat Dj ango uses for t hese views, but you can put t hem
anywhere you like wit h a lit t le effort .

By default , t he login view renders a t em plat e at registration/login.html ( you can change t his t em plat e nam e by passing an
ext ra view argum ent template_name) . This form needs t o cont ain a username and a password field. A sim ple t em plat e m ight look
like:

{% extends "base.html" %}

{% block content %}

{% if form.errors %}
<p class="error">Sorry, that's not a valid username or password</p>
{% endif %}

<form action='.' method='post'>


<label for="username">User name:</label>
<input type="text" name="username" value="" id="username">
<label for="password">Password:</label>
<input type="password" name="password" value="" id="password">
<input type="submit" value="login" />
<input type="hidden" name="next" value="{{ next }}" />
<form action='.' method='post'>

{% endblock %}

I f t he user successfully logs in, she’ll be redirect ed t o /accounts/profile/ by default . You can override t his by providing a
hidden field called next wit h t he URL t o redirect t o aft er logging in. You can also pass t his value as a GET param et er t o t he login
view and it ’ll be aut om at ically added t o t he cont ext as a variable called next t hat you can insert int o t hat hidden field.

The log out view works a lit t le different ly; by default it renders a t em plat e at registration/logged_out.html ( which usually
cont ains a “ you’ve successfully logged out ” m essage) . However, you can call t he view wit h an ext ra argum ent , next_page, which
will inst ruct t he view t o redirect aft er a log out .

Limiting access to logged-in users


Of course, t he reason we’re going t hrough all t his t rouble is so t hat we can lim it access t o part s of our sit e.

The sim ple, raw way t o lim it access t o pages is t o check request.user.is_authenticated() and redirect t o a login page:

from django.http import HttpResponseRedirect

def my_view(request):
if not request.user.is_authenticated():
return HttpResponseRedirect('/login/?next=%s' % request.path)
# ...

Or perhaps display an error m essage:

def my_view(request):
if not request.user.is_authenticated():
return render_to_response('myapp/login_error.html')
# ...

As a short cut , you can use t he convenient login_required decorat or:

from django.contrib.auth.decorators import login_required

@login_required
def my_view(request):
# ...

login_required does t he following:

● I f t he user isn’t logged in, redirect t o /accounts/login/, passing t he current absolut e URL in t he query st ring as next. For
exam ple: /accounts/login/?next=/polls/3/.
● I f t he user is logged in, execut e t he view norm ally. The view code can t hen assum e t hat t he user is logged in.

file:///D|/books/computer/programming/python/books/DJANGO BOOK/12.html (9 of 15)9/28/2007 2:29:09 PM


Chapter 12: Sessions, users, and registration

N ot e
I f you’re int o program m ing pat t erns, not e t hat t his decorat or and t he ones discussed below are exam ples of t he
“ Guard” pat t ern. Aren’t pat t erns fun?

Limiting access to users that pass a test


Lim it ing access based on cert ain perm issions or som e ot her t est , or providing a different locat ion for t he log- in view works
essent ially t he sam e way.

The raw way is t o run your t est on request.user in t he view direct ly. For exam ple, t his view checks t o m ake sure t he user is
logged in and has t he perm ission polls.can_vote ( see below for m ore about how perm issions work) :

def vote(request):
if request.user.is_authenticated() and request.user.has_perm('polls.can_vote')):
# vote here
else:
return HttpResponse("You can't vote in this poll.")

Again, Dj ango provides a short cut . This one is called user_passes_test which is act ually a de cor a t or fa ct or y: it t akes
argum ent s and generat es a specialized decorat or for your part icular sit uat ion. For exam ple:

def user_can_vote(user):
return user.is_authenticated() and user.has_perm("polls.can_vote")

@user_passes_text(user_can_vote, login_url="/login/")
def vote(request):
# Code here can assume a logged in user with the correct permission.
...

user_passes_test t akes one required argum ent : a callable t hat t akes a User obj ect and ret urns True if t he user is allowed t o
view t he page. Not e t hat user_passes_test does not aut om at ically check t hat t he User is aut hent icat ed; you should do t hat
yourself.

I n t his exam ple we’re also showing t he second opt ional argum ent , login_url, which let s you specify t he URL for your login page
( /accounts/login/ by default ) .

Since it ’s a relat ively com m on t ask t o check whet her a user has a part icular perm ission, Dj ango provides a short cut for t hat case:
t he permission_required() decorat or. Using t his decorat or, t he earlier exam ple can be writ t en as:

from django.contrib.auth.decorators import permission_required

@permission_required('polls.can_vote', login_url="/login/")
def vote(request):
# ...

Not e t hat permission_required() also t akes an opt ional login_url param et er which also default s t o '/accounts/login/'.

Limiting access to generic views


One of t he m ost frequent ly asked quest ions on t he Dj ango- users list deals wit h lim it ing access t o a generic view. To pull t his off,
you’ll need t o writ e a t hin wrapper around t he view, and point your URLconf t o your wrapper inst ead of t he generic view it self:

from dango.contrib.auth.decorators import login_required


from django.views.generic.date_based import object_detail
@login_required
def limited_object_detail(*args, **kwargs):
return object_detail(*args, **kwargs)

You can, of course, replace login_required wit h any of t he ot her lim it ing decorat ors.

Managing users, permissions, and groups


The easiest way by far t o m anage t he aut h syst em is t hrough t he adm in. Chapt er 6 discusses how t o use Dj ango’s adm in t o edit
users and cont rol t heir perm issions and access, and m ost of t he t im e you’ll j ust use t hat int erface.

However, t here are low- level API s you can delve int o when you need absolut e cont rol.

file:///D|/books/computer/programming/python/books/DJANGO BOOK/12.html (10 of 15)9/28/2007 2:29:09 PM


Chapter 12: Sessions, users, and registration

Creating users
The basic way t o creat e users is t o use t he create_user helper funct ion:

>>> from django.contrib.auth.models import User


>>> user = User.objects.create_user(username='john',
... email='jlennon@beatles.com',
... password='glass onion')

At t his point , user is a User inst ance ready t o be saved t o t he dat abase. You can cont inue t o change it s at t ribut es before saving,
t oo:

>>> user.is_staff = True


>>> user.save()

Changing passwords
You can change a password wit h set_password():

>>> user = User.objects.get(username='john')


>>> user.set_password('goo goo goo joob')
>>> user.save()

Don’t set t he password at t ribut e direct ly unless you know what you’re doing; t he password is act ually st ored as a sa lt e d h a sh
and t hus can’t be edit ed direct ly.

More form ally, t he password at t ribut e of a User obj ect is a st ring in t his form at :

hashtype$salt$hash

That ’s a hash t ype, t he salt and t he hash it self, separat ed by t he dollar- sign charact er.

hashtype is eit her sha1 ( default ) or md5 — t he algorit hm used t o perform a one- way hash of t he password. Salt is a random
st ring used t o salt t he raw password t o creat e t he hash.

For exam ple:

sha1$a1976$a36cc8cbf81742a8fb52e221aaeab48ed7f58ab4

The User.set_password() and User.check_password() funct ions handle t he set t ing and checking of t hese values behind t he
scenes.

I s t h a t som e k in d of dr u g?
No, a sa lt e d h a sh has not hing t o do wit h m arij uana; it ’s act ually a com m on way t o securly st ore passwords. A
h a sh is a one- way cryt ographic funct ion; t hat is, you can easily com put e t he hash of a given value, but it ’s nearly
im possible t o t ake a hash and reconst ruct t he original value.

I f we st ored passwords as plain t ext , anyone who got t heir hands on t he password dat abase would inst ant ly know
everyone’s password. St oring passwords as hashes reduces t he value of a com prom ised dat abase.

However, an at t acker wit h t he password dat abase could st ill run a br u t e for ce at t ack, hashing m illions of
passwords and com paring t hose hashes against t he st ored values. This m ight t ake som e t im e, but less t han you
t hink — com put ers are incredibly fast .

Worse, t here are publically available r a in bow t a ble s — dat abases of precom put ed hashes of m illions of passwords.
Wit h a rainbow t able, an at t acker can break m ost passwords in seconds.

Adding a sa lt — basically an init ial random value — t o t he st ored hash adds anot her layer of difficult y. Since t he salt
will differ from password t o password, salt s also prevent t he use of a rainbow t able, t hus forcing at t ackers t o fall
back on a brut e force at t ack — it self m ade m ore difficult by t he ext ra ent ropy added t o t he hash by t he salt .

While salt ed hashes aren’t absolut ely t he m ost secure way of st oring passwords, t hey’re a good m iddle ground
bet ween securit y and convience.

Handling registration
We can use t hese low- level t ools t o creat e views t hat allow users t o sign up. Nearly every developer want s t o im plem ent
regist rat ion different ly, so Dj ango leaves writ ing a regist rat ion view up t o you; luckily, it ’s pret t y easy.

file:///D|/books/computer/programming/python/books/DJANGO BOOK/12.html (11 of 15)9/28/2007 2:29:09 PM


Chapter 12: Sessions, users, and registration

At it s sim plest , we could provide a sm all view t hat prom pt s for t he required user inform at ion and creat es t hose users. Dj ango
provides a built - in form you can use for t his purpose, which we’ll use in t his exam ple:

from django import oldforms as forms


from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response
from django.contrib.auth.forms import UserCreationForm

def register(request):
form = UserCreationForm()

if request.method == 'POST':
data = request.POST.copy()
errors = form.get_validation_errors(data)
if not errors:
new_user = form.save()
return HttpResponseRedirect("/accounts/created/")
else:
data, errors = {}, {}

return render_to_response("registration/register.html", {
'form' : forms.FormWrapper(form, data, errors)
})

This assum es a t em plat e nam ed registration/register.html; here’s an exam ple of what t hat t em plat e m ight look like:

{% extends "base.html" %}
{% block title %}Create an account{% endblock %}
{% block content %}
<h1>Create an account</h1>
<form action="." method="post">
{% if form.error_dict %}
<p class="error">Please correct the errors below.</p>
{% endif %}
{% if form.username.errors %}
{{ form.username.html_error_list }}
{% endif %}
<label for="id_username">Username:</label> {{ form.username }}

{% if form.password1.errors %}
{{ form.password1.html_error_list }}
{% endif %}
<label for="id_password1">Password: {{ form.password1 }}

{% if form.password2.errors %}
{{ form.password2.html_error_list }}
{% endif %}
<label for="id_password2">Password (again): {{ form.password2 }}

<input type="submit" value="Create the account" />


</label>
{% endblock %}

Using authentication data in templates


The current ly logged- in user and his/ her perm issions are m ade available in t he t em plat e cont ext when you use RequestContext
( see Chapt er 10) .

N ot e
Technically, t hese variables are only m ade available in t he t em plat e cont ext if you use RequestContext and
your TEMPLATE_CONTEXT_PROCESSORS set t ing cont ains "django.core.context_processors.auth", which is default .
Again, see Chapt er 10 for t he full skinny.

When using RequestContext, t he current user — eit her a User inst ance or an` ` Anonym ousUser` ` inst ance — is st ored in t he
t em plat e variable {{ user }}:

file:///D|/books/computer/programming/python/books/DJANGO BOOK/12.html (12 of 15)9/28/2007 2:29:09 PM


Chapter 12: Sessions, users, and registration

{% if user.is_authenticated %}
<p>Welcome, {{ user.username }}. Thanks for logging in.</p>
{% else %}
<p>Welcome, new user. Please log in.</p>
{% endif %}

This user’s perm issions are st ored in t he t em plat e variable {{ perms }}. This is is a t em plat e- friendly proxy of t o a couple of
perm ission m et hods. See t he sect ion on perm issions, below, for m ore about what t hese m et hods m ap t o.

There are t wo ways you can use t his perms obj ect . You can use som et hing like {{ perms.polls }} t o check if t he user has any
perm issions som e given app, or you can use som et hing like {{ perms.polls.can_vote }} t o check if t he user has a specific
perm ission.

Thus, you can check perm issions in t em plat e {% if %} st at em ent s:

{% if perms.polls %}
<p>You have permission to do something in the polls app.</p>
{% if perms.polls.can_vote %}
<p>You can vote!</p>
{% endif %}
{% else %}
<p>You don't have permission to do anything in the polls app.</p>
{% endif %}

The ot her bit s: permissions, groups, messages, and prof iles


There’s a few ot her bit s of t he aut hent icat ion fram ework t hat we’ve only dealt wit h in passing. Let ’s t ake a closer look at t hem :

Permissions
Perm issions are a sim ple way t o “ m ark” users and groups as being able t o perform som e act ion. I t ’s usually used by t he Dj ango
adm in sit e, but you can easily use it in your own code.

The Dj ango adm in sit e uses perm issions as follows:

● Access t o view t he “ add” form and add an obj ect is lim it ed t o users wit h t he “ add” perm ission for t hat t ype of obj ect .
● Access t o view t he change list , view t he “ change” form and change an obj ect is lim it ed t o users wit h t he “ change” perm ission
for t hat t ype of obj ect .
● Access t o delet e an obj ect is lim it ed t o users wit h t he “ delet e” perm ission for t hat t ype of obj ect .

Perm issions are set globally per t ype of obj ect , not per specific obj ect inst ance. For exam ple, it ’s possible t o say “ Mary m ay
change news st ories,” but it ’s not current ly possible t o say “ Mary m ay change news st ories, but only t he ones she creat ed herself”
or “ Mary m ay only change news st ories t hat have a cert ain st at us, publicat ion dat e or I D.”

These t hree basic perm issions — add, creat e and delet e — are aut om at ically creat ed for each Dj ango m odel t hat has
a class Admin. Behind t he scenes, t hese perm issions are added t o t he auth_permission dat abase t able when you
run manage.py syncdb.

These perm issions will be of t he form "<app>.<action>_<object_name>". That is, if you’ve got a polls app wit h a Choice
m odel, you’ll get perm issions nam ed "polls.add_choice", "polls.change_choice" and "polls.delete_choice".

Not e t hat if your m odel doesn’t have class Admin set when you run syncdb, t he perm issions won’t be creat ed. I f you init ialize
your dat abase and add class Admin t o m odels aft er t he fact , you’ll need t o run syncdb again t o creat e any m issing perm issions
for your inst alled apps.

You can also creat e cust om perm issions for a given m odel obj ect , using t he permissions at t ribut e on Meta. This exam ple m odel
creat es t hree cust om perm issions:

class USCitizen(models.Model):
# ...
class Meta:
permissions = (
# Permission identifier human-readable permission name
("can_drive", "Can drive"),
("can_vote", "Can vote in elections"),
("can_drink", "Can drink alcohol"),
)

This only creat es t hose ext ra perm issions when you run syncdb; it ’s up t o you t o check for t hese perm issions in your views ( see
above) .

Just like users, perm issions are im plem ent ed in a Dj ango m odel t hat lives in django.contrib.auth.models; t his m eans t hat you

file:///D|/books/computer/programming/python/books/DJANGO BOOK/12.html (13 of 15)9/28/2007 2:29:09 PM


Chapter 12: Sessions, users, and registration

can use Dj ango’s dat abase API t o int eract direct ly wit h perm issions if you like.

Groups
Groups are a generic way of cat egorizing users so you can apply perm issions, or som e ot her label, t o t hose users. A user can
belong t o any num ber of groups.

A user in a group aut om at ically has t he perm issions grant ed t o t hat group. For exam ple, if t he group Site editors has t he
perm ission can_edit_home_page, any user in t hat group will have t hat perm ission.

Beyond perm issions, groups are a convenient way t o cat egorize users t o give t hem som e label, or ext ended funct ionalit y. For
exam ple, you could creat e a group 'Special users', and you could writ e code t hat could, say, give t hem access t o a m em bers-
only port ion of your sit e, or send t hem m em bers- only e- m ail m essages.

Like users, t he easiest way t o m anage groups in t hrough t he adm in. However, groups are also j ust Dj ango m odels t hat live
in django.contrib.auth.models, so once again you can always use Dj ango’s dat abase API s t o deal wit h groups at a low level.

Messages
The m essage syst em is a light weight way t o queue m essages for given users. A m essage is associat ed wit h a User. There’s no
concept of expirat ion or t im e st am ps.

Messages are used by t he Dj ango adm in aft er successful act ions. For exam ple, when you creat e an obj ect , you’ll not ice a “ The
obj ect was creat ed successfully” m essage at t he t op of t he adm in page.

You can use t he sam e API t o queue and display m essages in your own app. The API is sim ple:

● To creat e a new m essage, use user.message_set.create(message='message_text').


● To ret rieve/ delet e m essages, use user_obj.get_and_delete_messages(), which ret urns a list of Message obj ect s in t he
user’s queue ( if any) and delet es t he m essages from t he queue.

I n t his exam ple view, t he syst em saves a m essage for t he user aft er creat ing a playlist :

def create_playlist(request, songs):


# Create the playlist with the given songs.
# ...
request.user.message_set.create(
message="Your playlist was added successfully."
)
return render_to_response("playlists/create.html",
context_instance=RequestContext(request))

When you use RequestContext, t he current ly logged- in user and his/ her m essages are m ade available in t he t em plat e cont ext as
t he t em plat e variable {{ messages }}. Here’s an exam ple of t em plat e code t hat displays m essages:

{% if messages %}
<ul>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}

Not e t hat RequestContext calls get_and_delete_messages behind t he scenes, so any m essages will be delet ed even if you don’t
display t hem .

Finally, not e t hat t his m essages fram ework only works wit h users in t he user dat abase. To send m essages t o anonym ous users,
use t he session fram ework direct ly.

Profiles
The final piece of t he puzzle is t he profile syst em . To underst and what profiles are all about , let ’s first look at t he problem :

I n a nut shell, m any sit es need t o st ore m ore user inform at ion t han is available on t he st andard User obj ect . To com pound t he
problem , m ost sit es will have different “ ext ra” fields. Thus, Dj ango provides a light weight way of defining a “ profile” obj ect t hat ’s
linked t o a given user; t his profile obj ect can differ from proj ect t o proj ect , and can even handle different profiles for different
sit es served from t he sam e dat abase.

The first st ep in creat ing a profile is t o define a m odel t hat holds t he profile inform at ion. The only requirem ent Dj ango places on
t his m odel is t hat it have a unique ForeignKey t o t he User m odel; t his field m ust be nam ed user Ot her t hat t hat , you can use
any ot her fields you like. Here’s a st rict ly arbit rary profile m odel:

file:///D|/books/computer/programming/python/books/DJANGO BOOK/12.html (14 of 15)9/28/2007 2:29:09 PM


Chapter 12: Sessions, users, and registration

from django.db import models


from django.contrib.auth.models import User

class MySiteProfile(models.Model):
# This is the only required field
user = models.ForeignKey(User, unique=True)

# The rest is completely up to you...


favorite_band = models.CharField(maxlength=100, blank=True)
favorite_cheese = models.CharField(maxlength=100, blank=True)
lucky_number = models.IntegerField()

Next , you’ll need t o t ell Dj ango where t o look for t his profile obj ect . You do t hat by set t ing t he AUTH_PROFILE_MODULE set t ing t o
t he ident ifier for your m odel. So, if your m odel lives in an app called myapp, you’d put t his in your set t ings file:

AUTH_PROFILE_MODULE = "myapp.mysiteprofile"

Once t hat ’s done, you can access a user’s profile by calling user.get_profile(). This funct ion will raise
a SiteProfileNotAvailable except ion if AUTH_PROFILE_MODULE isn’t defined, and it also m ight raise a DoesNotExist except ion if
t he user doesn’t have a profile already ( you’ll usually cat ch t hat except ion and creat e a new profile at t hat t im e) .

Wrapping up
Yes, t he session and aut horizat ion syst em is a lot t o absorb. Most of t he t im e you won’t need all t he feat ures described in t his
chapt er, but when you need t o allow com plex int eract ions bet ween users, it ’s good t o have all t hat power available.

I n t he next chapt er, we’ll t ake a look at a piece of Dj ango t hat builds on t op of t his session/ user syst em : t he com m ent s app. I t
allows you t o easily at t ach com m ent s — from anonym ous or aut hent icat ed users — t o arbit rary obj ect s.

Onwards and upwards!

« previous ◊ t able of cont ent s ◊ next »


Copyright 2006 Adrian Holovat y and Jacob Kaplan- Moss.
This work is licensed under t he GNU Free Docum ent License.

file:///D|/books/computer/programming/python/books/DJANGO BOOK/12.html (15 of 15)9/28/2007 2:29:09 PM


Chapter 14: Caching

The Dj ango Book


« previous ◊ t able of cont ent s ◊ next »

Chapt er 14: Caching


St at ic websit es, in which sim ple files are served direct ly t o t he Web, scale like crazy. But a fundam ent al t radeoff in dynam ic Web
sit es is, well, t hey’re dynam ic. Each t im e a user request s a page, t he Web server m akes all sort s of calculat ions — from dat abase
queries t o t em plat e rendering t o business logic — t o creat e t he page t hat your sit e’s visit or sees. From a processing- overhead
perspect ive, t his is a lot m ore expensive.

For m ost Web applicat ions, t his overhead isn’t a big deal. Most Web applicat ions aren’t washingt onpost .com or slashdot .org;
t hey’re sim ply sm all- t o m edium - sized sit es wit h so- so t raffic. But for m edium - t o high- t raffic sit es, it ’s essent ial t o cut as m uch
overhead as possible.

That ’s where caching com es in.

To cache som et hing is t o save t he result of an expensive calculat ion so t hat you don’t have t o perform t he calculat ion next t im e.
Here’s som e pseudocode explaining how t his would work for a dynam ically generat ed Web page:

given a URL, try finding that page in the cache


if the page is in the cache:
return the cached page
else:
generate the page
save the generated page in the cache (for next time)
return the generated page

Dj ango com es wit h a robust cache syst em t hat let s you save dynam ic pages so t hey don’t have t o be calculat ed for each request .
For convenience, Dj ango offers different levels of cache granularit y. You can cache t he out put of specific views, you can cache
only t he pieces t hat are difficult t o produce, or you can cache your ent ire sit e.

Dj ango also works well wit h “ upst ream ” caches, such as Squid ( ht t p: / / www.squid- cache.org/ ) and browser- based caches. These
are t he t ypes of caches t hat you don’t direct ly cont rol but t o which you can provide hint s ( via HTTP headers) about which part s of
your sit e should be cached, and how.

Set t ing up t he cache


The cache syst em requires a sm all am ount of set up. Nam ely, you have t o t ell it where your cached dat a should live — whet her in
a dat abase, on t he filesyst em or direct ly in m em ory. This is an im port ant decision t hat affect s your cache’s perform ance; yes,
som e cache t ypes are fast er t han ot hers. I n- m em ory caching will generally be m uch fast er t han filesyst em or dat abase caching,
because t he form er lacks t he overhead of hit t ing t he filesyst em or dat abase.

Your cache preference goes in t he CACHE_BACKEND set t ing in your set t ings file. I f you use caching and do not
specify CACHE_BACKEND, Dj ango will use simple:/// by default . Here’s an explanat ion of all available values for CACHE_BACKEND.

Memcached
By far t he fast est , m ost efficient t ype of cache available t o Dj ango, Mem cached is an ent irely m em ory- based cache fram ework
originally developed t o handle high loads at LiveJournal.com and subsequent ly open- sourced by Danga I nt eract ive( ht t p: / / www.
danga.com ) . I t ’s used by sit es such as Slashdot and Wikipedia t o reduce dat abase access and dram at ically increase sit e
perform ance.

Mem cached is available for free at ht t p: / / danga.com / m em cached/ . I t runs as a daem on and is allot t ed a specified am ount of
RAM. I t s prim ary feat ure is t o provide an int erface — a super- light ning- fast int erface — for adding, ret rieving and delet ing
arbit rary dat a in t he cache. All dat a is st ored direct ly in m em ory, so t here’s no overhead of dat abase or filesyst em usage.

Aft er inst alling Mem cached it self, you’ll need t o inst all t he Mem cached Pyt hon bindings, which are not bundled wit h Dj ango
direct ly. These bindings are in a single Pyt hon m odule, m em cache.py, available at ht t p: / / www.dj angoproj ect .com / t hirdpart y/
pyt hon- m em cached/ .

To use Mem cached wit h Dj ango, set CACHE_BACKEND t o memcached://ip:port/, where ip is t he I P address of t he Mem cached
daem on and port is t he port on which Mem cached is running.

I n t his exam ple, Mem cached is running on localhost ( 127.0.0.1) port 11211:

CACHE_BACKEND = 'memcached://127.0.0.1:11211/'

One excellent feat ure of Mem cached is it s abilit y t o share cache over m ult iple servers. This m eans you can run Mem cached

file:///D|/books/computer/programming/python/books/DJANGO BOOK/Chapter 14 Caching.htm (1 of 3)9/28/2007 2:29:35 PM


Chapter 14: Caching

daem ons on m ult iple m achines, and t he program will t reat t he group of m achines as a single cache, wit hout t he need t o duplicat e
cache values on each m achine. To t ake advant age of t his feat ure wit h Dj ango, include all server addresses in CACHE_BACKEND,
separat ed by sem icolons.

I n t his exam ple, t he cache is shared over Mem cached inst ances running on t he I P addresses 172.19.26.240 and 172.19.26.242,
bot h on port 11211:

CACHE_BACKEND = 'memcached://172.19.26.240:11211;172.19.26.242:11211/'

I n t his exam ple, t he cache is shared over Mem cached inst ances running on t he I P addresses 172.19.26.240 ( port 11211) ,
172.19.26.242 ( port 11212) and 172.19.26.244 ( port 11213) :

CACHE_BACKEND = 'memcached://172.19.26.240:11211;172.19.26.242:11212;172.19.26.244:11213/'

A final point about Mem cached is t hat m em ory- based caching has one im port ant disadvant age. Because t he cached dat a is st ored
only in m em ory, t he dat a will be lost if your server crashes. Clearly, m em ory isn’t int ended for perm anent dat a st orage, so don’t
rely on m em ory- based caching as your only dat a st orage. Wit hout a doubt , none of t he Dj ango caching backends should be used
for perm anent st orage — t hey’re all int ended t o be solut ions for caching, not st orage — but we point t his out here because
m em ory- based caching is part icularly t em porary.

Database caching
To use a dat abase t able as your cache backend, creat e a cache t able in your dat abase and point Dj ango’s cache syst em at t hat
t able.

First , creat e a cache t able by running t his com m and:

python manage.py createcachetable [cache_table_name]

…where [cache_table_name] is t he nam e of t he dat abase t able t o creat e. This nam e can be what ever you want , as long as it ’s a
valid t able nam e t hat ’s not already being used in your dat abase. This com m and creat es a single t able in your dat abase t hat is in
t he proper form at Dj ango’s dat abase- cache syst em expect s.

Once you’ve creat ed t hat dat abase t able, set your CACHE_BACKEND set t ing t o "db://tablename", where tablename is t he nam e of
t he dat abase t able. I n t his exam ple, t he cache t able’s nam e is my_cache_table:

CACHE_BACKEND = 'db://my_cache_table'

The dat abase caching backend uses t he sam e dat abase as specified in your set t ings file. You can’t use a different dat abase
backend for your cache t able.

Filesystem caching
To st ore cached it em s on a filesyst em , use t he "file://" cache t ype for CACHE_BACKEND, specifying t he direct ory on your
filesyst em t hat should st ore t he cached dat a.

For exam ple, t o st ore cached dat a in /var/tmp/django_cache, use t his set t ing:

CACHE_BACKEND = 'file:///var/tmp/django_cache'

Not e t hat t here are t hree forward slashes t oward t he beginning of t hat exam ple. The first t wo are for file://, and t he t hird is
t he first charact er of t he direct ory pat h, /var/tmp/django_cache. I f you’re on Windows, put t he drive let t er aft er t he file://,
like so: : file://c:/foo/bar.

The direct ory pat h should be absolut e — t hat is, it should st art at t he root of your filesyst em . I t doesn’t m at t er whet her you put a
slash at t he end of t he set t ing.

Make sure t he direct ory point ed- t o by t his set t ing exist s and is readable and writ able by t he syst em user under which your Web
server runs. Cont inuing t he above exam ple, if your server runs as t he user apache, m ake sure t he
direct ory /var/tmp/django_cache exist s and is readable and writ able by t he user apache.

Each cache value will be st ored as a separat e file whose cont ent s are t he cache dat a saved in a serialized ( “ pickled” ) form at ,
using Pyt hon’s pickle m odule. Each file’s nam e is t he cache key, escaped for safe filesyst em use.

Local-memory caching
I f you want t he speed advant ages of in- m em ory caching but don’t have t he capabilit y of running Mem cached, consider t he local-
m em ory cache backend. This cache is m ult i- process and t hread- safe, but isn’t as efficient as Mem cached due t o it s sim plist ic
locking and m em ory allocat ion st rat egies.

To use it , set CACHE_BACKEND t o 'locmem:///'. For exam ple:

file:///D|/books/computer/programming/python/books/DJANGO BOOK/Chapter 14 Caching.htm (2 of 3)9/28/2007 2:29:35 PM


Chapter 14: Caching

CACHE_BACKEND = 'locmem:///'

Simple caching (for development)


A sim ple, single- process m em ory cache is available as 'simple:///'. This m erely saves cached dat a in process, which m eans it
should only be used in developm ent or t est ing environm ent s. For exam ple:

CACHE_BACKEND = 'simple:///'

Dummy caching (for development)


Finally, Dj ango com es wit h a “ dum m y” cache t hat doesn’t act ually cache — it j ust im plem ent s t he cache int erface wit hout doing
anyt hing.

This is useful if you have a product ion sit e t hat uses heavy- dut y caching in various places but a developm ent / t est environm ent on
which you don’t want t o cache. I n t hat case, set CACHE_BACKEND t o 'dummy:///' in t he set t ings file for your developm ent
environm ent . As a result , your developm ent environm ent won’t use caching and your product ion environm ent st ill will. For
exam ple:

CACHE_BACKEND = 'dummy:///'

CACHE_BACKEND arguments
Each cache backend m ay t ake argum ent s. They’re given in query- st ring st yle on t he CACHE_BACKEND set t ing. Valid argum ent s are:

file:///D|/books/computer/programming/python/books/DJANGO BOOK/Chapter 14 Caching.htm (3 of 3)9/28/2007 2:29:35 PM


Chapter 15: Other contributed sub-frameworks

The Dj ango Book


« previous ◊ t able of cont ent s ◊ next »

Chapt er 15: Ot her cont ribut ed sub-f rameworks


One of t he m any st rengt hs of Pyt hon is it s “ bat t eries included” philosophy; when you inst all Pyt hon, it com es wit h a large
“ st andard library” of com m only used m odules t hat you can st art using im m ediat ely, wit hout having t o download anyt hing else.
Dj ango aim s t o follow t his philosophy, as it includes it s own st andard library of add- ons useful for com m on Web developm ent
t asks. This chapt er covers t hat collect ion of add- ons.

About t he st andard library


Dj ango’s st andard library lives in t he package django.contrib. Wit hin, each subpackage is a separat e piece of add- on
funct ionalit y. These pieces are not necessarily relat ed, but som e django.contrib subpackages m ay require ot her ones.

There’s no hard requirem ent for t he t ypes of funct ionalit y in django.contrib. Som e of t he packages include m odels ( and, hence,
require you t o inst all t heir dat abase t ables int o your dat abase) , but ot hers consist solely of m iddleware or t em plat e t ags.

The single charact erist ic t he django.contrib packages have in com m on is t his: I f you were t o rem ove t he django.contrib
package ent irely, you could st ill use Dj ango’s fundam ent als wit h no problem s. When t he developers of Dj ango add new
funct ionalit y t o t he fram ework, t hey use t his rule of t hum b in deciding whet her t he new funct ionalit y should live
in django.contrib or elsewhere.

django.contrib consist s of t hese packages:

● admin — The aut om at ic adm in sit e. See Chapt er 6.


● auth — Dj ango’s aut hent icat ion fram ework. See Chapt er 12.
● comments — A com m ent s applicat ion. See Chapt er 13.
● contenttypes — A fram ework for hooking int o “ t ypes” of cont ent , where each inst alled Dj ango m odel is a separat e cont ent
t ype. See “ Cont ent t ypes” below.
● csrf — Prot ect ion against Cross Sit e Request Forgeries. See “ CSRF prot ect ion” below.
● flatpages — A fram ework for m anaging sim ple “ flat ” HTML cont ent in a dat abase. See “ Flat pages” below.
● formtools — A set of high- level abst ract ions for Dj ango form s. See “ Form t ools” below.
● humanize — A set of Dj ango t em plat e filt ers useful for adding a “ hum an t ouch” t o dat a. See “ Hum anizing dat a” below.
● markup — A set of Dj ango t em plat e filt ers t hat im plem ent a num ber of com m on m arkup languages. See “ Markup filt ers”
below.
● redirects — A fram ework for m anaging redirect s. See “ Redirect s” below.
● sessions — Dj ango’s session fram ework. See Chapt er 12.
● sitemaps — A fram ework for generat ing sit em ap XML files. See “ Sit em aps” below.
● sites — A fram ework t hat let s you operat e m ult iple Web sit es off of t he sam e dat abase and Dj ango inst allat ion. See “ Sit es”
below.
● syndication — A fram ework for generat ing syndicat ion feeds in RSS and At om . See “ Syndicat ion feeds” below.

The rest of t his chapt er goes int o det ail about each django.contrib package t hat hasn’t yet been covered in t his book.

Sit es
Dj ango’s “ sit es” syst em is a generic fram ework t hat let s you operat e m ult iple Web sit es off of t he sam e dat abase and Dj ango
proj ect . As t his is an abst ract concept , it can be t ricky t o underst and — so we’ll st art wit h a couple of exam ples.

Example 1: Reusing data on multiple sites


As we explained in Chapt er 1, t he Dj ango- powered sit es LJWorld.com and Lawrence.com are operat ed by t he sam e news
organizat ion — t he Lawrence Journal- World newspaper in Lawrence, Kansas. LJWorld.com focuses on news, while Lawrence.com
focuses on local ent ert ainm ent . But som et im es edit ors want t o publish an art icle on bot h sit es.

The brain- dead way of solving t he problem would be t o use a separat e dat abase for each sit e, and t o require sit e producers t o
publish t he sam e st ory t wice: once for LJWorld.com and again for Lawrence.com . But t hat ’s inefficient for sit e producers, and it ’s
redundant t o st ore m ult iple copies of t he sam e st ory in t he dat abase.

The bet t er solut ion is sim ple: Bot h sit es use t he sam e art icle dat abase, and an art icle is associat ed wit h one or m ore sit es via a
m any- t o- m any relat ionship. The Dj ango sit es fram ework provides t he dat abase t able t o which art icles can be relat ed. I t ’s a hook
for associat ing dat a wit h one or m ore “ sit es.”

Example 2: Storing your site name/ domain in one place

file:///D|/books/computer/programming/python/books/DJANGO BOOK/15.html (1 of 9)9/28/2007 2:30:07 PM


Chapter 15: Other contributed sub-frameworks

LJWorld.com and Lawrence.com bot h have e- m ail alert funct ionalit y, which let s readers sign up t o get not ificat ions when news
happens. I t ’s pret t y basic: A reader signs up on a Web form , and he im m ediat ely get s an e- m ail saying, “ Thanks for your
subscript ion.”

I t ’d be inefficient and redundant t o im plem ent t his signup- processing code t wice, so t he sit es use t he sam e code behind t he
scenes. But t he “ t hank you for signing up” not ice needs t o be different for each sit e. By using Site obj ect s, we can abst ract t he
“ t hank you” not ice t o use t he values of t he current sit e’s name ( e.g., 'LJWorld.com') and domain ( e.g., 'www.ljworld.com') .

The Dj ango sit es fram ework provides a place for you t o st ore t he name and domain for each sit e in your Dj ango proj ect , which
m eans you can reuse t hose values in a generic way.

Using the sites framework


The sit es fram ework is m ore of a series of convent ions t han a fram ework. The whole t hing is based on t wo sim ple concept s:

● The Site m odel, found in django.contrib.sites, has domain and name fields.
● The SITE_ID set t ing specifies t he dat abase I D of t he Site obj ect associat ed wit h t hat part icular set t ings file.

How you use t hese t wo concept s is up t o you, but Dj ango uses t hem in a couple of ways aut om at ically via sim ple convent ions.

To inst all t he sit es app, follow t hese st eps:

1. Add 'django.contrib.sites' t o your INSTALLED_APPS.


2. Run t he com m and manage.py syncdb t o inst all t he django_site t able int o your dat abase.
3. Add one or m ore Site obj ect s, eit her t hrough t he Dj ango adm in sit e or via t he Pyt hon API . Creat e a Site obj ect for each
sit e/ dom ain t hat t his Dj ango proj ect powers.
4. Define t he SITE_ID set t ing in each of your set t ings files. This value should be t he dat abase I D of t he Site obj ect for t he sit e
powered by t hat set t ings file.

Things you can do with the sites framework

Reusing data on multiple sites


To reuse dat a on m ult iple sit es, as explained in “ Exam ple 1,” j ust creat e a ManyToManyField t o Site in your m odels. For exam ple:

from django.db import models


from django.contrib.sites.models import Site

class Article(models.Model):
headline = models.CharField(maxlength=200)
# ...
sites = models.ManyToManyField(Site)

That ’s t he necessary infrast ruct ure you need in order t o associat e art icles wit h m ult iple sit es in your dat abase. Wit h t hat in place,
you can reuse t he sam e Dj ango view code for m ult iple sit es. Cont inuing t he Article exam ple, here’s what an article_detail
view m ight look like:

from django.conf import settings


def article_detail(request, article_id):
try:
a = Article.objects.get(id=article_id, sites__id=settings.SITE_ID)
except Article.DoesNotExist:
raise Http404
# ...

This view funct ion is reusable because it checks t he art icle’s sit e dynam ically, according t o t he value of t he SITE_ID set t ing.

For exam ple, say LJWorld.com ’s set t ings file has a SITE_ID set t o 1 and Lawrence.com ’s set t ings file has a SITE_ID set t o 2. I f
t his view is called when LJWorld.com ’s set t ings file is act ive, t hen it will lim it t he art icle lookup t o art icles in which t he list of sit es
includes LJWorld.com .

Associating content with a single site


Sim ilarly, you can associat e a m odel t o t he Site m odel in a m any- t o- one relat ionship, using ForeignKey.

For exam ple, if an art icle is only allowed on a single sit e, you’d use a m odel like t his:

from django.db import models


from django.contrib.sites.models import Site

file:///D|/books/computer/programming/python/books/DJANGO BOOK/15.html (2 of 9)9/28/2007 2:30:07 PM


Chapter 15: Other contributed sub-frameworks

class Article(models.Model):
headline = models.CharField(maxlength=200)
# ...
site = models.ForeignKey(Site)

This has t he sam e benefit s as described in t he last sect ion.

Hooking into the current site from views


On a lower level, you can use t he sit es fram ework in your Dj ango views t o do part icular t hings based on what sit e in which t he
view is being called. For exam ple:

from django.conf import settings

def my_view(request):
if settings.SITE_ID == 3:
# Do something.
else:
# Do something else.

Of course, it ’s ugly t o hard- code t he sit e I Ds like t hat . This sort of hard- coding is best for hackish fixes t hat you need done
quickly. A slight ly cleaner way of accom plishing t he sam e t hing is t o check t he current sit e’s dom ain:

from django.conf import settings


from django.contrib.sites.models import Site

def my_view(request):
current_site = Site.objects.get(id=settings.SITE_ID)
if current_site.domain == 'foo.com':
# Do something
else:
# Do something else.

The idiom of ret rieving t he Site obj ect for t he value of settings.SITE_ID is quit e com m on, so t he Site m odel’s m anager
( Site.objects) has a get_current() m et hod. This exam ple is equivalent t o t he previous one:

from django.contrib.sites.models import Site

def my_view(request):
current_site = Site.objects.get_current()
if current_site.domain == 'foo.com':
# Do something
else:
# Do something else.

Not e t hat in t his final exam ple, you don’t have t o im port django.conf.settings.

Getting the current domain for display


For a DRY ( Don’t Repeat Yourself) approach t o st oring your sit e’s nam e and dom ain nam e, as explained in “ Exam ple 2,” j ust
reference t he name and domain of t he current Site obj ect . For exam ple:

from django.contrib.sites.models import Site


from django.core.mail import send_mail

def register_for_newsletter(request):
# Check form values, etc., and subscribe the user.
# ...
current_site = Site.objects.get_current()
send_mail('Thanks for subscribing to %s alerts' % current_site.name,
'Thanks for your subscription. We appreciate it.\n\n-The %s team.' % current_site.
name,
'editor@%s' % current_site.domain,
[user_email])
# ...

Cont inuing our ongoing exam ple of LJWorld.com and Lawrence.com : On Lawrence.com , t his e- m ail has t he subj ect line “ Thanks
for subscribing t o lawrence.com alert s.” On LJWorld.com , t he e- m ail has t he subj ect “ Thanks for subscribing t o LJWorld.com
alert s.” This sam e sit e- specific behavior is done in t he e- m ail’s m essage body.

file:///D|/books/computer/programming/python/books/DJANGO BOOK/15.html (3 of 9)9/28/2007 2:30:07 PM


Chapter 15: Other contributed sub-frameworks

Not e t hat an even m ore flexible ( but m ore heavyweight ) way of doing t his would be t o use Dj ango’s t em plat e syst em . Assum ing
Lawrence.com and LJWorld.com have different t em plat e direct ories ( TEMPLATE_DIRS) , you could sim ply delegat e t o t he t em plat e
syst em like so:

from django.core.mail import send_mail


from django.template import loader, Context
def register_for_newsletter(request):
# Check form values, etc., and subscribe the user.
# ...
subject = loader.get_template('alerts/subject.txt').render(Context({}))
message = loader.get_template('alerts/message.txt').render(Context({}))
send_mail(subject, message, 'do-not-reply@example.com', [user_email])
# ...

I n t his case, you’d have t o creat e subject.txt and message.txt t em plat es in bot h t he LJWorld.com and Lawrence.com t em plat e
direct ories. That gives you m ore flexibilit y, but it ’s also m ore com plex.

I t ’s a good idea t o exploit t he Site obj ect s as m uch as possible, t o rem ove unneeded com plexit y and redundancy.

Getting the current domain for full URLs


Dj ango’s get_absolute_url() convent ion is nice for get t ing your obj ect s’ URL wit hout t he dom ain nam e, but in som e cases you
m ight want t o display t he full URL — wit h http:// and t he dom ain and everyt hing — for an obj ect . To do t his, you can use t he
sit es fram ework. A sim ple exam ple:

>>> from django.contrib.sites.models import Site


>>> obj = MyModel.objects.get(id=3)
>>> obj.get_absolute_url()
'/mymodel/objects/3/'
>>> Site.objects.get_current().domain
'example.com'
>>> 'http://%s%s' % (Site.objects.get_current().domain, obj.get_absolute_url())
'http://example.com/mymodel/objects/3/'

The CurrentSiteManager
I f Sites play a key role in your applicat ion, consider using t he helpful CurrentSiteManager in your m odel( s) . I t ’s a m odel
m anager ( see Chapt er 5) t hat aut om at ically filt ers it s queries t o include only obj ect s associat ed wit h t he current Site.

Use CurrentSiteManager by adding it t o your m odel explicit ly. For exam ple:

from django.db import models


from django.contrib.sites.models import Site
from django.contrib.sites.managers import CurrentSiteManager

class Photo(models.Model):
photo = models.FileField(upload_to='/home/photos')
photographer_name = models.CharField(maxlength=100)
pub_date = models.DateField()
site = models.ForeignKey(Site)
objects = models.Manager()
on_site = CurrentSiteManager()

Wit h t his m odel, Photo.objects.all() will ret urn all Photo obj ect s in t he dat abase, but Photo.on_site.all() will ret urn only
t he Photo obj ect s associat ed wit h t he current sit e, according t o t he SITE_ID set t ing.

I n ot her words, t hese t wo st at em ent s are equivalent :

Photo.objects.filter(site=settings.SITE_ID)
Photo.on_site.all()

How did CurrentSiteManager know which field of Photo was t he Site? I t default s t o looking for a field called site. I f your m odel
has a ForeignKey or ManyToManyField called som et hing ot her t han site, you need t o explicit ly pass t hat as t he param et er
t o CurrentSiteManager. The following m odel, which has a field called publish_on, dem onst rat es t his:

from django.db import models


from django.contrib.sites.models import Site
from django.contrib.sites.managers import CurrentSiteManager

file:///D|/books/computer/programming/python/books/DJANGO BOOK/15.html (4 of 9)9/28/2007 2:30:07 PM


Chapter 15: Other contributed sub-frameworks

class Photo(models.Model):
photo = models.FileField(upload_to='/home/photos')
photographer_name = models.CharField(maxlength=100)
pub_date = models.DateField()
publish_on = models.ForeignKey(Site)
objects = models.Manager()
on_site = CurrentSiteManager('publish_on')

I f you at t em pt t o use CurrentSiteManager and pass a field nam e t hat doesn’t exist , Dj ango will raise a ValueError.

Finally, not e t hat you’ll probably want t o keep a norm al ( non- sit e- specific) Manager on your m odel, even if you
use CurrentSiteManager. As explained in Chapt er 5, if you define a m anager m anually, t hen Dj ango won’t creat e t he
aut om at ic objects = models.Manager() m anager for you. Also, not e t hat cert ain part s of Dj ango — nam ely, t he Dj ango adm in
sit e and generic views — use whichever m anager is defined first in t he m odel, so if you want your adm in sit e t o have access t o all
obj ect s ( not j ust sit e- specific ones) , put objects = models.Manager() in your m odel, before you define CurrentSiteManager.

How Dj ango uses the sites framework


Alt hough it ’s not required t hat you use t he sit es fram ework, it ’s st rongly encouraged, because Dj ango t akes advant age of it in a
few places. Even if your Dj ango inst allat ion is powering only a single sit e, you should t ake t he t wo seconds t o creat e t he sit e
obj ect wit h your domain and name, and point t o it s I D in your SITE_ID set t ing.

Here’s how Dj ango uses t he sit es fram ework:

● I n t he redirect s fram ework ( see “ Redirect s” below) , each redirect obj ect is associat ed wit h a part icular sit e. When Dj ango
searches for a redirect , it t akes int o account t he current SITE_ID.
● I n t he com m ent s fram ework ( see Chapt er 13) , each com m ent is associat ed wit h a part icular sit e. When a com m ent is
post ed, it s site is set t o t he current SITE_ID, and when com m ent s are list ed via t he appropriat e t em plat e t ag, only t he
com m ent s for t he current sit e are displayed.
● I n t he flat pages fram ework ( see “ Flat pages” below) , each flat page is associat ed wit h a part icular sit e. When a flat page is
creat ed, you specify it s site, and t he flat page m iddleware checks t he current SITE_ID in ret rieving flat pages t o display.
● I n t he syndicat ion fram ework ( see “ Syndicat ion feeds” below) , t he t em plat es for title and description aut om at ically have
access t o a variable {{{ site }}}, which is t he Site obj ect represent ing t he current sit e. Also, t he hook for providing it em
URLs will use t he domain from t he current Site obj ect if you don’t specify a fully- qualified dom ain.
● I n t he aut hent icat ion fram ework ( see Chapt er 12) , t he django.contrib.auth.views.login view passes t he current Site
nam e t o t he t em plat e as {{{ site_name }}}.
● The short cut view ( see Chapt er XX) uses t he dom ain of t he current Site obj ect when calculat ing an obj ect ’s URL.

Flat pages
Oft en t im es, you’ll have a dat abase- driven Web applicat ion up and running, but you’ll need t o add a couple “ one- off” st at ic pages,
such as an “ About ” page or a “ Privacy Policy” page. I t ’d be possible t o use a st andard Web server such as Apache t o serve t hese
files as flat HTML files, but t hat int roduces an ext ra level of com plexit y int o your applicat ion, because t hen you have t o worry
about configuring Apache, you’ve got t o set up access for your t eam t o edit t hose files, and you can’t t ake advant age of Dj ango’s
t em plat e syst em t o st yle t he pages.

The solut ion t o t his problem is Dj ango’s “ flat pages” app, which lives in t he package django.contrib.flatpages. This app let s
you m anage such “ one- off” pages via Dj ango’s adm in sit e, and it let s you specify t em plat es for t hem using Dj ango’s t em plat e
syst em . I t uses Dj ango m odels behind t he scenes, which m eans it st ores t he pages in a dat abase, j ust like t he rest of your dat a,
and you can access flat pages wit h t he st andard Dj ango dat abase API .

Flat pages are keyed by t heir URL and sit e. When you creat e a flat page, you specify which URL it ’s associat ed wit h, along wit h
which sit e( s) it ’s on. ( For m ore on sit es, see t he “ Sit es” sect ion above.)

Using flatpages
To inst all t he flat pages app, follow t hese st eps:

1. Add 'django.contrib.flatpages' t o your INSTALLED_APPS.


2. Add 'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware' t o your MIDDLEWARE_CLASSES set t ing.
3. Run t he com m and manage.py syncdb t o inst all t he t wo required t ables int o your dat abase.

How it works
The flat pages app creat es t wo t ables in your dat abase: django_flatpage and django_flatpage_sites. django_flatpage is a
lookup t able t hat sim ply m aps a URL t o a t it le and bunch of t ext cont ent . django_flatpage_sites is a m any- t o- m any t able t hat
associat es a flat page wit h one or m ore sit es.

The app com es wit h a single FlatPage m odel, defined in django/contrib/flatpages/models.py. I t looks like t his:

from django.db import models


from django.contrib.sites.models import Site

file:///D|/books/computer/programming/python/books/DJANGO BOOK/15.html (5 of 9)9/28/2007 2:30:07 PM


Chapter 15: Other contributed sub-frameworks

class FlatPage(models.Model):
url = models.CharField(maxlength=100)
title = models.CharField(maxlength=200)
content = models.TextField()
enable_comments = models.BooleanField()
template_name = models.CharField(maxlength=70, blank=True)
registration_required = models.BooleanField()
sites = models.ManyToManyField(Site)

Let ’s cover t hese fields one at a t im e:

● url — The URL at which t his flat page lives, excluding t he dom ain nam e but including t he leading slash.
Exam ple: '/about/contact/.
● title — The t it le of t he flat page. The fram ework doesn’t do anyt hing special wit h t his. I t ’s your responsibilit y t o display it in
your t em plat e.
● content — The cont ent of t he flat page, i.e., t he HTML of t he page. The fram ework doesn’t do anyt hing special wit h t his. I t ’s
your responsibilit y t o display it in t he t em plat e.
● enable_comments — Whet her t o enable com m ent s on t his flat page. The fram ework doesn’t do anyt hing special wit h t his. You
can check t his value in your t em plat e and display a com m ent form if needed.
● template_name — The nam e of t he t em plat e t o use for rendering t his flat page. This is opt ional. I f it ’s not given, t he
fram ework will use t he t em plat e flatpages/default.html.
● registration_required — Whet her regist rat ion is required for viewing t his flat page. This int egrat es wit h Dj ango’s
aut hent icat ion/ user fram ework, which was explained in Chapt er 12.
● sites — The sit es t hat t his flat page lives on. This int egrat es wit h Dj ango’s sit es fram ework, which was explained in t he
“ Sit es” sect ion above.

You can creat e flat pages t hrough eit her t he Dj ango adm in int erface or t he Dj ango dat abase API . For m ore, see “ How t o add,
change and delet e flat pages” below.

Once you’ve creat ed flat pages, t he FlatpageFallbackMiddleware does all of t he work. Each t im e any Dj ango applicat ion raises a
404 error, t his m iddleware checks t he flat pages dat abase for t he request ed URL as a last resort . Specifically, it checks for a
flat page wit h t he given URL wit h a sit e I D t hat corresponds t o t he SITE_ID set t ing.

I f it finds a m at ch, it loads t he flat page’s t em plat e, or flatpages/default.html if t he flat page has not specified a cust om
t em plat e. I t passes t hat t em plat e a single cont ext variable, flatpage, which is t he flat page obj ect . I t uses RequestContext in
rendering t he t em plat e.

I f it doesn’t find a m at ch, t he request cont inues t o be processed as usual.

Not e t hat t his m iddleware only get s act ivat ed for 404s — not for 500s or responses of any ot her st at us code. Also not e t hat t he
order of MIDDLEWARE_CLASSES m at t ers. Generally, you can put FlatpageFallbackMiddleware at t he end of t he list , because it ’s a
last resort .

How to add, change and delete flatpages

Via the admin interface


I f you’ve act ivat ed t he aut om at ic Dj ango adm in int erface, you should see a “ Flat pages” sect ion on t he adm in index page. Edit
flat pages as you edit any ot her obj ect in t he syst em .

Via the Python API


As described above, flat pages are represent ed by a st andard Dj ango m odel t hat lives in django/contrib/flatpages/models.py.
Hence, you can access flat page obj ect s via t he Dj ango dat abase API . For exam ple:

>>> from django.contrib.flatpages.models import FlatPage


>>> from django.contrib.sites.models import Site
>>> fp = FlatPage(
... url='/about/',
... title='About',
... content='<p>About this site...</p>',
... enable_comments=False,
... template_name='',
... registration_required=False,
... )
>>> fp.save()
>>> fp.sites.add(Site.objects.get(id=1))
>>> FlatPage.objects.get(url='/about/')
<FlatPage: /about/ -- About>

file:///D|/books/computer/programming/python/books/DJANGO BOOK/15.html (6 of 9)9/28/2007 2:30:07 PM


Chapter 15: Other contributed sub-frameworks

Flatpage templates
By default , flat pages are rendered via t he t em plat e flatpages/default.html, but you can override t hat for a part icular flat page.

Creat ing t he flatpages/default.html t em plat e is your responsibilit y. I n your t em plat e direct ory, j ust creat e a flatpages
direct ory cont aining a file default.html.

Flat page t em plat es are passed a single cont ext variable, flatpage, which is t he flat page obj ect .

Here’s a sam ple flatpages/default.html t em plat e:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"


"http://www.w3.org/TR/REC-html40/loose.dtd">
<html>
<head>
<title>{{ flatpage.title }}</title>
</head>
<body>
{{ flatpage.content }}
</body>
</html>

Redirect s
Dj ango’s redirect s fram ework let s you m anage redirect s easily by st oring t hem in a dat abase and t reat ing t hem as any ot her
Dj ango m odel obj ect . For exam ple, you can use t he redirect s fram ework t o t ell Dj ango, “ redirect any request t o /music/
t o /sections/arts/music/.”

Using the redirects framework


To inst all t he redirect s app, follow t hese st eps:

1. Add 'django.contrib.redirects' t o your INSTALLED_APPS.


2. Add 'django.contrib.redirects.middleware.RedirectFallbackMiddleware' t o your MIDDLEWARE_CLASSES set t ing.
3. Run t he com m and manage.py syncdb t o inst all t he single required t able int o your dat abase.

How it works
manage.py syncdb creat es a django_redirect t able in your dat abase. This is a sim ple lookup t able wit h site_id, old_path
and new_path fields.

You can creat e redirect s t hrough eit her t he Dj ango adm in int erface or t he Dj ango dat abase API . For m ore, see “ How t o add,
change and delet e redirect s” below.

Once you’ve creat ed redirect s, t he RedirectFallbackMiddleware does all of t he work. Each t im e any Dj ango applicat ion raises a
404 error, t his m iddleware checks t he redirect s dat abase for t he request ed URL as a last resort . Specifically, it checks for a
redirect wit h t he given old_path wit h a sit e I D t hat corresponds t o t he SITE_ID set t ing. ( See “ Sit es” above for m ore on SITE_ID
and t he sit es fram ework.) Then, it follows t hese st eps:

● I f it finds a m at ch, and new_path is not em pt y, it redirect s t o new_path.


● I f it finds a m at ch, and new_path is em pt y, it sends a 410 ( “ Gone” ) HTTP header and em pt y ( cont ent - less) response.
● I f it doesn’t find a m at ch, t he request cont inues t o be processed as usual.

The m iddleware only get s act ivat ed for 404s — not for 500s or responses of any ot her st at us code.

Not e t hat t he order of MIDDLEWARE_CLASSES m at t ers. Generally, you can put RedirectFallbackMiddleware at t he end of t he list ,
because it ’s a last resort .

How to add, change and delete redirects

Via the admin interface


I f you’ve act ivat ed t he aut om at ic Dj ango adm in int erface, you should see a “ Redirect s” sect ion on t he adm in index page. Edit
redirect s as you edit any ot her obj ect in t he syst em .

Via the Python API


Redirect s are represent ed by a st andard Dj ango m odel t hat lives in django/contrib/redirects/models.py. Hence, you can
access redirect obj ect s via t he Dj ango dat abase API . For exam ple:

>>> from django.contrib.redirects.models import Redirect

file:///D|/books/computer/programming/python/books/DJANGO BOOK/15.html (7 of 9)9/28/2007 2:30:07 PM


Chapter 15: Other contributed sub-frameworks

>>> from django.contrib.sites.models import Site


>>> red = Redirect(
... site=Site.objects.get(id=1),
... old_path='/music/',
... new_path='/sections/arts/music/',
... )
>>> red.save()
>>> Redirect.objects.get(old_path='/music/')
<Redirect: /music/ ---> /sections/arts/music/>

CSRF prot ect ion


The django.contrib.csrf package provides easy- t o- use prot ect ion against Cross- Sit e Request Forgeries ( CSRF) .

CSRF explained
CSRF, also known as “ session riding,” is a Web- sit e securit y exploit . I t happens when a m alicious Web sit e t ricks a user int o
unknowingly loading a URL from a sit e at which t hey’re already aut hent icat ed — hence, t aking advant age of t heir aut hent icat ed
st at us. This can be a bit t ricky t o underst and at first , so we’ve included t wo exam ples here:

A simple example
Say you’re logged int o a webm ail account at example.com. Say t his webm ail sit e has a “ Log out ” but t on t hat point s t o t he
URL example.com/logout — t hat is, t he only act ion you need t o t ake in order t o log out is t o visit t he page example.com/logout.

A m alicious sit e can coerce you t o visit t he URL example.com/logout by including t hat URL as a hidden <iframe> on it s own
( m alicious) page. Thus, if you’re logged int o t he example.com webm ail account and visit t he m alicious page t hat has an <iframe>
t o example.com/logout, t he act of visit ing t he m alicious page will log you out from example.com.

Clearly, being logged out of a webm ail sit e against your will is not a t errifying breach of securit y, but t his sam e t ype of exploit can
happen t o any sit e t hat “ t rust s” users — such as bank sit es or e- com m erce sit es.

A more complex example


I n previous exam ple, example.com was part ially at fault because it allowed a st at e change ( i.e., logging yourself out ) t o be
request ed via t he HTTP GET m et hod. I t ’s m uch bet t er pract ice t o require an HTTP POST for any request t hat changes st at e on t he
server. But even Web sit es t hat require POST for st at e- changing act ions are vulnerable t o CSRF.

Say example.com has upgraded it s “ Log out ” funct ionalit y so t hat it ’s a <form> but t on t hat is request ed via POST t o t he
URL example.com/logout. Furt herm ore, t he log- out <form> includes t his hidden field:

<input type="hidden" name="confirm" value="true" />

This ensures t hat a sim ple POST t o t he URL example.com/logout won’t perform t he logging out ; in order for a user t o log out , t he
user m ust request example.com/logout via POST and send t he confirm POST variable wit h a value of 'true'.

Well, despit e t he ext ra securit y, t his arrangem ent can st ill be exploit ed by CSRF; t he m alicious page j ust needs t o do a lit t le m ore
work. I nst ead of loading t he example.com/logout page in an <iframe>, it can call t hat URL via POST using JavaScript , passing
t he confirm=true variable.

Prevention
How, t hen, can your sit e prot ect it self from t his exploit ?

The first st ep is t o m ake sure all GET request s are free of side effect s. That way, if a m alicious sit e includes one of your pages as
an <iframe>, it won’t have a negat ive effect .

That leaves POST request s. The second st ep, t hen, is t o give each POST <form> a hidden field whose value is secret and is
generat ed from t he user’s session I D. Then, when processing t he form on t he server side, check for t hat secret field and raise an
error if it doesn’t validat e.

This is exact ly what Dj ango’s CSRF prevent ion layer does.

Using the CSRF middleware


The django.csrf package cont ains only one m odule: middleware.py. This m odule cont ains a Dj ango m iddleware
class, CsrfMiddleware, which im plem ent s t he CSRF prot ect ion.

To use it , add 'django.contrib.csrf.middleware.CsrfMiddleware' t o t he MIDDLEWARE_CLASSES set t ing in your set t ings file.
This m iddleware needs t o process t he response aft er SessionMiddleware, so CsrfMiddleware m ust appear
before SessionMiddleware in t he list . Also, it m ust process t he response before t he response get s com pressed or ot herwise
m angled, so CsrfMiddleware m ust com e aft er GZipMiddleware.

file:///D|/books/computer/programming/python/books/DJANGO BOOK/15.html (8 of 9)9/28/2007 2:30:07 PM


Chapter 15: Other contributed sub-frameworks

Once you’ve added t hat t o your MIDDLEWARE_CLASSES set t ing, you’re done. That ’s all you need t o do.

How it works
I n case you’re int erest ed, here’s how CsrfMiddleware works. I t does t hese t wo t hings:

1. I t m odifies out going request s by adding a hidden form field t o all POST form s, wit h t he nam e csrfmiddlewaretoken and a
value t hat is a hash of t he session I D plus a secret . The m iddleware does not m odify t he response if t here’s no session I D
set , so t he perform ance penalt y is negligible for request s t hat don’t use sessions.
2. On all incom ing POST request s t hat have t he session cookie set , it checks t hat t he csrfmiddlewaretoken is present and
correct . I f it isn’t , t he user will get a 403 HTTP error. The cont ent s of t he 403 error page are t he m essage “ Cross Sit e
Request Forgery det ect ed. Request abort ed.”
This ensures t hat only form s originat ing from your Web sit e can be used t o POST dat a back.

This m iddleware deliberat ely only t arget s HTTP POST request s ( and t he corresponding POST form s) . As we explained above, GET
request s ought never t o have side effect s; ensuring t his is your own responsibilit y.

POST request s t hat are not accom panied by a session cookie are not prot ect ed, but t hey don’t need t o be prot ect ed, because a
m alicious Web sit e could m ake t hese kind of request s anyway.

To avoid alt ering non- t ext ual request s, t he m iddleware checks t he response’s Content-Type header before m odifying it . Only
pages t hat are served as text/html or application/xml+xhtml are m odified.

Limitations
CsrfMiddleware requires Dj ango’s session fram ework t o work. ( See Chapt er 12 for m ore on sessions.) I f you’ve using a cust om
session or aut hent icat ion fram ework t hat m anually m anages session cookies, t his m iddleware will not help you.

I f your app creat es HTML pages and form s in som e unusual way — e.g., if it sends fragm ent s of HTML in
JavaScript document.write st at em ent s — you m ight bypass t he filt er t hat adds t he hidden field t o t he form . I n t his case, t he
form subm ission would always fail. ( This would happen because t he CsrfMiddleware uses a regular expression t o add
t he csrfmiddlewaretoken field t o your HTML before t he page is sent t o t he client , and t he regular expression som et im es cannot
handle wacky HTML.) I f you suspect t his m ight be happening, j ust view source in your Web browser t o see whet her
t he csrfmiddlewaretoken was insert ed int o your <form>.

For m ore CSRF inform at ion and exam ples, visit ht t p: / / en.wikipedia.org/ wiki/ Csrf

Cont ent t ypes


This sect ion hasn’t been writ t en yet .

Form t ools
This sect ion hasn’t been writ t en yet .

Humanizing dat a
This sect ion hasn’t been writ t en yet .

Markup f ilt ers


This sect ion hasn’t been writ t en yet .

Syndicat ion f eeds


This sect ion hasn’t been writ t en yet .

« previous ◊ t able of cont ent s ◊ next »


Copyright 2006 Adrian Holovat y and Jacob Kaplan- Moss.
This work is licensed under t he GNU Free Docum ent License.

file:///D|/books/computer/programming/python/books/DJANGO BOOK/15.html (9 of 9)9/28/2007 2:30:07 PM


Chapter 16: Middleware

The Dj ango Book


« previous ◊ t able of cont ent s ◊ next »

Chapt er 16: Middleware


On occasion, you’ll need t o run a piece of code on each and every request t hat Dj ango handles. This code m ight need t o m odify
t he request before t he view handles it , or m aybe log inform at ion about t he request for debugging purposes, et c.

Dj ango’s m iddle w a r e fram ework is essent ially a set of hooks int o Dj ango’s request / response processing. I t ’s a light , low- level
“ plugin” syst em for globally alt ering Dj ango’s input and/ or out put .

Each m iddleware com ponent is responsible for doing som e specific funct ion. I f you’re reading t his book linearly — sorry,
post m odernist s — you’ll have already seen m iddleware a num ber of t im es:

● All of t he nift y session and user t ools t hat we looked at in Chapt er 12 are m ade possible by a few sm all pieces of m iddleware
( m ore specifically, t he m iddleware m akes request.session and request.user available t o you in views) .
● The sit e- wide cache discussed in Chapt er 12 is act ually j ust a piece of m iddleware t hat short - circuit s t he call t o your view
funct ion if t he response for t hat view has already been cached.
● The flatpages, redirects and csrf cont ribut ed apps from Chapt er 15 all do t heir m agic t hrough t he use of m iddleware
com ponent s

This chapt ers dives deeper int o exact ly what m iddleware is and how it works, and explains how you can writ e your own
m iddleware.

What ’ s middleware?
Middleware is act ually incredible sim ple. A m iddleware com ponent is sim ply a Pyt hon class t hat conform s t o a cert ain API — duck
t yping st rikes again! Before diving int o t he form al aspect s of what t hat API is, let ’s look at a very sim ple exam ple.

High- t raffic sit es oft en need t o deploy Dj ango behind a load balancing proxy ( see Chapt er 21) . This can cause a few sm all
com plicat ions, one of which is t hat every request ’s rem ot e I P ( request.META["REMOTE_IP"]) will be t hat of t he load balancer, not
t he act ual I P m aking t he request . Load balancers deal wit h t his by set t ing a special header, X-Forwarded-For, t o t he act ual
request ing I P address.

So here’s a sm all bit of m iddleware t hat let s sit es running behind a proxy st ill see t he correct I P address
in request.META["REMOTE_IP"]:

class SetRemoteAddrFromForwardedFor(object):

def process_request(self, request):


try:
real_ip = request.META['HTTP_X_FORWARDED_FOR']
except KeyError:
pass
else:
# HTTP_X_FORWARDED_FOR can be a comma-separated list of IPs.
# Take just the first one.
real_ip = real_ip.split(",")[0]
request.META['REMOTE_ADDR'] = real_ip

I f t his is inst alled ( see below) , every request ’s X-Forwarded-For value will be aut om at ically insert ed
int o request.META['REMOTE_ADDR']. Sim ple, isn’t it ?

I n fact , t his is a com m on enough need t hat t his piece of m iddleware is a built - in part of Dj ango; it lives
in django.middleware.http, and you can read a bit m ore about it below.

Inst alling middleware


The linear readers in t he crowd are probably old hands at t his already; m any of t he exam ples in t he previous few chapt ers will
only work if you’ve already figured out how t o enable m iddleware. However, for com plet eness — and for t he benefit of Julio
Cort ázar fans who’ve t orn all t he pages out of t his book, shuffled t hem , and are now reading t hem in random order — let ’s break
it down.

To act ivat e a m iddleware com ponent , add it t o t he MIDDLEWARE_CLASSES list in your set t ings m odule. I n MIDDLEWARE_CLASSES,
each m iddleware com ponent is represent ed by a st ring: t he full Pyt hon pat h t o t he m iddleware’s class nam e. For exam ple, here’s
t he default MIDDLEWARE_CLASSES creat ed by django-admin.py startproject:

MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',

file:///D|/books/computer/programming/python/books/DJANGO BOOK/16.html (1 of 4)9/28/2007 2:30:22 PM


Chapter 16: Middleware

'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.middleware.doc.XViewMiddleware',
)

A Dj ango inst allat ion doesn’t require any m iddleware — e.g., MIDDLEWARE_CLASSES can be em pt y, if you’d like — but it ’s st rongly
suggest ed t hat you use CommonMiddleware.

The order is significant . On t he request and view phases, Dj ango applies m iddleware in t he order given in MIDDLEWARE_CLASSES,
and on t he response and except ion phases, Dj ango applies m iddleware in reverse order. That is, Dj ango
t reat s MIDDLEWARE_CLASSES as a sort of “ wrapper” around t he view funct ion: on t he request , it walks down t he list t o t he view,
and on t he response it walks back up.

Middleware met hods


Now t hat we know what m iddleware is and how t o inst all it , let ’s t ake a look at all t he available m et hods t hat m iddleware classes
m ay define.

Initializer: __init__(self)
I f m iddleware classes define an init ializer ( i.e. an __init__ m et hod) , it should t ake no argum ent s ( beyond t he st andard self) .

For perform ance reasons, m iddleware classes are only inst ant iat ed on ce in long- running server processes; t his m eans t hat you
can’t count on __init__ get t ing called every t im e a request runs, only once at server st art up.

Middleware classes m ay also use init ializat ion t im e t o rem ove t hem selves from being inst alled. I f an init ializer
raises django.exceptions.MiddlewareNotUsed, Dj ango will rem ove t hat piece of m iddleware from t he m iddleware st ack. You
m ight use t his t o check for som e piece of soft ware t hat t he m iddleware class depends on, or whet her t he server is running in
debug m ode, or any ot her sort of environm ent al sit uat ion t hat m ight m ake you want t o disable t he m iddleware.

Request pre-processor: process_request(self, request)


This m et hod get s called as soon as t he request as been received, and before t he URL has been resolved t o det erm ine which view
t o run. I t ’s passed t he HttpRequest obj ect , which you m ay m odify at will.

process_request() should ret urn eit her None or an HttpResponse obj ect . I f it ret urns None, Dj ango will cont inue processing t his
request , execut ing any ot her m iddleware and t hen t he appropriat e view.

I f a request m iddleware ret urns an HttpResponse obj ect , Dj ango won’t bot her calling any ot her m iddleware ( of any t ype) or t he
appropriat e view; it ’ll ret urn t hat HttpResponse.

View pre-processor: process_view(self, request, view, args, kwargs)


This m et hod get s called aft er t he request m iddleware has run, and aft er t he URL has been resolved int o a view, but before t hat
view has act ually been called.

The argum ent s passed t o t his view are:

Ar gu m e n t Ex pla n a t ion
request The HttpRequest obj ect .
view The Pyt hon funct ion t hat Dj ango will call t o handle t his request . This is t he act ual funct ion obj ect
it self, not t he nam e of t he funct ion as a st ring.
args The list of posit ional argum ent s t hat will be passed t o t he view, not including t he request
argum ent ( which is always t he first argum ent t o a view) .
kwargs The dict ionary of keyword argum ent s t hat will be passed t o t he view.

Just like process_request(), process_view() should ret urn eit her None or an HttpResponse obj ect . I f it ret urns None, Dj ango
will cont inue processing t his request , execut ing any ot her view m iddleware and t hen t he appropriat e view.

I f any view m iddleware ret urns an HttpResponse obj ect , Dj ango won’t bot her calling any ot her m iddleware or t he appropriat e
view; it ’ll ret urn t hat response.

Response post-processor: process_response(self, request, response)


This m et hod get s called aft er t he view funct ion has already been called and t he response has been generat ion. This is where
m iddleware can m odify t he out put of a response; out put com pression ( see below) is one obvious use for response m iddleware.

The param et ers should be pret t y self- explanat ory — request is t he request obj ect , and response is t he response obj ect ret urned
from t he view.

Unlike t he request and view m iddleware m et hods which m ay ret urn None, process_response() m ust ret urn an HttpResponse
obj ect . That response could be t he original one passed int o t he funct ion ( possibly m odified) , or a brand new one.

file:///D|/books/computer/programming/python/books/DJANGO BOOK/16.html (2 of 4)9/28/2007 2:30:22 PM


Chapter 16: Middleware

Exception post-processor: process_exception(self, request, exception)


This m et hod only get s called if som et hing goes wrong and a view raises an uncaught except ion, not including Http404 except ions.
You can use t his hook t o send error not ificat ions, dum p post - m ort em inform at ion t o a log, or even t ry t o recover from t he error
aut om at ically.

The param et ers t o t his funct ion are t he sam e request obj ect we’ve been dealing wit h all along, and exception, which is t he
act ual Exception obj ect raised by t he view funct ion.

process_exception() m ay ret urn an HttpResponse which will be used as t he response shown t o t he browser, or it m ay
ret urn None t o cont inue wit h Dj ango’s built - in except ion handling.

Examples
Dj ango ships wit h a num ber of m iddleware classes — discussed below — t hat m ake good exam ples; reading t he code for t hem
should give you a good feel for t he power of m iddleware.

You can also find a num ber of com m unit y- cont ribut ed exam ples on Dj ango’s wiki: ht t p: / / code.dj angoproj ect .com / wiki/
Cont ribut edMiddleware.

Built -in middleware


Dj ango com es wit h som e built - in m iddleware t o deal wit h com m on problem s.

Authentication support middleware


Middleware class: django.contrib.auth.middleware.AuthenticationMiddleware

Enables aut hent icat ion support . Technically, t his m iddleware adds t he request.user at t ribut e, represent ing t he current ly- logged-
in user, t o every incom ing HttpRequest obj ect .

See Chapt er 15 for t he com plet e det ails.

“ Common” middleware
Middleware class: django.middleware.common.CommonMiddleware.

Adds a few conveniences for perfect ionist s:

● Forbids access t o user agent s in t he DISALLOWED_USER_AGENTS set t ing, which should be a list of st rings.

● Perform s URL rewrit ing based on t he APPEND_SLASH and PREPEND_WWW set t ings. I f APPEND_SLASH is True, URLs t hat lack a
t railing slash will be redirect ed t o t he sam e URL wit h a t railing slash, unless t he last com ponent in t he pat h cont ains a
period. So foo.com/bar is redirect ed t o foo.com/bar/, but foo.com/bar/file.txt is passed t hrough unchanged.

I f PREPEND_WWW is True, URLs t hat lack a leading “ www.” will be redirect ed t o t he sam e URL wit h a leading “ www.”

Bot h of t hese opt ions are m eant t o norm alize URLs. The philosophy is t hat each URL should exist in one, and only one, place.
Technically a URL foo.com/bar is dist inct from foo.com/bar/ — a search- engine indexer would t reat t hem as separat e URLs
— so it ’s best pract ice t o norm alize URLs.

● Handles ETags based on t he USE_ETAGS set t ing. I f USE_ETAGS is set t o True, Dj ango will calculat e an ETag for each request
by MD5- hashing t he page cont ent , and it ’ll t ake care of sending Not Modified responses, if appropriat e.

Compression middleware
Middleware class: django.middleware.gzip.GZipMiddleware

I f enabled, t his m iddleware will aut om at ically com press cont ent for browsers t hat underst and gzip com pression ( all m odern
browsers) .

This can great ly reduce t he am ount of bandwidt h a web server consum es at t he expense of processing t im e. We usually prefer
speed over bandwidt h, but if you’d like t o t ake t he opposit e side of t his t rade- off, j ust enable t his m iddleware.

Conditional GET middleware


Middleware class: django.middleware.http.ConditionalGetMiddleware

I f enabled, provides support for condit ional GET operat ions. I f t he response has a ETag or Last-Modified header, and t he request
has If-None-Match or If-Modified-Since, t he response is replaced by an 304 ( “ Not m odified” ) response.

Also rem oves t he cont ent from any response t o a HEAD request and set s t he Date and Content-Length response- headers for all

file:///D|/books/computer/programming/python/books/DJANGO BOOK/16.html (3 of 4)9/28/2007 2:30:22 PM


Chapter 16: Middleware

request s.

Reverse proxy support ( X-Forwarded-For middleware)


Middleware class: django.middleware.http.SetRemoteAddrFromForwardedFor

This is t he exam ple we looked at above. I t set s request.META['REMOTE_ADDR'] based


on request.META['HTTP_X_FORWARDED_FOR'], if t he lat t er is set . This is useful if you’re sit t ing behind a reverse proxy t hat causes
each request ’s REMOTE_ADDR t o be set t o 127.0.0.1.

D a n ge r , W ill Robin son !


This does n ot validat e HTTP_X_FORWARDED_FOR.

I f you’re not behind a reverse proxy t hat set s HTTP_X_FORWARDED_FOR aut om at ically, do not use t his m iddleware.
Anybody can spoof t he value of HTTP_X_FORWARDED_FOR, and because t his set s REMOTE_ADDR based
on HTTP_X_FORWARDED_FOR, t hat m eans anybody can fake t heir I P address.

Only use t his m iddlware when you can absolut ely t rust t he value of HTTP_X_FORWARDED_FOR.

Session support middleware


Middleware class: django.contrib.sessions.middleware.SessionMiddleware.

Enables session support ; see Chapt er 15 for det ails.

Site-wide cache middleware


Middleware class: django.middleware.cache.CacheMiddleware.

I f t his is enabled, each Dj ango- powered page will be cached. This is discussed in det ail in Chapt er 14.

Transaction middleware
Middleware class: django.middleware.transaction.TransactionMiddleware

Binds a dat abase COMMIT or ROLLBACK t o t he request / response phase. I f a view funct ion runs successfully, a COMMIT is done. I f it
fails wit h an except ion, a ROLLBACK is done.

The order of t his m iddleware in t he st ack is im port ant : m iddleware m odules running out side of it run wit h com m it - on- save - t he
default Dj ango behavior. Middleware m odules running inside it ( com ing lat er in t he st ack) will be under t he sam e t ransact ion
cont rol as t he view funct ions.

See XXX for m ore about inform at ion about dat abase t ransact ions.

“ X-View” middleware
Middleware class: django.middleware.doc.XViewMiddleware

Sends cust om X-View HTTP headers t o HEAD request s t hat com e from I P addresses defined in t he INTERNAL_IPS set t ing. This is
used by Dj ango’s aut om at ic docum ent at ion syst em .

« previous ◊ t able of cont ent s ◊ next »


Copyright 2006 Adrian Holovat y and Jacob Kaplan- Moss.
This work is licensed under t he GNU Free Docum ent License.

file:///D|/books/computer/programming/python/books/DJANGO BOOK/16.html (4 of 4)9/28/2007 2:30:22 PM


Chapter 17: Integrating with legacy databases and applications

The Dj ango Book


« previous ◊ t able of cont ent s ◊ next »

Chapt er 17: Int egrat ing wit h legacy dat abases and applicat ions
Alt hough Dj ango is best suit ed for developing proj ect s from scrat ch — so- called “ green- field” developm ent — it ’s possible t o
int egrat e t he fram ework int o legacy dat abases and applicat ions. This chapt er explains a few int egrat ion st rat egies.

Int egrat ing wit h a legacy dat abase


Dj ango’s dat abase layer generat es SQL schem as from Pyt hon code — but in t he case of a legacy dat abase, you already have t he
SQL schem as. I n t hat case, you’ll need t o writ e m odels for your exist ing dat abase t ables. ( For perform ance reasons, Dj ango’s
dat abase layer does not support on- t he- fly obj ect - relat ional m apping by int rospect ing t he dat abase at run t im e; in order t o use
t he dat abase API , you’re required t o writ e m odel code.) Fort unat ely, Dj ango com es wit h a ut ilit y t hat can generat e m odel code by
reading your dat abase t able layout s. This ut ilit y is called manage.py inspectdb.

Using inspectdb
The inspectdb ut ilit y int rospect s t he dat abase point ed t o by your set t ings file, det erm ines t he Dj ango m odel represent at ion of
your t ables and print s t he Pyt hon m odel code t o st andard out put . Here’s a walkt hrough of a t ypical legacy- dat abase process from
scrat ch; t he only t hings it assum es are t hat Dj ango is inst alled and t hat you have a legacy dat abase.

1. Creat e a Dj ango proj ect by running django-admin.py startproject mysite ( where mysite is your proj ect ’s nam e) . We’ll
use mysite as t he proj ect nam e in t his exam ple.

2. Edit t he set t ings file in t hat proj ect , mysite/settings.py, t o t ell Dj ango what your dat abase connect ion param et ers are,
and what t he nam e of t he dat abase is. Specifically, you’ll want t o specify t he DATABASE_NAME, DATABASE_ENGINE
, DATABASE_USER, DATABASE_PASSWORD, DATABASE_HOST and DATABASE_PORT set t ings.

3. Creat e a Dj ango app wit hin your proj ect by running python mysite/manage.py startapp myapp ( where myapp is your app’s
nam e) . We’ll use myapp as t he proj ect nam e here.

4. Run t he com m and python mysite/manage.py inspectdb. This will exam ine t he t ables in t he DATABASE_NAME dat abase and
print t he m odel class for each t able. Take a look at t he out put t o get an idea of what inspectdb can do.

5. Save t hat out put t o t he models.py file wit hin your app by using st andard shell out put redirect ion:

python mysite/manage.py inspectdb > mysite/myapp/models.py

6. Edit t he mysite/myapp/models.py file t o clean up t he generat ed m odels and m ake what ever cust om izat ions you need t o
m ake. We’ll give som e hint s for t his in t he next sect ion.

Cleaning up generated models


As you m ight expect , t he dat abase int rospect ion isn’t perfect , and you’ll need t o do som e light cleanup of t he result ing m odel
code. Here are a few point ers for dealing wit h t he generat ed m odels:

1. Each dat abase t able is convert ed t o a m odel class — i.e., t here is a one- t o- one m apping bet ween dat abase t ables and m odel
classes. This m eans t hat you’ll need t o refact or t he m odels for any m any- t o- m any j oin t ables int o ManyToManyField obj ect s.

2. Each generat ed m odel has an at t ribut e for every field — including id prim ary- key fields. However, recall t hat Dj ango
aut om at ically adds an id prim ary- key field if a m odel doesn’t have a prim ary key. Thus, if you’re part icularly anal, you’ll
want t o rem ove any lines t hat look like t his, because t hey’re redundant :

id = models.IntegerField(primary_key=True)

3. Each field’s t ype ( e.g., CharField, DateField) is det erm ined by looking at t he dat abase colum n t ype ( e.g., VARCHAR, DATE) .
I f inspectdb cannot m ap a colum n’s t ype t o a m odel field t ype, it will use TextField and will insert t he Pyt hon
com m ent 'This field type is a guess.' next t o t he field in t he generat ed m odel. Keep an eye out for t hat , and change
t he field t ype accordingly if needed.

4. I f a dat abase colum n nam e is a Pyt hon reserved word ( such as pass, class or for) , inspectdb will append '_field' t o t he
at t ribut e nam e. For exam ple, if a t able has a colum n for, t he generat ed m odel will have a field for_field, wit h
t he db_column at t ribut e set t o 'for'. inspectdb will insert t he Pyt hon
com m ent 'Field renamed because it was a Python reserved word.' next t o t he field.

5. I f your dat abase cont ains t ables t hat refer t o ot her t ables ( as m ost dat abases do) , you m ight need t o rearrange t he order of
t he generat ed m odels so t hat m odels t hat refer t o ot her m odels are ordered properly. For exam ple, if m odel Foo has
a ForeignKey t o m odel Bar, m odel Bar should be defined before m odel Foo.

file:///D|/books/computer/programming/python/books/DJANGO BOOK/17.html (1 of 2)9/28/2007 2:31:09 PM


Chapter 17: Integrating with legacy databases and applications

6. inspectdb det ect s prim ary keys for Post greSQL, MySQL and SQLit e. That is, it insert s primary_key=True where appropriat e.
For ot her dat abases, you’ll need t o insert primary_key=True for at least one field in each m odel, because Dj ango m odels are
required t o have a primary_key=True field.

7. Foreign- key det ect ion only works wit h Post greSQL and wit h cert ain t ypes of MySQL t ables. I n ot her cases, foreign- key fields
will be generat ed as IntegerField``s, assuming the foreign-key column was an ``INT colum n.

More
What else would you like t o see in t his chapt er? What problem s/ quest ions do you have wit h int egrat ing Dj ango wit h legacy
dat abases/ applicat ions? Leave a com m ent on t his paragraph and let us know.

« previous ◊ t able of cont ent s ◊ next »


Copyright 2006 Adrian Holovat y and Jacob Kaplan- Moss.
This work is licensed under t he GNU Free Docum ent License.

file:///D|/books/computer/programming/python/books/DJANGO BOOK/17.html (2 of 2)9/28/2007 2:31:09 PM


Chapter 18: Customizing the Django admin

The Dj ango Book


« previous ◊ t able of cont ent s ◊ next »

Chapt er 18: Ext ending Dj ango’ s admin int erf ace


Chapt er 6 int roduced Dj ango’s adm in int erface, and now it ’s t im e t o circle back and t ake a closer look.

As we’ve said a few t im es before, t he adm in is one of Dj ango’s “ killer feat ures,” and m ost Dj ango developers quickly fall in love wit h all
it s t im esaving feat ures. I t follows nat urally, t hen, t hat event ually m ost Dj ango developers look t o cust om ize or ext end t he adm in.

The last few sect ions of Chapt er 6 t alk about som e sim ple ways t o cust om ize cert ain part s of t he adm in int erface. I t ’s probably a good
idea t o go read back over t hat m at erial; it covers som e sim ple ways t o cust om ize t he adm in change list s and edit form s, as well as an
easy way t o “ re- brand” t he adm in t o m at ch your sit e.

Chapt er 6 also discusses when and why you’d want t o use t he adm in int erface, and since t hat m at erial m akes a good j um ping- off point
for t he rest of t his chapt er, we’ll reproduce it here:

Obviously, [ t he admin is] ext remely usef ul f or edit ing dat a (f ancy t hat ). If you have any sort of dat a ent ry t asks, t he
admin simply can’ t be beat . We suspect t hat t he vast maj orit y of readers of t his book will have a whole host of dat a
ent ry t asks.

Dj ango’ s admin especially shines when non-t echnical users need t o be able t o ent er dat a; t hat ’ s t he original genesis of
t he f eat ure. At t he newspaper where Dj ango was f irst developed, development of a t ypical online f eat ure — a special
report on wat er qualit y in t he municipal supply, say — goes somet hing like t his:

● The report er responsible f or t he st ory meet s wit h one of t he developers and goes over t he available dat a.

● The developer designs a model around t his dat a, and t hen opens up t he admin int erf ace t o t he report er.

● While t he report er ent ers dat a int o Dj ango, t he programmer can f ocus on developing t he publicly-accessible
int erf ace (t he f un part ! )

In ot her works, t he raison d’ êt re of Dj ango’ s admin is f acilit at ing t he simult aneous work of cont ent producers and
programmers.

However, beyond t he obvious dat a-ent ry t asks, we f ind t he admin usef ul in a f ew ot her cases:

file:///D|/books/computer/programming/python/books/DJANGO BOOK/18.html (1 of 10)9/28/2007 2:31:28 PM


Chapter 18: Customizing the Django admin

● Inspect ing dat a models: t he f irst t hing we do when we’ ve def ined a new model is t o call it up in t he admin and
ent er some dummy dat a. This is usually when we f ind any dat a modeling errors; having a graphical int erf ace t o a
model quickly reveals t hose mist akes.
● Managing acquired dat a: t here’ s lit t le act ual dat a ent ry associat ed wit h a sit e like chicagocrime. org since most of
t he dat a comes f rom an aut omat ed source. However, when problems wit h t he aut omat ically acquired dat a crop
up, it ’ s very usef ul t o be able t o go in and edit t hat dat a easily.

Dj ango’s adm in handles t hese com m on cases wit h lit t le or no cust om izat ion. As wit h m ost design t rade- offs, t hough, handling t hese
com m on cases so well m eans t hat Dj ango’s adm in doesn’t handle som e ot her m odes of edit ing very well at all.

We’ll t alk about t he cases t hat Dj ango’s adm in isn’t designed t o cover a bit lat er on, but first , a brief digression on philosophy:

The Zen of Admin


At it ’s core, Dj ango’s adm in is designed for a single act ivit y:

Trust ed users edit ing st ruct ured cont ent .

Yes, ext rem ely sim ple — but in t hat sim plicit y lies a whole host of supposit ions t hat t he adm in t akes as given. The ent ire philosophy of
Dj ango’s adm in follows direct ly from t hese assum pt ions, so let ’s dig int o t he subt ext of t his phrase:

“ Trusted users …”
The adm in is designed t o be used by people who you, t he developer, t r u st . This doesn’t j ust m ean “ people who have been
aut hent icat ed; ” it m eans t hat Dj ango assum es t hat your cont ent edit ors can be t rust ed t o do t he right t hing.

This m eans t hat t here’s no “ approval” process for edit ing cont ent — if you t rust your users, nobody needs t o approve of t heir edit s. I t
also m eans t hat t he perm ission syst em , while powerful, has no support for lim it ing access on a per- obj ect basis. I f you t rust som eone
t o edit t heir own st ories, you t rust t hem not t o edit anyone else’s wit hout perm ission.

“ . . editing …”
The prim ary purpose of Dj ango’s adm in is t o let people edit st uff. This seem s obvious at first , but again has som e subt le and powerful
repercussions.

For inst ance, alt hough t he adm in is quit e useful for reviewing dat a ( see above) , it ’s not designed wit h t hat purpose as a goal: not e t he
lack of a “ can view” perm ission ( see Chapt er 12) . Dj ango assum es t hat if people are allowed t o view cont ent in t he adm in, t hey’re also
allowed t o edit it .

Anot her m ore im port ant not e is t he lack of anyt hing even rem ot ely approaching “ workflow.” I f som e given t asks requires a series of
st eps, t here’s no support for enforcing t hat t hey be done in any part icular order. Dj ango’s adm in focuses on e dit in g, not on act ivit ies
surrounding t hat edit ing. This avoidance of workflow also st em s from t he principle of t rust : t he adm in’s philosophy is t hat workflow is
a personnel issue, not one t o be im plem ent ed in code.

file:///D|/books/computer/programming/python/books/DJANGO BOOK/18.html (2 of 10)9/28/2007 2:31:28 PM


Chapter 18: Customizing the Django admin

Finally, not e t he lack of aggregat ion in t he adm in. That is, t here’s no support for displaying t ot als, averages, et c. Again, t he adm in is
for edit ing — it ’s expect ed t hat you’ll writ e cust om views for all t he rest .

“ … structured content”
As wit h t he rest of Dj ango, t he adm in want s you t o work wit h st ruct ured dat a. Thus, t he adm in only support s edit ing dat a st ored in
Dj ango m odels; for anyt hing else, you’ll need cust om views.

Full stop
I t should be clear by now t hat Dj ango’s adm in does not t ry t o be all t hings t o all people; inst ead we choose t o focus t ight ly on one
t hing, and do t hat t hing ext rem ely well.

When it com es t o ext ending Dj ango’s adm in, m uch of t hat sam e philosophy holds ( not e t hat “ ext ensibilit y” shows up nowhere in our
goals) . Because cust om Dj ango views can do anyt hing — and because t hey can easily be visually int egrat ed int o t he adm in ( see
below) — t he built - in opport unit ies for cust om izing t he adm in are som ewhat lim it ed by design.

Cust omizing admin t emplat es


Out of t he box, you’ve got a num ber of t ools for cust om izing t he built - in adm in t em plat es which we’ll go over below, but for t asks
beyond t hat — anyt hing requiring cust om workflow or granular perm issions, for exam ple — you’ll need t o read t he sect ion on cust om
adm in views at t he end of t his chapt er.

For now, t hough, let ’s look at som e quick ways of cust om izing t he appearance ( and, t o som e ext ent , behavior) of t he adm in. Chapt er
6 covers a few of t he m ost com m on t asks — “ re- branding” t he Dj ango adm in ( for t hose point y- haired bosses who hat e blue) and
providing a cust om adm in form .

Past t hat point , t he goal usually involves changing som e of t he t em plat es for a part icular it em . Each of t he adm in views — t he change
list s, edit form s, delet e confirm at ion pages, and hist ory views — has an associat ed t em plat e which can be overridden in a num ber of
ways.

First , you can override t he t em plat e globally. The adm in view looks for t em plat es using t he st andard t em plat e loading m echanism , so
if you creat e t em plat es in one of your t em plat e direct ories, Dj ango will load t hose inst ead of t he default adm in t em plat es bundles wit h
Dj ango.

These global t em plat es are:

Vie w Ba se t e m pla t e n a m e
Change list admin/change_list.html
Add/ edit form admin/change_form.html
Delet e confirm at ion admin/delete_confirmation.html
Obj ect hist ory admin/object_history.html

However, m ost of t he t im e you’ll want t o change t he t em plat e j ust for a single obj ect or app ( not globally) . Thus, each adm in view
looks for m odel- and app- specific t em plat es first . Those views look for t em plat es in t his order:

● admin/<app_label>/<object_name>/<template>.html

file:///D|/books/computer/programming/python/books/DJANGO BOOK/18.html (3 of 10)9/28/2007 2:31:28 PM


Chapter 18: Customizing the Django admin

● admin/<app_label>/<template>.html
● admin/<template>.html

For exam ple, t he add/ edit form view for a Book m odel in t he bookstore app ( i.e. t he exam ple from Chapt er 6) looks for t em plat es in
t his order:

● admin/bookstore/book/change_form.html
● admin/bookstore/change_form.html
● admin/change_form.html

Custom model templates


Most of t he t im e, you’ll usually want t o use t he first t em plat e t o creat e a m odel- specific t em plat e; t his is usually best done by
ext ending t he base t em plat e and adding inform at ion t o one of t he blocks defined in t hat t em plat e.

For exam ple, let ’s say we want ed t o add a lit t le bit of help t ext t o t he t op of t hat book page. Maybe som et hing like t his:

file:///D|/books/computer/programming/python/books/DJANGO BOOK/18.html (4 of 10)9/28/2007 2:31:28 PM


Chapter 18: Customizing the Django admin

This is pret t y easy t o do: sim ple creat e a t em plat e called adm in/ bookst ore/ book/ change_form .ht m l` , and insert t his code:

{% extends "admin/change_form.html" %}

file:///D|/books/computer/programming/python/books/DJANGO BOOK/18.html (5 of 10)9/28/2007 2:31:28 PM


Chapter 18: Customizing the Django admin

{% block form_top %}
<p>Insert meaningful help message here...</p>
{% endblock %}

All t hese t em plat es define a num ber of blocks you can override. As wit h m ost program s, t he best docum ent at ion is t he code, so we
encourage you t o look t hrough t he adm in t em plat es ( t hey like in django/contrib/admin/templates/) for t he m ost up- t o- dat e
inform at ion.

Custom JavaScript
A com m on use for t hese cust om m odel t em plat es involves adding cust om JavaScript t o adm in pages — perhaps t o im plem ent som e
special widget or client - side behavior.

Luckily, t hat couldn’t be easier. Each adm in t em plat e defines a {% block extrahead %} which you can use t o put ext ra cont ent in t o
t he <head> elem ent . For exam ple, if you want ed t o include j Query in one of your adm in hist ory, it ’s as sim ple as:

{% extends "admin/object_history.html" %}
{% block extrahead %}
<script src="http://media.example.com/javascript/jquery.js" type="text/javascript"></script>
<script type="text/javascript">
// code to actually use jQuery here...
</script>
{% endblock %}

( I ’m not sure why you’d need j Query on t he obj ect hist ory page, but of course t his exam ple applies t o any of t he adm in t em plat es.)

You can use t his t echnique t o include any sort of ext ra JavaScript widget s you m ight need.

Cust om admin views


At t his point , anyone looking t o add cust om behavior t o Dj ango’s adm in is probably st art ing t o get a bit frust rat ed. “ All you’ve t alked
about is how t o change t he t he adm in visually,” t hey’ll cry, “ but how do I change t he way t he adm in works?”

Well, cry no m ore, for here com es t he answer.

The first t hing t o underst and is t hat it ’s n ot m a gic. That is, not hing t he adm in does is “ special” in any way — t he adm in is j ust a set
of views ( t hey live in django.contrib.admin.views) t hat m anipulat e dat a j ust like any ot her view.

Sure, t here’s quit e a bit of code in t here; it has t o deal wit h all t he various opt ions, field t ypes, and set t ings t hat influence m odel
behavior. St ill, when you realize t hat t he adm in is j ust a set of views, adding cust om adm in views becom es easier t o underst and.

By way of exam ple, let ’s add a “ publisher report ” view t o our book app from Chapt er 6. We’ll build an adm in view t hat shows t he list of
books broken down by publisher — a pret t y t ypical exam ple of a cust om adm in “ report ” view you m ight need t o build.

file:///D|/books/computer/programming/python/books/DJANGO BOOK/18.html (6 of 10)9/28/2007 2:31:28 PM


Chapter 18: Customizing the Django admin

First , we’ll wire up a view in our URLconf. We need t o insert t his line:

(r'^admin/bookstore/report/$', 'bookstore.admin_views.report'),

before t he line including t he adm in views. A bare- bones URLconf m ight look like:

from django.conf.urls.defaults import *

urlpatterns = patterns('',
(r'^admin/bookstore/report/$', 'bookstore.admin_views.report'),
(r'^admin/', include('django.contrib.admin.urls')),
)

Why put t he cust om view before t he adm in inclusion? Well, recall t hat Dj ango processes URL pat t erns in order. Because t he adm in
URLs m at ch nearly anyt hing t hat falls under t he inclusion point , if we reverse t he order of t hose lines Dj ango will find a built - in adm in
view for t hat pat t ern, which of course won’t work. I n t his part icular case, it ’ll t ry t o load a change list for a Report m odel in
t he bookstore app, which doesn’t exist .

Now let ’s writ e our view. For t he sake of sim plicit y, we’ll j ust load all books int o t he cont ext and let t he t em plat e handle t he grouping
wit h t he {% regroup %} t ag. Creat e a file bookstore/admin_views.py wit h t his code:

from bookstore.models import Book


from django.template import RequestContext
from django.shortcuts import render_to_response
from django.contrib.admin.views.decorators import staff_member_required

@staff_member_required
def report(request):
return render_to_response(
"admin/bookstore/report.html",
{'book_list' : Book.objects.all()},
RequestContext(request, {}),
)

Because we left t he grouping up t o t he t em plat e, t his view is pret t y sim ple. However, t here are som e subt le bit s here wort h m aking
explicit :

● We use t he staff_member_required decorat or from django.contrib.admin.views.decorators. This is t he sim ilar t o


t he login_required decorat or discussed in Chapt er 12, but t his one also checks t hat t he given user is m arked as a “ st aff”
m em ber, and t hus is allowed access t o t he adm in.

This decorat or prot ect s all t he built - in adm in views, and t hus m akes t he aut hent icat ion logic for your view m at ch t he rest of t he
adm in.

● We render a t em plat e locat ed under admin/. While t his isn’t st rict ly required, it ’s considered good pract ice t o keep all your adm in
t em plat es grouped in an admin direct ory. We’ve also put t he t em plat e in a direct ory nam ed bookstore aft er our app — also a
best pract ice.
file:///D|/books/computer/programming/python/books/DJANGO BOOK/18.html (7 of 10)9/28/2007 2:31:28 PM
Chapter 18: Customizing the Django admin

● We use RequestContext as t he t hird param et er ( context_instance) t o render_to_response. This ensures t hat inform at ion
about t he current user is available t o t he t em plat e.

See Chapt er 10 for m ore about RequestContext.

Finally, we’ll m ake a t em plat e for t his view. We’ll ext end t he built - in adm in t em plat es t o m ake t his view visually appear t o be part of
t he adm in:

{% extends "admin/base_site.html" %}

{% block title %}List of books by publisher{% endblock %}

{% block content %}
<div id="content-main">
<h1>List of books by publisher:</h1>
{% regroup book_list|dictsort:"publisher.name" by publisher as books_by_publisher %}
{% for publisher in books_by_publisher %}
<h3>{{ publisher.grouper }}</h3>
<ul>
{% for book in publisher.list|dictsort:"title" %}
<li>{{ book }}</li>
{% endfor %}
</ul>
{% endfor %}
</div>
{% endblock %}

By ext ending admin/base_site.html we get t he look and feel of t he Dj ango adm in “ for free.” Here’s what t he end result looks like:

file:///D|/books/computer/programming/python/books/DJANGO BOOK/18.html (8 of 10)9/28/2007 2:31:28 PM


Chapter 18: Customizing the Django admin

Where do you want t o admin t oday?


You can use t his t echnique t o add anyt hing you can dream of t o t he adm in. Rem em ber t hat t hese so- called “ cust om adm in views” are
really j ust norm al Dj ango views; you can use all t he t echniques you learn in t he rest of t his book t o provide as com plex an adm in as
file:///D|/books/computer/programming/python/books/DJANGO BOOK/18.html (9 of 10)9/28/2007 2:31:28 PM
Chapter 18: Customizing the Django admin

you need.

We’ll close out t his chapt er wit h som e ideas for cust om adm in views:

Overriding built-in views


At t im es t he default adm in views j ust don’t cut it . You can easily swap in your own cust om view for any st age of t he adm in; j ust let
your URL shadow t he built - in adm in one.

For exam ple, we could replace t he built - in “ creat e” view for a book wit h a form t hat let s t he user sim ply ent er an I SBN We could t hen
look up t he book’s inform at ion from ht t p: / / isbn.nu/ and creat e t he obj ect aut om at ically.

The code for such a view is left as an exercise t o t he reader, but t he im port ant part is t his URLconf snippet :

(r'^admin/bookstore/book/add/$', 'bookstore.admin_views.add_by_isbn'),

I f t his bit com es before t he adm in URLs in your URLconf, t he add_by_isbn view will com plet ely replace t he st andard adm in view.

We could follow a sim ilar t act t o replace a delet e confirm at ion page, t he edit st age, or any ot her part of t he adm in.

Contribute!
This sect ion is not yet com plet e. Are t here ot her t ypes of cust om adm in views you’d like covered? Leave a com m ent on t his paragraph
and let us know!

« previous ◊ t able of cont ent s ◊ next »


Copyright 2006 Adrian Holovat y and Jacob Kaplan- Moss.
This work is licensed under t he GNU Free Docum ent License.

file:///D|/books/computer/programming/python/books/DJANGO BOOK/18.html (10 of 10)9/28/2007 2:31:28 PM


Chapter 19: Internationalization and localization

The Dj ango Book


« previous ◊ t able of cont ent s ◊ next »

Chapt er 19: Int ernat ionalizat ion


Dj ango has full support for int ernat ionalizat ion of t ext in code and t em plat es. Here’s how it works.

Overview
The goal of int ernat ionalizat ion is t o allow a single Web applicat ion t o offer it s cont ent and funct ionalit y in m ult iple languages.

You, t he Dj ango developer, can accom plish t his goal by adding a m inim al am ount of hooks t o your Pyt hon code and t em plat es.
These hooks are called t r a n sla t ion st r in gs. They t ell Dj ango: “ This t ext should be t ranslat ed int o t he end user’s language, if a
t ranslat ion for t his t ext is available in t hat language.”

Dj ango t akes care of using t hese hooks t o t ranslat e Web apps, on t he fly, according t o users’ language preferences.

Essent ially, Dj ango does t wo t hings:

● I t let s developers and t em plat e aut hors specify which part s of t heir apps should be t ranslat able.
● I t uses t hese hooks t o t ranslat e Web apps for part icular users according t o t heir language preferences.

How to internationalize your app: in three steps


1. Em bed t ranslat ion st rings in your Pyt hon code and t em plat es.
2. Get t ranslat ions for t hose st rings, in whichever languages you want t o support .
3. Act ivat e t he locale m iddleware in your Dj ango set t ings.

Be h in d t h e sce n e s
Dj ango’s t ranslat ion m achinery uses t he st andard gettext m odule t hat com es wit h Pyt hon.

If you don’ t need int ernat ionalizat ion


Dj ango’s int ernat ionalizat ion hooks are on by default , and t hat m eans t here’s a bit of i18n- relat ed overhead in cert ain places of
t he fram ework. I f you don’t use int ernat ionalizat ion, you should t ake t he t wo seconds t o set USE_I18N = False in your set t ings
file. I f USE_I18N is set t o False, t hen Dj ango will m ake som e opt im izat ions so as not t o load t he int ernat ionalizat ion m achinery.

You’ll probably also want t o rem ove 'django.core.context_processors.i18n' from your TEMPLATE_CONTEXT_PROCESSORS
set t ing.

How t o specif y t ranslat ion st rings


Translat ion st rings specify “ This t ext should be t ranslat ed.” These st rings can appear in your Pyt hon code and t em plat es. I t ’s your
responsibilit y t o m ark t ranslat able st rings; t he syst em can only t ranslat e st rings it knows about .

In Python code

Standard translation
Specify a t ranslat ion st ring by using t he funct ion _(). ( Yes, t he nam e of t he funct ion is t he “ underscore” charact er.) This funct ion
is available globally in any Pyt hon m odule; you don’t have t o im port it .

I n t his exam ple, t he t ext "Welcome to my site." is m arked as a t ranslat ion st ring:

def my_view(request):
output = _("Welcome to my site.")
return HttpResponse(output)

The funct ion django.utils.translation.gettext() is ident ical t o _(). This exam ple is ident ical t o t he previous one:

from django.utils.translation import gettext


def my_view(request):
output = gettext("Welcome to my site.")
return HttpResponse(output)

file:///D|/books/computer/programming/python/books/DJANGO BOOK/19.html (1 of 9)9/28/2007 2:31:46 PM


Chapter 19: Internationalization and localization

Translat ion works on com put ed values. This exam ple is ident ical t o t he previous t wo:

def my_view(request):
words = ['Welcome', 'to', 'my', 'site.']
output = _(' '.join(words))
return HttpResponse(output)

Translat ion works on variables. Again, here’s an ident ical exam ple:

def my_view(request):
sentence = 'Welcome to my site.'
output = _(sentence)
return HttpResponse(output)

( The caveat wit h using variables or com put ed values, as in t he previous t wo exam ples, is t hat Dj ango’s t ranslat ion- st ring-
det ect ing ut ilit y, make-messages.py, won’t be able t o find t hese st rings. More on make-messages lat er.)

The st rings you pass t o _() or gettext() can t ake placeholders, specified wit h Pyt hon’s st andard nam ed- st ring int erpolat ion
synt ax. Exam ple:

def my_view(request, n):


output = _('%(name)s is my name.') % {'name': n}
return HttpResponse(output)

This t echnique let s language- specific t ranslat ions reorder t he placeholder t ext . For exam ple, an English t ranslat ion m ay
be "Adrian is my name.", while a Spanish t ranslat ion m ay be "Me llamo Adrian." — wit h t he placeholder ( t he nam e) placed
aft er t he t ranslat ed t ext inst ead of before it .

For t his reason, you should use nam ed- st ring int erpolat ion ( e.g., %(name)s) inst ead of posit ional int erpolat ion ( e.g., %s or %d) . I f
you used posit ional int erpolat ion, t ranslat ions wouldn’t be able t o reorder placeholder t ext .

Marking strings as no-op


Use t he funct ion django.utils.translation.gettext_noop() t o m ark a st ring as a t ranslat ion st ring wit hout t ranslat ing it . The
st ring is lat er t ranslat ed from a variable.

Use t his if you have const ant st rings t hat should be st ored in t he source language because t hey are exchanged over syst em s or
users — such as st rings in a dat abase — but should be t ranslat ed at t he last possible point in t im e, such as when t he st ring is
present ed t o t he user.

Lazy translation
Use t he funct ion django.utils.translation.gettext_lazy() t o t ranslat e st rings lazily — when t he value is accessed rat her
t han when t he gettext_lazy() funct ion is called.

For exam ple, t o t ranslat e a m odel’s help_text, do t he following:

from django.utils.translation import gettext_lazy

class MyThing(models.Model):
name = models.CharField(help_text=gettext_lazy('This is the help text'))

I n t his exam ple, gettext_lazy() st ores a lazy reference t o t he st ring — not t he act ual t ranslat ion. The t ranslat ion it self will be
done when t he st ring is used in a st ring cont ext , such as t em plat e rendering on t he Dj ango adm in sit e.

I f you don’t like t he verbose nam e gettext_lazy, you can j ust alias it as _ ( underscore) , like so:

from django.utils.translation import gettext_lazy as _


class MyThing(models.Model):
name = models.CharField(help_text=_('This is the help text'))

Always use lazy t ranslat ions in Dj ango m odels. And it ’s a good idea t o add t ranslat ions for t he field nam es and t able nam es, t oo.
This m eans writ ing explicit verbose_name and verbose_name_plural opt ions in t he Meta class, t hough:

from django.utils.translation import gettext_lazy as _


class MyThing(models.Model):
name = models.CharField(_('name'), help_text=_('This is the help text'))

file:///D|/books/computer/programming/python/books/DJANGO BOOK/19.html (2 of 9)9/28/2007 2:31:46 PM


Chapter 19: Internationalization and localization

class Meta:
verbose_name = _('my thing')
verbose_name_plural = _('mythings')

Pluralization
Use t he funct ion django.utils.translation.ngettext() t o specify pluralized m essages. Exam ple:

from django.utils.translation import ngettext


def hello_world(request, count):
page = ngettext('there is %(count)d object', 'there are %(count)d objects', count) % {
'count': count,
}
return HttpResponse(page)

ngettext t akes t hree argum ent s: t he singular t ranslat ion st ring, t he plural t ranslat ion st ring and t he num ber of obj ect s ( which is
passed t o t he t ranslat ion languages as t he count variable) .

In template code
Using t ranslat ions in Dj ango t em plat es uses t wo t em plat e t ags and a slight ly different synt ax t han in Pyt hon code. To give your
t em plat e access t o t hese t ags, put {% load i18n %} t oward t he t op of your t em plat e.

The {% trans %} t em plat e t ag t ranslat es a const ant st ring or a variable cont ent :

<title>{% trans "This is the title." %}</title>

I f you only want t o m ark a value for t ranslat ion, but t ranslat e it lat er from a variable, use t he noop opt ion:

<title>{% trans "value" noop %}</title>

I t ’s not possible t o use t em plat e variables in {% trans %} — only const ant st rings, in single or double quot es, are allowed. I f your
t ranslat ions require variables ( placeholders) , use {% blocktrans %}. Exam ple:

{% blocktrans %}This will have {{ value }} inside.{% endblocktrans %}

To t ranslat e a t em plat e expression — say, using t em plat e filt ers — you need t o bind t he expression t o a local variable for use
wit hin t he t ranslat ion block:

{% blocktrans with value|filter as myvar %}


This will have {{ myvar }} inside.
{% endblocktrans %}

I f you need t o bind m ore t han one expression inside a blocktrans t ag, separat e t he pieces wit h and:

{% blocktrans with book|title as book_t and author|title as author_t %}


This is {{ book_t }} by {{ author_t }}
{% endblocktrans %}

To pluralize, specify bot h t he singular and plural form s wit h t he {% plural %} t ag, which appears wit hin {% blocktrans %}
and {% endblocktrans %}. Exam ple:

{% blocktrans count list|count as counter %}


There is only one {{ name }} object.
{% plural %}
There are {{ counter }} {{ name }} objects.
{% endblocktrans %}

I nt ernally, all block and inline t ranslat ions use t he appropriat e gettext / ngettext call.

Each RequestContext has access t o t wo t ranslat ion- specific variables:

● LANGUAGES is a list of t uples in which t he first elem ent is t he language code and t he second is t he language nam e ( in t hat
language) .
● LANGUAGE_CODE is t he current user’s preferred language, as a st ring. Exam ple: en-us. ( See “ How language preference is
discovered” , below.)
● LANGUAGE_BIDI is t he current language’s direct ion. I f True, it ’s a right - t o- left language, e.g: Hebrew, Arabic. I f False it ’s a

file:///D|/books/computer/programming/python/books/DJANGO BOOK/19.html (3 of 9)9/28/2007 2:31:46 PM


Chapter 19: Internationalization and localization

left - t o- right language, e.g: English, French, Germ an et c.

I f you don’t use t he RequestContext ext ension, you can get t hose values wit h t hree t ags:

{% get_current_language as LANGUAGE_CODE %}
{% get_available_languages as LANGUAGES %}
{% get_current_language_bidi as LANGUAGE_BIDI %}

These t ags also require a {% load i18n %}.

Translat ion hooks are also available wit hin any t em plat e block t ag t hat accept s const ant st rings. I n t hose cases, j ust use _()
synt ax t o specify a t ranslat ion st ring. Exam ple:

{% some_special_tag _("Page not found") value|yesno:_("yes,no") %}

I n t his case, bot h t he t ag and t he filt er will see t he already- t ranslat ed st ring, so t hey don’t need t o be aware of t ranslat ions.

How t o creat e language f iles


Once you’ve t agged your st rings for lat er t ranslat ion, you need t o writ e ( or obt ain) t he language t ranslat ions t hem selves. Here’s
how t hat works.

Message files
The first st ep is t o creat e a m e ssa ge file for a new language. A m essage file is a plain- t ext file, represent ing a single language,
t hat cont ains all available t ranslat ion st rings and how t hey should be represent ed in t he given language. Message files have a .po
file ext ension.

Dj ango com es wit h a t ool, bin/make-messages.py, t hat aut om at es t he creat ion and upkeep of t hese files.

To creat e or updat e a m essage file, run t his com m and:

bin/make-messages.py -l de

…where de is t he language code for t he m essage file you want t o creat e. The language code, in t his case, is in locale form at . For
exam ple, it ’s pt_BR for Brazilian and de_AT for Aust rian Germ an.

The script should be run from one of t hree places:

● The root django direct ory ( not a Subversion checkout , but t he one t hat is linked- t o via $PYTHONPATH or is locat ed
som ewhere on t hat pat h) .
● The root direct ory of your Dj ango proj ect .
● The root direct ory of your Dj ango app.

The script runs over t he ent ire Dj ango source t ree and pulls out all st rings m arked for t ranslat ion. I t creat es ( or updat es) a
m essage file in t he direct ory conf/locale. I n t he de exam ple, t he file will be conf/locale/de/LC_MESSAGES/django.po.

I f run over your proj ect source t ree or your applicat ion source t ree, it will do t he sam e, but t he locat ion of t he locale direct ory
is locale/LANG/LC_MESSAGES ( not e t he m issing conf prefix) .

N o ge t t e x t ?
I f you don’t have t he gettext ut ilit ies inst alled, make-messages.py will creat e em pt y files. I f t hat ’s t he case, eit her
inst all t he gettext ut ilit ies or j ust copy t he English m essage file ( conf/locale/en/LC_MESSAGES/django.po) and
use it as a st art ing point ; it ’s j ust an em pt y t ranslat ion file.

The form at of .po files is st raight forward. Each .po file cont ains a sm all bit of m et adat a, such as t he t ranslat ion m aint ainer’s
cont act inform at ion, but t he bulk of t he file is a list of m e ssa ge s — sim ple m appings bet ween t ranslat ion st rings and t he act ual
t ranslat ed t ext for t he part icular language.

For exam ple, if your Dj ango app cont ained a t ranslat ion st ring for t he t ext "Welcome to my site.", like so:

_("Welcome to my site.")

…t hen make-messages.py will have creat ed a .po file cont aining t he following snippet — a m essage:

#: path/to/python/module.py:23
msgid "Welcome to my site."

file:///D|/books/computer/programming/python/books/DJANGO BOOK/19.html (4 of 9)9/28/2007 2:31:46 PM


Chapter 19: Internationalization and localization

msgstr ""

A quick explanat ion:

● msgid is t he t ranslat ion st ring, which appears in t he source. Don’t change it .


● msgstr is where you put t he language- specific t ranslat ion. I t st art s out em pt y, so it ’s your responsibilit y t o change it . Make
sure you keep t he quot es around your t ranslat ion.
● As a convenience, each m essage includes t he filenam e and line num ber from which t he t ranslat ion st ring was gleaned.

Long m essages are a special case. There, t he first st ring direct ly aft er t he msgstr ( or msgid) is an em pt y st ring. Then t he cont ent
it self will be writ t en over t he next few lines as one st ring per line. Those st rings are direct ly concat enat ed. Don’t forget t railing
spaces wit hin t he st rings; ot herwise, t hey’ll be t acked t oget her wit hout whit espace!

M in d you r ch a r se t
When creat ing a .po file wit h your favorit e t ext edit or, first edit t he charset line ( search for "CHARSET") and set it t o
t he charset you’ll be using t o edit t he cont ent . Generally, ut f- 8 should work for m ost languages, but gettext should
handle any charset you t hrow at it .

To reexam ine all source code and t em plat es for new t ranslat ion st rings and updat e all m essage files for a ll languages, run t his:

make-messages.py -a

Compiling message files


Aft er you creat e your m essage file — and each t im e you m ake changes t o it — you’ll need t o com pile it int o a m ore efficient form ,
for use by gettext. Do t his wit h t he bin/compile-messages.py ut ilit y.

This t ool runs over all available .po files and creat es .mo files, which are binary files opt im ized for use by gettext. I n t he sam e
direct ory from which you ran make-messages.py, run compile-messages.py like t his:

bin/compile-messages.py

That ’s it . Your t ranslat ions are ready for use.

How Dj ango discovers language pref erence


Once you’ve prepared your t ranslat ions — or, if you j ust want t o use t he t ranslat ions t hat com e wit h Dj ango — you’ll j ust need t o
act ivat e t ranslat ion for your app.

Behind t he scenes, Dj ango has a very flexible m odel of deciding which language should be used — inst allat ion- wide, for a
part icular user, or bot h.

To set an inst allat ion- wide language preference, set LANGUAGE_CODE in your set t ings file. Dj ango uses t his language as t he default
t ranslat ion — t he final at t em pt if no ot her t ranslat or finds a t ranslat ion.

I f all you want t o do is run Dj ango wit h your nat ive language, and a language file is available for your language, all you need t o
do is set LANGUAGE_CODE.

I f you want t o let each individual user specify which language he or she prefers, use LocaleMiddleware. LocaleMiddleware
enables language select ion based on dat a from t he request . I t cust om izes cont ent for each user.

To use LocaleMiddleware, add 'django.middleware.locale.LocaleMiddleware' t o your MIDDLEWARE_CLASSES set t ing. Because


m iddleware order m at t ers, you should follow t hese guidelines:

● Make sure it ’s one of t he first m iddlewares inst alled.


● I t should com e aft er SessionMiddleware, because LocaleMiddleware m akes use of session dat a.
● I f you use CacheMiddleware, put LocaleMiddleware aft er it .

For exam ple, your MIDDLEWARE_CLASSES m ight look like t his:

MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware',
)

LocaleMiddleware t ries t o det erm ine t he user’s language preference by following t his algorit hm :

file:///D|/books/computer/programming/python/books/DJANGO BOOK/19.html (5 of 9)9/28/2007 2:31:46 PM


Chapter 19: Internationalization and localization

● First , it looks for a django_language key in t he t he current user’s session.


● Failing t hat , it looks for a cookie called django_language.
● Failing t hat , it looks at t he Accept-Language HTTP header. This header is sent by your browser and t ells t he server which
language( s) you prefer, in order by priorit y. Dj ango t ries each language in t he header unt il it finds one wit h available
t ranslat ions.
● Failing t hat , it uses t he global LANGUAGE_CODE set t ing.

Not es:

● I n each of t hese places, t he language preference is expect ed t o be in t he st andard language form at , as a st ring. For
exam ple, Brazilian is pt-br.

● I f a base language is available but t he sublanguage specified is not , Dj ango uses t he base language. For exam ple, if a user
specifies de-at ( Aust rian Germ an) but Dj ango only has de available, Dj ango uses de.

● Only languages list ed in t he LANGUAGES set t ing can be select ed. I f you want t o rest rict t he language select ion t o a subset of
provided languages ( because your applicat ion doesn’t provide all t hose languages) , set LANGUAGES t o a list of languages. For
exam ple:

LANGUAGES = (
('de', _('German')),
('en', _('English')),
)

This exam ple rest rict s languages t hat are available for aut om at ic select ion t o Germ an and English ( and any sublanguage,
like de- ch or en- us) .

● I f you define a cust om LANGUAGES set t ing, as explained in t he previous bullet , it ’s OK t o m ark t he languages as t ranslat ion
st rings — but use a “ dum m y” gettext() funct ion, not t he one in django.utils.translation. You should never
im port django.utils.translation from wit hin your set t ings file, because t hat m odule in it self depends on t he set t ings, and
t hat would cause a circular im port .

The solut ion is t o use a “ dum m y” gettext() funct ion. Here’s a sam ple set t ings file:

gettext = lambda s: s

LANGUAGES = (
('de', gettext('German')),
('en', gettext('English')),
)

Wit h t his arrangem ent , make-messages.py will st ill find and m ark t hese st rings for t ranslat ion, but t he t ranslat ion won’t
happen at runt im e — so you’ll have t o rem em ber t o wrap t he languages in t he real gettext() in any code t hat
uses LANGUAGES at runt im e.

● The LocaleMiddleware can only select languages for which t here is a Dj ango- provided base t ranslat ion. I f you want t o
provide t ranslat ions for your applicat ion t hat aren’t already in t he set of t ranslat ions in Dj ango’s source t ree, you’ll want t o
provide at least basic t ranslat ions for t hat language. For exam ple, Dj ango uses t echnical m essage I Ds t o t ranslat e dat e
form at s and t im e form at s — so you will need at least t hose t ranslat ions for t he syst em t o work correct ly.

A good st art ing point is t o copy t he English .po file and t o t ranslat e at least t he t echnical m essages — m aybe t he validat or
m essages, t oo.

Technical m essage I Ds are easily recognized; t hey’re all upper case. You don’t t ranslat e t he m essage I D as wit h ot her
m essages, you provide t he correct local variant on t he provided English value. For exam ple, wit h DATETIME_FORMAT
( or DATE_FORMAT or TIME_FORMAT) , t his would be t he form at st ring t hat you want t o use in your language. The form at is
ident ical t o t he form at st rings used by t he now t em plat e t ag.

Once LocaleMiddleware det erm ines t he user’s preference, it m akes t his preference available as request.LANGUAGE_CODE for
each request obj ect . Feel free t o read t his value in your view code. Here’s a sim ple exam ple:

def hello_world(request, count):


if request.LANGUAGE_CODE == 'de-at':
return HttpResponse("You prefer to read Austrian German.")
else:
return HttpResponse("You prefer to read another language.")

Not e t hat , wit h st at ic ( m iddleware- less) t ranslat ion, t he language is in settings.LANGUAGE_CODE, while wit h dynam ic
( m iddleware) t ranslat ion, it ’s in request.LANGUAGE_CODE.

The set_language redirect view

file:///D|/books/computer/programming/python/books/DJANGO BOOK/19.html (6 of 9)9/28/2007 2:31:46 PM


Chapter 19: Internationalization and localization

As a convenience, Dj ango com es wit h a view, django.views.i18n.set_language, t hat set s a user’s language preference and
redirect s back t o t he previous page.

Act ivat e t his view by adding t he following line t o your URLconf:

(r'^i18n/', include('django.conf.urls.i18n')),

( Not e t hat t his exam ple m akes t he view available at /i18n/setlang/.)

The view expect s t o be called via t he GET m et hod, wit h a language param et er set in t he query st ring. I f session support is
enabled, t he view saves t he language choice in t he user’s session. Ot herwise, it saves t he language choice in a django_language
cookie.

Aft er set t ing t he language choice, Dj ango redirect s t he user, following t his algorit hm :

● Dj ango looks for a next param et er in t he query st ring.


● I f t hat doesn’t exist , or is em pt y, Dj ango t ries t he URL in t he Referer header.
● I f t hat ’s em pt y — say, if a user’s browser suppresses t hat header — t hen t he user will be redirect ed t o / ( t he sit e root ) as a
fallback.

Here’s exam ple HTML t em plat e code:

<form action="/i18n/setlang/" method="get">


<input name="next" type="hidden" value="/next/page/" />
<select name="language">
{% for lang in LANGUAGES %}
<option value="{{ lang.0 }}">{{ lang.1 }}</option>
{% endfor %}
</select>
<input type="submit" value="Go" />
</form>

Using t ranslat ions in your own proj ect s


Dj ango looks for t ranslat ions by following t his algorit hm :

● First , it looks for a locale direct ory in t he applicat ion direct ory of t he view t hat ’s being called. I f it finds a t ranslat ion for t he
select ed language, t he t ranslat ion will be inst alled.
● Next , it looks for a locale direct ory in t he proj ect direct ory. I f it finds a t ranslat ion, t he t ranslat ion will be inst alled.
● Finally, it checks t he base t ranslat ion in django/conf/locale.

This way, you can writ e applicat ions t hat include t heir own t ranslat ions, and you can override base t ranslat ions in your proj ect
pat h. Or, you can j ust build a big proj ect out of several apps and put all t ranslat ions int o one big proj ect m essage file. The choice
is yours.

N ot e
I f you’re using m anually configured set t ings, t he locale direct ory in t he proj ect direct ory will not be exam ined,
since Dj ango loses t he abilit y t o work out t he locat ion of t he proj ect direct ory. ( Dj ango norm ally uses t he locat ion of
t he set t ings file t o det erm ine t his, and a set t ings file doesn’t exist if you’re m anually configuring your set t ings.)

All m essage file reposit ories are st ruct ured t he sam e way. They are:

● $APPPATH/locale/<language>/LC_MESSAGES/django.(po|mo)
● $PROJECTPATH/locale/<language>/LC_MESSAGES/django.(po|mo)
● All pat hs list ed in LOCALE_PATHS in your set t ings file are searched in t hat order
for <language>/LC_MESSAGES/django.(po|mo)
● $PYTHONPATH/django/conf/locale/<language>/LC_MESSAGES/django.(po|mo)

To creat e m essage files, you use t he sam e make-messages.py t ool as wit h t he Dj ango m essage files. You only need t o be in t he
right place — in t he direct ory where eit her t he conf/locale ( in case of t he source t ree) or t he locale/ ( in case of app m essages
or proj ect m essages) direct ory are locat ed. And you use t he sam e compile-messages.py t o produce t he binary django.mo files
t hat are used by gettext.

Applicat ion m essage files are a bit com plicat ed t o discover — t hey need t he LocaleMiddleware. I f you don’t use t he m iddleware,
only t he Dj ango m essage files and proj ect m essage files will be processed.

Finally, you should give som e t hought t o t he st ruct ure of your t ranslat ion files. I f your applicat ions need t o be delivered t o ot her

file:///D|/books/computer/programming/python/books/DJANGO BOOK/19.html (7 of 9)9/28/2007 2:31:46 PM


Chapter 19: Internationalization and localization

users and will be used in ot her proj ect s, you m ight want t o use app- specific t ranslat ions. But using app- specific t ranslat ions and
proj ect t ranslat ions could produce weird problem s wit h make-messages: make-messages will t raverse all direct ories below t he
current pat h and so m ight put m essage I Ds int o t he proj ect m essage file t hat are already in applicat ion m essage files.

The easiest way out is t o st ore applicat ions t hat are not part of t he proj ect ( and so carry t heir own t ranslat ions) out side t he
proj ect t ree. That way, make-messages on t he proj ect level will only t ranslat e st rings t hat are connect ed t o your explicit proj ect
and not st rings t hat are dist ribut ed independent ly.

Translat ions and JavaScript


Adding t ranslat ions t o JavaScript poses som e problem s:

● JavaScript code doesn’t have access t o a gettext im plem ent at ion.


● JavaScript code doesn’t have access t o .po or .m o files; t hey need t o be delivered by t he server.
● The t ranslat ion cat alogs for JavaScript should be kept as sm all as possible.

Dj ango provides an int egrat ed solut ion for t hese problem s: I t passes t he t ranslat ions int o JavaScript , so you can call gettext,
et c., from wit hin JavaScript .

The javascript_catalog view


The m ain solut ion t o t hese problem s is t he javascript_catalog view, which sends out a JavaScript code library wit h funct ions
t hat m im ic t he gettext int erface, plus an array of t ranslat ion st rings. Those t ranslat ion st rings are t aken from t he applicat ion,
proj ect or Dj ango core, according t o what you specify in eit her t he { { { info_dict } } } or t he URL.

You hook it up like t his:

js_info_dict = {
'packages': ('your.app.package',),
}

urlpatterns = patterns('',
(r'^jsi18n/$', 'django.views.i18n.javascript_catalog', js_info_dict),
)

Each st ring in packages should be in Pyt hon dot t ed- package synt ax ( t he sam e form at as t he st rings in INSTALLED_APPS) and
should refer t o a package t hat cont ains a locale direct ory. I f you specify m ult iple packages, all t hose cat alogs are m erged int o
one cat alog. This is useful if you have JavaScript t hat uses st rings from different applicat ions.

You can m ake t he view dynam ic by put t ing t he packages int o t he URL pat t ern:

urlpatterns = patterns('',
(r'^jsi18n/(?P<packages>\S+?)/$, 'django.views.i18n.javascript_catalog'),
)

Wit h t his, you specify t he packages as a list of package nam es delim it ed by ‘+ ’ signs in t he URL. This is especially useful if your
pages use code from different apps and t his changes oft en and you don’t want t o pull in one big cat alog file. As a securit y
m easure, t hese values can only be eit her django.conf or any package from t he INSTALLED_APPS set t ing.

Using the JavaScript translation catalog


To use t he cat alog, j ust pull in t he dynam ically generat ed script like t his:

<script type="text/javascript" src="/path/to/jsi18n/"></script>

This is how t he adm in fet ches t he t ranslat ion cat alog from t he server. When t he cat alog is loaded, your JavaScript code can use
t he st andard gettext int erface t o access it :

document.write(gettext('this is to be translated'));

There even is a ngettext int erface and a st ring int erpolat ion funct ion:

d = {
count: 10
};
s = interpolate(ngettext('this is %(count)s object', 'this are %(count)s objects', d.count),
d);

The interpolate funct ion support s bot h posit ional int erpolat ion and nam ed int erpolat ion. So t he above could have been writ t en

file:///D|/books/computer/programming/python/books/DJANGO BOOK/19.html (8 of 9)9/28/2007 2:31:46 PM


Chapter 19: Internationalization and localization

as:

s = interpolate(ngettext('this is %s object', 'this are %s objects', 11), [11]);

The int erpolat ion synt ax is borrowed from Pyt hon. You shouldn’t go over t he t op wit h st ring int erpolat ion, t hough: t his is st ill
JavaScript , so t he code will have t o do repeat ed regular- expression subst it ut ions. This isn’t as fast as st ring int erpolat ion in
Pyt hon, so keep it t o t hose cases where you really need it ( for exam ple, in conj unct ion wit h ngettext t o produce proper
pluralizat ions) .

Creating JavaScript translation catalogs


You creat e and updat e t he t ranslat ion cat alogs t he sam e way as t he ot her Dj ango t ranslat ion cat alogs — wit h t he { { { m ake-
m essages.py} } } t ool. The only difference is you need t o provide a -d djangojs param et er, like t his:

make-messages.py -d djangojs -l de

This would creat e or updat e t he t ranslat ion cat alog for JavaScript for Germ an. Aft er updat ing t ranslat ion cat alogs, j ust
run compile-messages.py t he sam e way as you do wit h norm al Dj ango t ranslat ion cat alogs.

Not es f or users f amiliar wit h gettext


I f you know gettext, you m ight not e t hese specialit ies in t he way Dj ango does t ranslat ion:

● The st ring dom ain is django or djangojs. The st ring dom ain is used t o different iat e bet ween different program s t hat st ore
t heir dat a in a com m on m essage- file library ( usually /usr/share/locale/) . The django dom ain is used for pyt hon and
t em plat e t ranslat ion st rings and is loaded int o t he global t ranslat ion cat alogs. The djangojs dom ain is only used for
JavaScript t ranslat ion cat alogs t o m ake sure t hat t hose are as sm all as possible.
● Dj ango only uses gettext and gettext_noop. That ’s because Dj ango always uses DEFAULT_CHARSET st rings int ernally. There
isn’t m uch use in using ugettext, because you’ll always need t o produce ut f- 8 anyway.
● Dj ango doesn’t use xgettext alone. I t uses Pyt hon wrappers around xgettext and msgfmt. That ’s m ost ly for convenience.

« previous ◊ t able of cont ent s ◊ next »


Copyright 2006 Adrian Holovat y and Jacob Kaplan- Moss.
This work is licensed under t he GNU Free Docum ent License.

file:///D|/books/computer/programming/python/books/DJANGO BOOK/19.html (9 of 9)9/28/2007 2:31:46 PM


Chapter 20: Security

The Dj ango Book


« previous ◊ t able of cont ent s ◊ next »

Securit y
The int ernet can be a scary place.

I n t he past few years, int ernet horror st ories have been in t he news alm ost cont inuously. We’ve seen viruses spread wit h am azing
speed, swarm s of com prom ised com put ers wielded as weapons, a never- ending arm s race against spam m ers, and m any, m any
report s of ident ify t heft from com prom ised web sit es.

As good web developers, it ’s our dut y t o do what we can t o com bat t hese forces of darkness. Every web developer needs t o t reat
securit y as a fundam ent al aspect of web program m ing. Unfort unat ely, it t urns out t hat securit y is hard — at t ackers only need t o
find a single vulnerabilit y, but defenders have t o prot ect every single one.

Dj ango at t em pt s t o m it igat e t his difficult y. I t ’s designed t o aut om at ically prot ect you from m any of t he com m on securit y m ist akes
t hat new ( and even experienced) web developers m ake. St ill, it ’s im port ant t o underst and what t hese problem s are, how Dj ango
prot ect s you, and — m ost im port ant ly — t he st eps you can t ake t o m ake your code even m ore secure.

First , t hough, an im port ant disclaim er: we’re in no way expert s in t his realm , and so we won’t t ry t o explain each vulnerabilit y in
a com prehensive m anner. I nst ead, we’ll give a short synopsis of securit y problem s as t hey apply t o Dj ango.

The t heme of web securit y


I f you learn only one t hing from t his chapt er, let it be t his:

Never — under any circumst ances — t rust dat a f rom t he browser.

You never know who’s on t he ot her side of t hat HTTP connect ion. I t m ight be one of your users, but it j ust as easily could be a
cracker or script kiddie looking for an opening.

Any dat a of any nat ure t hat com es from t he browser needs t o be t reat ed wit h a healt hy dose of paranoia. This includes dat a
t hat ’s bot h “ in band” — i.e. subm it t ed from web form s — and “ out of band” — i.e. HTTP headers, cookies, and ot her request info.
I t ’s t rivial t o spoof t he request m et adat a t hat browsers usually add aut om at ically.

Every one of t he vulnerabilit ies discussed in t his chapt er st em s direct ly from t rust ing dat a t hat com es over t he wire and t hen
failing t o sanit ize t hat dat a before using it . You should m ake it a general pract ice t o cont inuously ask, “ where does t his dat a com e
from ?” .

SQL inj ect ion


SQL in j e ct ion is a com m on exploit in which an at t acker alt ers Web- page param et ers ( such as GET/ POST dat a or URLs) t o insert
arbit rary SQL snippet s t hat a naive Web applicat ion execut es in it s dat abase direct ly. I t ’s probably t he m ost dangerous — and
unfort unat ely one of t he m ost com m on — vulnerabilit ies in t he wild.

This vulnerabilit y m ost com m only crops up when const ruct ing SQL “ by hand” from user input . For exam ple, im agine writ ing a
funct ion t o gat her a list of a cont act info from a cont act search page. To prevent spam m ers from reading every single em ail in our
syst em , we’ll force t he user t o t ype in som eone’s usernam e before we provide t heir em ail address:

def user_contacts(request):
user = request.GET['username']
sql = "SELECT * FROM user_contacts WHERE username = '%s';" % username
# execute the SQL here...

N ot e
I n t his exam ple, and all sim ilar “ don’t do t his” exam ples t hat follow, we’ve deliberat ely left out m ost of t he code
needed t o m ake t he funct ions act ually work. We don’t want t his code t o work if som eone accident ally t akes it out of
cont ext .

Though at first t his doesn’t look dangerous, it really is.

First , our at t em pt at prot ect ing our ent ire em ail list will fail wit h a cleverly const ruct ed query. Think about what happens if an
at t acker t ypes "' OR 'a'='a" int o t he query box. I n t hat case, t he query t hat t he st ring int erpolat ion will const ruct will be:

SELECT * FROM user_contacts WHERE username = '' OR 'a' = 'a';

file:///D|/books/computer/programming/python/books/DJANGO BOOK/20.html (1 of 6)9/28/2007 2:32:01 PM


Chapter 20: Security

Because we allowed unsecured SQL int o t he st ring, t he at t acker’s added OR clause ensures t hat every single row is ret urned.

However, t hat ’s t he least scary at t ack. I m agine what will happen if t he at t acker
subm it s "'; DELETE FROM user_contacts WHERE 'a' = 'a“ . We’ll end up wit h t his com plet e query:

SELECT * FROM user_contacts WHERE username = ''; DELETE FROM user_contacts WHERE 'a' = 'a';

Yikes! Where’d our cont act list go?

The solution
Alt hough t his problem is insidious and som et im es hard t o spot , t he solut ion is sim ple: never t rust user- subm it t ed dat a, and
always escape it when passing it int o SQL.

The Dj ango dat abase API does t his for you. I t aut om at ically escapes all special SQL param et ers, according t o t he quot ing
convent ions of t he dat abase server you’re using ( e.g. Post greSQL, MySQL) .

For exam ple, in t his API call:

foo.get_list(bar__exact="' OR 1=1")

Dj ango will escape t he input accordingly, result ing in a st at em ent like t his:

SELECT * FROM foos WHERE bar = '\' OR 1=1'

Com plet ely harm less.

This applies t o t he ent ire Dj ango dat abase API , wit h a couple of except ions:

● The where argum ent t o t he extra() m et hod ( see Appendix XXX) . That param et er accept s raw SQL by design.
● Queries done “ by hand” using t he lower- level dat abase API .

I n each of t hese cases, it ’s easy t o keep yourself prot ect ed. I n each case, avoid st ring int erpolat ion in favor of passing in “ bind
param et ers” . That is, t he exam ple we st art ed t his sect ion wit h should be writ t en:

from django.db import connection


def user_contacts(request):
user = request.GET['username']
sql = "SELECT * FROM user_contacts WHERE username = %s;"
cursor = connection.cursor()
cursor.execute(sql, [user])
# ... do something with the results

The low- level execute m et hod t akes a SQL st ring wit h %s placeholders, and aut om at ically escapes and insert s param et ers from
t he list passed as t he second argum ent . You should always const ruct cust om SQL t his way.

Unfort unat ely, you can’t use bind param et ers everywhere in SQL; t hey’re not allowed as ident ifiers ( i.e. t able or colum n nam es) .
Thus, if you need t o, say, dynam ically const ruct a list of t ables from a POST variable, you’ll need t o escape t hat nam e in your
code. Dj ango provides a funct ion, django.db.backend.quote_name, which will escape t he ident ifier according t o t he current
dat abase’s quot ing schem e.

Cross-sit e Script ing (XSS)


Probably t he m ost com m on web vulnerabilit y, cr oss- sit e scr ipt in g, or XSS, is found in web applicat ions t hat fail t o properly
escape user- subm it t ed cont ent before rendering it int o HTML. This allows an at t acker t o m aliciously insert arbit rary HTML, usually
in t he form of <script> t ags.

At t ackers oft en use XSS at t acks t o st eal cookie and session info, or t o t rick users int o giving privat e inform at ion t o t he wrong
person ( a.k.a ph ish in g) .

This t ype of at t ack can t ake a num ber of different form s, and has alm ost infinit e perm ut at ions, so we’ll j ust look at a t ypical
exam ple. Let ’s look at a ext rem ely sim ple “ hello world” view:

def say_hello(request):
name = request.GET.get('name', 'world')
return render_to_response("hello.html", {"name" : name})

This view sim ply reads a nam e from a GET param et er and passes t hat nam e t o t he hello.html t em plat e. We m ight writ e a
t em plat e for t his view like:

file:///D|/books/computer/programming/python/books/DJANGO BOOK/20.html (2 of 6)9/28/2007 2:32:01 PM


Chapter 20: Security

<h1>Hello, {{ name }}!</h1>

So if we accessed http://example.com/hello/name=Jacob, t he rendered page would cont ain:

<h1>Hello, Jacob!</h1>

But wait — what happens if we access http://example.com/hello/name=<i>Jacob</i>? Then we’d get :

<h1>Hello, <i>Jacob</i>!</h1>

Of course, an at t acker wouldn’t use som et hing as benign as <i> t ags; he could include a whole set of HTML t hat hij acked your
page wit h arbit rary cont ent . This t ype of at t ack has been used t o t rick users int o ent ering dat a int o what looks like t heir bank’s
websit e, but in fact is an XSS- hij acked form t hat subm it s your back account inform at ion t o an at t acker.

This get s worse if you st oring t his dat a in t he dat abase and lat er display it it on your sit e.

For exam ple, at one point MySpace was found t o be vulnerable t o an XSS at t ack of t his nat ure. A user insert ed j avascript int o his
profile t hat aut om at ically added him as your friend when you visit ed his profile page. Wit hin a few days he had m illions of friends.

Now, t his m ay sound relat ively benign, but keep in m ind t hat t his at t acker m anaged t o get his code — not MySpace’s — running
on your com put er. This violoat es t he assum ed t rust t hat all t he code on MySpace is act ually writ t en by MySpace.

MySpace was ext rem ely lucky t hat t his m alicious code didn’t aut om at ically delet e viewer’s account s, change t heir passwords,
flood t he sit e wit h spam , or any of t he ot her night m are scenarios t his vulnerabilit y unleashes.

The solution
The solut ion is sim ple: always escape any cont ent t hat m ight have com e from a user. I f we sim ply rewrit e our t em plat e as:

<h1>Hello, {{ name|escape }}!</h1>

t hen we’re no longer vulnerable. You should always use t he escape t ag ( or an analogue) when displaying user- subm it t ed cont ent
on your sit e.

W h y doe sn ’t D j a n go j u st do t h is for you ?


Modifying Dj ango t o aut om at ically escape all variables displayed in t em plat es is a frequent t opic of discussion on t he
Dj ango developer m ailing list .

So far, Dj ango’s t em plat es have avoided t his behavior because it subt ley and invisibly changes what should be
relat ively st reight forward behavior ( displaying variables) . I t ’s a t ricky issue and a difficult t rade- off t o evaluat e.
Adding hidden im plicit behavior is against Dj ango’s core ideals ( and Pyt hon’s, for t hat m at t er) , but securit y is
equally im port ant .

All t his t o say, t hen, t hat t here’s a fair chance t hat Dj ango will grow som e form of aut o- escaping ( or nearly- aut o-
escaping) behavior in t he fut ure. I t ’s always a good idea t o check t he official Dj ango docum ent at ion; it ’ll always be
m ore up- t o- dat e t han t his book ( especially t he dead- t ree version) .

Even if Dj ango does add t his feat ure, however, you should st ill be in t he habit of t hinking “ where does t his dat a
com e from ?” at all t im es. No aut om at ic solut ion will ever prot ect your sit e from XSS at t acks 100% of t he t im e.

Cross-sit e Request Forgery (CSRF)


CSRF happens when a m alicious Web sit e t ricks a user int o unknowingly loading a URL from a sit e at which t hey’re already
aut hent icat ed — hence, t aking advant age of t heir aut hent icat ed st at us.

Dj ango has built - in t ools t o prot ect from t his kind of at t ack; bot h t he at t ack it self and t hose t ools are covered in great det ail in
Chapt er 15.

Session f orging/ hij acking


This isn’t a specific at t ack, but rat her a general class of at t acks on a user’s session dat a. I t can t ake a num ber of different form s:

● A m a n - in - t h e - m iddle at t ack, where an at t acker snoops on session dat a as it t ravels over t he wire ( or wireless) net work.

● Se ssion for gin g, where an at t acker uses a fake session I D ( perhaps obt ained t hrough a m an- in- t he- m iddle at t ack) t o
pret end t o be anot her user.

An exam ple of t his first t wo would be an at t acker in a coffee shop using t he wireless net work t o capt ure a session cookie; he

file:///D|/books/computer/programming/python/books/DJANGO BOOK/20.html (3 of 6)9/28/2007 2:32:01 PM


Chapter 20: Security

could t hen use t hat cookie t o im personat e t he original user.

● A cook ie for gin g at t ack, where an at t acker overrides t he supposedly read- only dat a st ored in a cookie. Chapt er 12 explains
in det ails how cookies work, and one of t he salient point s is t hat it ’s t rivial for browsers and m alicious users t o change
cookies wit hout your knowledge.

There’s a long hist ory of web sit es t hat have st ored a cookie like IsLoggedIn=1 or even LoggedInAsUser=jacob; it ’s alm ost
t oo easy t o exploit t hese t ypes of at t ackers.

On a m ore subt le level, t hough, it ’s never a good idea t o t rust anyt hing st ored in a cookie; you never know who’s been
poking at t hem .

● Se ssion fix a t ion , where an at t acker t ricks a user int o set t ing or reset ing t heir session I D.

For exam ple, PHP allows session ident ifiers t o be passed in t he URL ( i.
e. http://example.com/?PHPSESSID=fa90197ca25f6ab40bb1374c510d7a32) . An at t acker who t ricks a user int o clicking on a
link wit h a hardcoded session I D will cause t he user t o pick up t hat session.

This has been used in phishing at t acks t o t rick users int o ent ering personal inform at ion int o t he an account which t he
at t acker owns; he can lat er log int o t hat account and ret rieve t he dat a.

● Se ssion poison in g, where an at t acker inj ect s pot ent ially dangerous dat a int o a user’s session — usually t hrough a web
form t hat t he user subm it s t o set session dat a.

A canonical exam ple is a sit e t hat st ores a sim ple user preference ( like a page’s background color) in a cookie. An at t acker
could t rick a user int o clicking on a link t o subm it a “ color” t hat act ually cont ains an XSS at t ack; if t hat color isn’t escaped
( see above) t he user could again inj ect m alicious code int o t he user’s environm ent .

The solution
There are a num ber of general principles t hat can prot ect from t hese at t acks:

● Never allow session inform at ion t o be cont ained in t he URL.

Dj ango’s session fram ework ( see Chapt er 12) sim ply doesn’t allow sessions t o be cont ained in t he URL.

● Don’t st ore dat a in cookies direct ly; inst ead, st ore a session I D t hat m aps t o session dat a st ored on t he backend.

I f you use Dj ango’s built - in session fram ework ( i.e. request.session) , t his is handled aut om at ically for you. The only cookie
t hat t he session fram ework uses is a single session I D; all t he session dat a is st ored in t he dat abase.

● Rem em ber t o escape session dat a if you display it in t he t em plat e. See t he XSS sect ion above, and rem em ber t hat it applies
t o any user- creat ed cont ent . You should t reat session inform at ion as user- creat ed.

● Prevent at t ackers from spoofing session I Ds whenever possible.

Alt hough it ’s nearly im possible t o det ect som eone who’s hij acked a session I D, Dj ango does have built - in prot ect ion against
a brut e- force session at t ack. Session I Ds are st ored as hashes ( inst ead of sequent ial num bers) which prevent s a brut e- force
at t ack, and a user will always get a new session I D if t hey t ry a non- exist ent one which prevent session fixat ion.

Not ice t hat none of t hose principles and t ools prevent m an- in- t he- m iddle at t acks. These t ypes of at t acks are nearly im possible t o
det ect . I f your sit e allows logged- in users t o see any sort of sensit ive dat a, you should always serve t hat sit e over HTTPS.
Addit ionally, if you’ve got an SSL- enabled sit e, you should set t he SESSION_COOKIE_SECURE set t ing t o True; t his will m ake Dj ango
only send session cookies over HTTPS.

E-mail header inj ect ion


SQL inj ect ion’s less- well- known sibling, e - m a il h e a de r in j e ct ion hij acks em ail- sending web form s and uses t hem t o send spam .
Any form t hat const ruct s em ail headers from web form dat a is a t arget for t his kind of at t ack.

Let ’s look at t he canonical cont act form found on m any sit es. Usually t his em ails a hard- coded em ail address, and so at first
glance doesn’t appear vulnerable t o spam abuse.

However, m ost of t hese form s also allow t he user t o t ype in his own subj ect for t he em ail ( along wit h a from address, body, and
som et im es a few ot her fields) . This subj ect field is used t o const ruct t he “ subj ect ” header of t he em ail m essage.

I f t hat header is unescaped when building t he em ail m essage, an at t acker could use som et hing
like "hello\ncc:spamvictim@example.com" ( where "\n” is a newline charact er) . That would m ake t he const ruct ed em ail headers
t urn int o:

To: hardcoded@example.com
Subject: hello
cc: spamvictim@example.com

Like SQL inj ect ion, if we t rust t he subj ect line given by t he user, we’ll allow him t o const ruct a m alicious set of headers, and t hey

file:///D|/books/computer/programming/python/books/DJANGO BOOK/20.html (4 of 6)9/28/2007 2:32:01 PM


Chapter 20: Security

can use our cont act form t o send spam .

The solution
We can prevent t his at t ack in t he sam e way we prevent SQL inj ect ion: always escape or validat e user- subm it t ed cont ent .

Dj ango’s built - in m ail funct ions ( in django.core.mail) sim ply do not allow newlines in any fields used t o const ruct headers ( t he
from and t o addresses and t he subj ect ) . I f you t ry t o use django.core.mail.send_mail wit h a subj ect t hat cont ains newlines,
Dj ango will raise a BadHeaderError except ion.

I f you decide t o use ot her m et hods of sending em ail, you’ll need t o m ake sure t hat newlines in headers eit her cause an error or
are st ripped. You m ay want t o exam ine t he SafeMIMEText class in django.core.mail t o see how Dj ango does t his.

Direct ory t raversal


D ir e ct or y t r a ve r sa l is anot her inj ect ion- st yle at t ack wherein a m alicious user t ricks filesyst em code int o reading and/ or writ ing
files t hat t he web server shouldn’t have access t o.

An exam ple m ight be a view t hat reads files from t he disk wit hout carefully sanit izing t he file nam e:

def dump_file(request):
filename = request.GET["filename"]
filename = os.path.join(BASE_PATH, filename)
content = open(filename).read()

# ...

Thought it looks like t hat view rest rict s file access t o files beneat h BASE_PATH ( by using os.path.join) , if t he at t acker passes in
a filename cont aining .. ( t hat ’s t wo periods, t he UNI X short hand for “ t he parent direct ory” ) , he can access files
“ above” BASE_PATH. I t ’s only a m at t er of t im e before he can discover t he correct num ber of dot s t o successfully access,
say, ../../../../../etc/passwd.

Anyt hing t hat reads files wit hout proper escaping is vulnerable t o t his problem . Views t hat writ e files are j ust as vulnerable, but
t he consequences are doubly dire.

Anot her perm ut at ion of t his problem lies in code t hat dynam ically loads m odules based on t he URL or ot her request inform at ion.
A well- publicized exam ple cam e from t he world of Ruby on Rails. Prior t o m id- 2006, Rails used URLs
like http://example.com/person/poke/1 direct ly t o load m odules and call m et hods. The result was t hat a carefully- const ruct ed
URL could aut om at ically load arbit rary code, including a dat abase reset script !

The solution
I f your code ever needs t o read or writ e files based on user input , you need t o very carefully sanit ize t he request ed pat h t o
ensure t hat an at t acker isn’t able t o escape from t he base direct ory you’re rest rict ing access t o.

N ot e
Needless t o say, you should n e ve r writ e code t hat can read from any area of t he disk!

A good exam ple of how t o do t his escaping lies in t he Dj ango’s built - in st at ic cont ent serving view ( in django.views.static) .
Here’s t he relevant code:

import os
import posixpath

# ...

path = posixpath.normpath(urllib.unquote(path))
newpath = ''
for part in path.split('/'):
if not part:
# strip empty path components
continue

drive, part = os.path.splitdrive(part)


head, part = os.path.split(part)
if part in (os.curdir, os.pardir):
# strip '.' amd '..' in path
continue

newpath = os.path.join(newpath, part).replace('\\', '/')

file:///D|/books/computer/programming/python/books/DJANGO BOOK/20.html (5 of 6)9/28/2007 2:32:01 PM


Chapter 20: Security

Dj ango it self doesn’t read files ( unless you use t he static.serve funct ion, but t hat ’s prot ect ed wit h t he code shown above) , so
t his vulnerabilit y doesn’t affect t he core code m uch.

I n addit ion, t he use of t he URLconf abst ract ion m eans t hat Dj ango will never load code you’ve not explicit ly t old it t o load. There’s
no way t o creat e a URL t hat causes Dj ango t o load som et hing not m ent ioned in a URLconf.

Exposed error messages


During developm ent , being able t o see t racebacks and errors live in your browser is ext rem ely useful. Dj ango has “ pret t y” and
inform at ive debug m essages specifically t o m ake debugging easier.

However, if t hese errors get displayed once t he sit e goes live, t hey can som et im es unint ent ionally reveal aspect s of your code or
configurat ion t hat could aid an at t acker.

Furt herm ore, errors and t racebacks aren’t at all useful t o end users. Dj ango’s philosophy is t hat sit e visit ors should never see
applicat ion- relat ed error m essages. I f your code raises an unhandled except ion, a sit e visit or should not see t he full t raceback —
or any hint of code snippet s or Pyt hon ( program m er- orient ed) error m essages. I nst ead, t he visit or should see a friendly “ This
page is unavailable” m essage.

Nat urally, of course, developers need t o see t racebacks t o debug problem s in t heir code. So t he fram ework should hide all error
m essages from t he public, but it should display t hem t o t he t rust ed sit e developers.

The solution
Dj ango has a sim ple flag t hat cont rols t he display of t hese error m essages. I f t he DEBUG set t ing is set t o True, error m essages will
be displayed in t he browser. I f not , Dj ango will render ret urn a HTTP 500 ( “ int ernal server error” ) m essage and render an error
t em plat e t hat you provide. This error t em plat e is called 500.html, and should live in t he root of one of your t em plat e direct ories.

Since developers st ill need t o see errors generat ed on a live sit e, any errors handled t his way will send an em ail wit h t he full
t raceback t o any addresses given in t he ADMINS set t ing.

Users deploying under Apache and m od_pyt hon should also m ake sure t hey have PythonDebug Off in t heir Apache conf files;
t his will ensure t hat any errors t hat occur before Dj ango’s had a chance t o load won’t be displayed publicly.

A f inal word
Hopefully all t his t alk of securit y problem s isn’t t oo int im idat ing. I t ’s t rue t hat t he web can be a wild and wooly world, but wit h a
lit t le bit of foresight you can have an incredibly secure websit e.

Keep in m ind t hat web securit y is a const ant ly changing field; if you’re reading t he dead- t ree version of t his book, be sure t o
check m ore up- t o- dat e securit y resources for any new vulnerabilit ies t hat have been discovered. I n fact , it ’s always a good idea
t o spend som e t im e each m ont h or week researching and keeping current on t he st at e of web applicat ion securit y. I t ’s a sm all
invest m ent t o m ake, but t he prot ect ion you’ll get for your sit e and your users is priceless.

D id w e m iss a n yt h in g? Are t here ot her securit y vulnerabilit ies you t hink we should cover in t his chapt er? Did we get som et hing
wrong ( $DEITY forbid) ? Leave a not e on t his paragraph and let us know!

« previous ◊ t able of cont ent s ◊ next »


Copyright 2006 Adrian Holovat y and Jacob Kaplan- Moss.
This work is licensed under t he GNU Free Docum ent License.

file:///D|/books/computer/programming/python/books/DJANGO BOOK/20.html (6 of 6)9/28/2007 2:32:01 PM

Вам также может понравиться