0506 Django Web Framework For Python PDF
0506 Django Web Framework For Python PDF
bir2su.blogspot.com
Chapt er 3: The basics of generat ing Web pages Novem ber 6, 2006
Chapt er 5: I nt eract ing wit h a dat abase: m odels Novem ber 13, 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 15: Ot her cont ribut ed sub- fram eworks Decem ber 18, 2006
Chapt er 17: I nt egrat ing wit h legacy dat abases and applicat ions Decem ber 25, 2006
Chapt er 19: I nt ernat ionalizat ion and localizat ion January 8, 2007
“ 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.
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 .
● 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.
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
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.
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.
By cont rast , Dj ango decouples URLs from callback code, so you can change t he URLs for a given piece of code.
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.
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:
On a m ore serious not e, Dj ango does not yet reverse t he effect s of global warm ing.
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 / ) .
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
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.
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.
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:
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.
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:
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 .
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.”
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.
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.
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.
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/ .
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.
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.
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.
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.)
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
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.
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.
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.
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 :
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:
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.
This chapt er int roduces how t o creat e dynam ic Web pages wit h Dj ango.
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 :
def current_datetime(request):
now = datetime.datetime.now()
html = "<html><body>It is now %s.</body></html>" % now
return HttpResponse(html)
● 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.)
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
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:
urlpatterns = patterns('',
# Example:
# (r'^mysite/', include('mysite.apps.foo.urls.foo')),
● 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.)
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.
● 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
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.
● 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.
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/
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 .
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.
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:
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.)
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
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:
urlpatterns = patterns('',
(r'^now/$', current_datetime),
(r'^now/plus(\d{1,2})hours/$', hours_ahead),
)
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?
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:
● 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).
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:
def current_datetime(request):
now = datetime.datetime.now()
html = "<html><body>It is now %s.</body></html>" % now
return HttpResponse(html)
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:
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/.
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:
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.
(r'^now/minus(\d{1,2})hours/$', hours_behind),
(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:
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…
● 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 .
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>
<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.
● 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
I f you’re following along int eract ively, you’ll see som et hing like t his aft er t yping print t:
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:
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:
The syst em raises a TemplateSyntaxError except ion for any of t he following cases:
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:
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:
... {% 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>"
● 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.
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:
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.
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 :
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:
Dot s are also used t o access list indices. For exam ple:
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:
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:
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()) :
● 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:
● 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:
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
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.”
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.
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 %}
{% 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 %} 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:
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:
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 %}.
<ul>
{% for athlete in athlete_list %}
<li>{{ athlete.name }}</li>
{% endfor %}
</ul>
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:
● 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.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:
● 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:
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:
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:
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:
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:
{# This is a comment #}
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 ) :
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.
{{ 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.
● 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" }}
● 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:
● 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) .
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.
● 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:
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:
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:
● 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) :
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',
)
● 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'),
)
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:
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.
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:
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.
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.
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.
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" %}
{% 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.
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:
<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:
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:
<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:
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
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.
● 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:
Tem plat e 2:
Tem plat e 3:
<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:
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:
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.
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:
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.
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.
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:
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.
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
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:
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 %}
{% 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.
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 .)
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:
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 :
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.
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.
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 = ''
● 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:
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:
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:
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:
( 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.
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 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
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.
● 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:
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
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) :
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.
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:
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.
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) :
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:
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 .
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.
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):
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:
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.
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.
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.
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:
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.
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.
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 :
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:
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:
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:
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.
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:
● 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.
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 .
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.
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.
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:
● 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.
URLconf t ricks
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:
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:
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:
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.
● 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.
● 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.
Old:
urlpatterns = patterns('',
(r'^/?$', 'mysite.views.archive_index'),
(r'^(\d{4})/([a-z]{3})/$', 'mysite.views.archive_month'),
(r'^tag/(\w+)/$', 'weblog.views.tag'),
)
New:
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.
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 :
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.
urlpatterns = patterns('',
(r'^articles/(\d{4})/$', views.year_archive),
(r'^articles/(\d{4})/(\d{2})/$', views.month_archive),
)
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:
Wit h nam ed groups, t hough, t he sam e request would result in t his funct ion call:
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.
● 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.
# urls.py
urlpatterns = patterns('',
(r'^foo/$', views.foo_view),
(r'^bar/$', views.bar_view),
)
# views.py
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
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
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
urlpatterns = patterns('',
(r'^foo/$', views.foobar_view, {'template_name': 'template1.html'}),
(r'^bar/$', views.foobar_view, {'template_name': 'template2.html'}),
)
# views.py
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:
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:
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.
def say_hello(person_name):
print 'Hello, %s' % person_name
def say_goodbye(person_name):
print 'Goodbye, %s' % 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.”
# urls.py
# views.py
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
urlpatterns = patterns('',
(r'^events/$', views.object_list, {'model': models.Event}),
(r'^blog/entries/$', views.object_list, {'model': models.BlogEntry}),
)
# views.py
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.
● 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.
● 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.
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.
# urls.py
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^blog/$', views.page),
(r'^blog/page(?P<num>\d+)/$', views.page),
)
# views.py
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
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:
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:
…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.)
(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:
# urls.py
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)
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.
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.
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.
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.
● /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.
# root urls.py
urlpatterns = patterns('',
(r'^(?P<username>\w+)/blog/', include('foo.urls.blog')),
)
# foo/urls/blog.py
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.
For exam ple, t hese t wo URLconf set s are funct ionally ident ical:
Set one:
# urls.py
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
urlpatterns = patterns('',
(r'^blog/', include('inner')),
)
# inner.py
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.
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.
● 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.
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 :
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.
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.
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.
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.
● 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.
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.
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:
urlpatterns = patterns('',
# We'll add URL patterns here.
)
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) :
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
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:
● 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:
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",
}
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.
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'.
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.
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 = {
"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) :
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:
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.
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.
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:
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 .
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 ?
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) .
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.
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:
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) .
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.
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.
(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
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'.
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.
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.
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.
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:
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 .
See Chapt er 7 for m ore inform at ion about working wit h form s.
Example
Following t he above exam ple, we could provide an edit int erface for a single book wit h t his URLconf snippet :
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 ) .
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
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:
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.
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:
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:
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.
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.
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:
urlpatterns = patterns('',
#...
(r'^authors/(?P<author_id>d+)/$', author_detail),
)
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)
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.
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.
{% 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.
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.
● 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:
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.
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:
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:
The t em plat e syst em uses t he first lookup t ype t hat works; it ’s short - circuit logic.
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.
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:
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.
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.
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.
Here’s an exam ple of how t hese m ult iple levels m ight work:
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.
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, {
'foo': 'bar',
}, processors=[ip_address_processor])
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.
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:
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",
)
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.
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
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
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
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.
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.
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:
register = template.Library()
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.
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.
{{ 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:
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)
@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.
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.
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.
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:
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 :
● 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 .
import datetime
class CurrentTimeNode(template.Node):
def __init__(self, format_string):
self.format_string = format_string
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.
register.tag('current_time', do_current_time)
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.
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):
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.
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:
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
class CurrentTimeNode3(template.Node):
Now, do_current_time() grabs t he form at st ring and t he variable nam e, passing bot h t o CurrentTimeNode3.
To creat e a t em plat e t ag like t his, use parser.parse() in your com pilat ion funct ion.
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.
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 %}
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):
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.
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)
@register.simple_tag
def current_time(token):
...
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 .
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 %}
<ul>
<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.
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 .
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)
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 .
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", [])
# 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.
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:
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.
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 .
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 :
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.
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:
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:
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 .
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
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:
I f t hat com m and doesn’t raise any errors, t he inst allat ion worked.
# 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.")
● 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) .
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'
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
● 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 .
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.
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) .
Initialization
To act ivat e syndicat ion feeds on your Dj ango sit e, add t his line t o your URLconf:
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:
urlpatterns = patterns('',
# ...
(r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed',
{'feed_dict': feeds}),
# ...
)
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:
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]
● 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 .
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.
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 }}
{{ obj.description }}
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:
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:
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])
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/ .
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:
def items(self):
return Song.objects.all()[:30]
def item_enclosure_url(self, item):
return item.song_url
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.
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
feeds = {
'rss': RssSiteNewsFeed,
'atom': AtomSiteNewsFeed,
}
urlpatterns = patterns('',
# ...
(r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed',
{'feed_dict': feeds}),
# ...
)
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.
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:
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:
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:
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) .
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.
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().
❍ 'always'
❍ 'hourly'
❍ 'daily'
❍ 'weekly'
❍ 'monthly'
❍ 'yearly'
❍ 'never'
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.
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) :
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})
)
Here is what t he relevant URLconf lines would look like for t he exam ple above:
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.
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.
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!
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
...
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.
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.")
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"])
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:
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.
● 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.
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:
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.
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 login(request):
m = members.get_object(username__exact=request.POST['username'])
if m.password == request.POST['password']:
request.session['member_id'] = m.id
return HttpResponse("You're logged in.")
else:
return HttpResponse("Your username and password didn't match.")
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.
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.
def login(request):
# 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.
>>> 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}
# Session is modified.
request.session['foo'] = 'bar'
# Session is modified.
del request.session['foo']
# Session is modified.
request.session['foo'] = {}
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 .
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.
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.
● 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.
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) .
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 .
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:
myuser.groups.clear()
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:
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:
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:
Not e t hat logout() doesn’t t hrow any errors if t he user wasn’t logged in.
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 :
urlpatterns = patterns('',
# existing patterns here...
(r'^accounts/login/$', login)
(r'^accounts/logout/$', logout)
/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 %}
{% 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 .
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:
def my_view(request):
if not request.user.is_authenticated():
return HttpResponseRedirect('/login/?next=%s' % request.path)
# ...
def my_view(request):
if not request.user.is_authenticated():
return render_to_response('myapp/login_error.html')
# ...
@login_required
def my_view(request):
# ...
● 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.
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?
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:
@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/'.
You can, of course, replace login_required wit h any of t he ot her lim it ing decorat ors.
However, t here are low- level API s you can delve int o when you need absolut e cont rol.
Creating users
The basic way t o creat e users is t o use t he create_user helper funct ion:
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:
Changing passwords
You can change a password wit h set_password():
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.
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.
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:
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 }}
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 }}:
{% 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.
{% 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 %}
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.
● 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
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:
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 :
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:
class MySiteProfile(models.Model):
# This is the only required field
user = models.ForeignKey(User, unique=True)
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.
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.
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:
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.
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
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.
…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.
CACHE_BACKEND = 'locmem:///'
CACHE_BACKEND = 'simple:///'
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:
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.
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.
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.”
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.
● 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.
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:
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 .
For exam ple, if an art icle is only allowed on a single sit e, you’d use a m odel like t his:
class Article(models.Model):
headline = models.CharField(maxlength=200)
# ...
site = models.ForeignKey(Site)
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:
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:
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.
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.
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:
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.
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:
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.
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:
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.
● 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:
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:
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)
● 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.
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 .
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 .
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/.”
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:
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 .
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.
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:
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.
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.
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
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 .
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):
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.
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',
'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.
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.
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.
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.
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.
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.
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 .
“ Common” middleware
Middleware class: django.middleware.common.CommonMiddleware.
● 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.
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
request s.
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.
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 .
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.
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:
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.
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.
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.
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:
● 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:
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.
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.
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.
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
● 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
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:
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" %}
{% 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.
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.
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:
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:
@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 :
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.
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 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:
you need.
We’ll close out t his chapt er wit h som e ideas for cust om adm in views:
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!
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.
● 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.
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.
You’ll probably also want t o rem ove 'django.core.context_processors.i18n' from your TEMPLATE_CONTEXT_PROCESSORS
set t ing.
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:
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:
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 .
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.
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:
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:
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:
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 :
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:
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:
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:
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:
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:
I nt ernally, all block and inline t ranslat ions use t he appropriat e gettext / ngettext call.
● 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
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 %}
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:
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.
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.
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 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."
msgstr ""
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
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
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.
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 :
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:
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.
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.
(r'^i18n/', include('django.conf.urls.i18n')),
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 :
● 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
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.
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 .
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.
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
as:
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) .
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.
● 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.
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.
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 ?” .
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 .
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:
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';
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) .
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:
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:
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.
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:
<h1>Hello, Jacob!</h1>
<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:
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.
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.
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.
● 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
● 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:
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.
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.
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:[email protected]" ( where "\n” is a newline charact er) . That would m ake t he const ruct ed em ail headers
t urn int o:
To: [email protected]
Subject: hello
cc: [email protected]
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
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.
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
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.
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!