První aplikace v Djangu, část 1

V tomto návodu si ukážeme, jak vytvořit jednoduchou anketní aplikaci.

Bude se skládat ze dvou částí:

  • Veřejná stránka, na které bude možné prohlížet anketní otázky a hlasovat v nich.
  • Administrátorská část, na které bude možné přidávat, upravovat a mazat ankety.

Předpokládejme, že máte Django již nainstalované. Můžete to zjistit spuštěním interaktivního interpretera Pythonu a napsáním import django. Pokud příkaz proběhne bez chyb, Django je nainstalováno.

Kde hledat pomoc:

Pokud narazíte na nějaké potíže, pošlete dotaz do:

Vytváření projektu

Pokud s Djangem začínáte, budete se muset postarat o některá počáteční nastavení. Přesněji, necháte si vygenerovat kód, který založí nový project (projekt).

Na příkazové řádce se přesuňte do adresáře (cd), kde chcete mít uložený svůj zdrojový kód a potom spusťte příkaz django-admin.py startproject mysite. Tímto způsobem se v aktuálním adresáři vytvoří adresář mysite.

Práva v systému Mac OS X

Pokud používáte Mac OS X, můžete se setkat při spuštění příkazu django-admin.py startproject se zprávou “permission denied” (“přístup odepřen”). To proto, že v systémech založených na Unixu (tj. i na Mac OS X), musí být soubor označen jako “spustitelný”, aby jej bylo možno spustit jako program. Otevřete si Terminal.app, pomocí příkazu cd se dostaňte do adresáře, kde je nainstalován django-admin.py a spusťte příkaz chmod +x django-admin.py.

Poznámka

Projektům nesmíte dávat stejné názvy, jaké mají vestavěné (built-in) komponenty Pythonu nebo Djanga. Například nemůžete použít jméno django (které by vytvářelo konflikt s Djangem samotným), nebo test (které by vytvářelo konflikt s vestavěnou Python knihovnou).

Pokud jste Django instalovali pomocí příkazu python setup.py, měl by se django-admin.py nacházet na vaší systémové cestě. Jestliže tam není, najdete jej v site-packages/django/bin, přičemž site-packages je adresář ve vaší instalaci Pythonu. V případě, že váš systém příkaz django-admin.py nenajde, vytvořte na něj symbolický odkaz z nějakého místa, které leží na systémové cestě, např /usr/local/bin, apod.

Kde by měl být samotný kód?

Pokud používáte PHP, asi jste zvyklí ukládat kód do kořenového adresáře web serveru (jako třeba /var/www). U Djanga se to nedělá. Ukládat jakýkoli kód psaný v Pythonu do kořenového adresáře web serveru není dobrý nápad, protože by pak kdokoliv mohl číst váš zdrojový kód přes internet (a to by jste nechtěli, že ne?).

Ukládejte váš kód někam mimo kořenový adresář, třeba do adresáře :file:/home/mycode.

Podívejme se, co příkaz startproject vytvořil:

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

Popišme si význam jednotlivých souborů:

  • __init__.py: Prázdný soubor, který říká Pythonu, že tento adresář má být považován za Python balíček. (Pokud s Pythonem začínáte, přečtěte si více o balíčcích v oficiální dokumentaci).
  • manage.py: Utilita pro příkazový řádek, která vám umožňuje projekt spravovat. Podrobnosti naleznete v django-admin.py and manage.py.
  • settings.py: Nastavení/konfigurace pro váš Django projekt. Na stránce Django settings se dozvíte vše podstatné.
  • urls.py: Deklarace URL adres pro váš Django projekt; “rejstřík” vašeho webu. Podrobnosti viz URL dispatcher.

Vývojový server

Ověřme si, jestli výše uvedený příkaz správně zafungoval. Přesuňte se do adresáře mysite (pokud tam ještě nejste) a spusťte příkaz python manage.py runserver. Na příkazové řádce uvidíte následující výstup:

Validating models...
0 errors found.

Django version 1.0, using settings 'mysite.settings'
Development server is running at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

Právě jste spustili vývojový server Djanga, odlehčený web server, napsaný čistě v Pythonu. Zahrnuli jsme jej v Djangu, takže díky němu můžete okamžitě vyvíjet a nemusíte se starat o konfiguraci produkčního serveru — jako je např. Apache — dokud nebudete připraveni vaše stránky zveřejnit.

Zde je dobré poznamenat: Vývojový server nepoužívejte v ostrém provozu — tento server je určen pouze pro vývoj (vyrábíme frameworky, ne web servery).

Teď, když nám server běží, navštivte ve vašem prohlížeči adresu http://127.0.0.1:8000/. Uvidíte stránku “Welcome to Django” ve velmi přítulné, světle modré barvě. Fachá to!

Změna portu

Standardně příkaz runserver spouští vývojový server na interní IP a portu 8000.

Pokud chcete změnit port serveru, napište jej jako parametr příkazu. Např. tento příkaz spustí server na portu 8080:

python manage.py runserver 8080

Pokud chcete změnit IP adresu, na které bude server pověšený, zadejte ji společně s číslem portu. Jednoduchým způsobem je možné zařídit, aby server poslouchal na všech veřejných IP adresách (může se hodit v případech, kdy chcete své dílo ukázat na ostatních počítačích v síti):

python manage.py runserver 0.0.0.0:8080

Kompletní dokumentaci k vývojovému serveru můžete najít v referenci k příkazu runserver.

Nastavení databáze

Je na čase upravit soubor settings.py. Soubor settings.py je obyčejný Python modul s proměnnými, kterými se konfiguruje Django. Změnte následující klíče u záznamu DATABASES ‘default’ tak, aby vyhovovaly parametrům vaší databáze:

  • ENGINE — Vyberte ‘django.db.backends.postgresql_psycopg2’, ‘django.db.backends.mysql’ nebo ‘django.db.backends.sqlite3’. Django podporuje i další databázové backendy.

  • NAME — Jméno vaší databáze. Pokud používáte SQLite, uveďte zde absolutní cestu (včetně jména souboru) k databázovému souboru (v případě, že soubor neexistuje, bude vytvořen při první synchronizaci, viz níže).

    Při uvádění cesty vždy používejte dopředné lomítka (/), a to i v systému Windows (např. C:/homes/user/mysite/sqlite3.db).

  • USER — Vaše databázové uživatelské jméno (u SQLite se nepoužívá).

  • PASSWORD — Vaše databázové heslo (u SQLite se nepoužívá).

  • HOST — Počítač, na kterém jede databáze. Pokud je na stejném počítači jako Django, nechte řetězec prázdný. (u SQLite se nepoužívá)

Jestliže se v databázích neorientujete, vyberte si SQLite (tj. nastavte ENGINE na ‘sqlite3’). SQLite je součástí Pythonu od verze 2.5, takže se nemusí nic instalovat.

Poznámka

Pokud používáte PostgreSQL nebo MySQL, ujistěte se, že databáze již existuje. Pokud ne, vytvoříte ji příkazem “CREATE DATABASE jmeno_databaze;” v interaktivní databázové příkazové řádce.

Pokud používáte SQLite, nemusíte dělat nic — databázový soubor bude automaticky vytvořen až bude třeba.

Když už editujete settings.py, všimněte si proměnné INSTALLED_APPS ve spodní části souboru. Tato proměnná udržuje informace o všech aktivních aplikacích pro tuto instanci Djanga. Aplikace mohou být použity ve více projektech, můžete vytvářet jejich balíčky a nabízet je ostatním vyvojářům (kteří je pak začleňují do svých projektů).

Standardně INSTALLED_APPS obsahuje tyto aplikace:

Uvedené aplikace jsou součástí Djanga, protože jde o běžně používané komponenty při vývoji webů.

Každá z uvedených aplikací používá alespoň jednu databázovou tabulku. Než je začneme používat, musíme pro ně v databázi tabulky vytvořit:

python manage.py syncdb

Příkaz syncdb čte proměnnou INSTALLED_APPS a vytvoří v databázi všechny potřebné tabulky. Po jeho spuštění uvidíte zprávu o vytvoření každé z tabulek a dotaz, jestli chcete vytvořit účet superuživatele pro autentifikační systém (odpovězte “yes” a účet vytvořte).

Pokud vás zajímá, jaké tabulky Django vytvořilo, spusťte interaktivní databázovou příkazovou řádku a napište \dt (PostgreSQL), SHOW TABLES; (MySQL), nebo .schema (SQLite).

Poznámka pro minimalisty (a nihilisty)

Jak už bylo řečeno, Django obsahuje sadu standardních aplikací, ale ne všichni je potřebují (např. nihilisti). Pokud si myslíte, že jsou vám na nic, s klidem zakomentujte nebo vymažte patřičný řádek z proměnné INSTALLED_APPS než použijete příkaz syncdb. Příkaz syncdb vytvoří tabulky pouze pro aplikace uvedené v INSTALLED_APPS.

Vytváření modelů

Tak. Konečně máme vše ponastavováno — “projekt” — a můžem začít dělat něco kloudného.

Každá aplikace, kterou v Djangu napíšete, se skládá z Python balíčku umístěného na některé z cest v PYTHONPATH. Django obsahuje utilitu, která vám pomůže vygenerovat základní adresářovou strukturu nové aplikace.

Projekty kontra aplikace

Jaký je rozdíl mezi projektem a aplikací? Aplikace je webový kód, který něco dělá — např. blogovací systém, databáze veřejných záznamů nebo jednoduchá anketa. Projekt je soubor aplikací a nastavení, který funguje na konkrétní doméně. Projekt může obsahovat více aplikací. Aplikace může být ve více projektech.

V tomto návodu vytvoříme anketní aplikaci, kterou pro zjednodušení umístíme přímo do adresáře mysite. Důsledkem tohoto zjednodušení bude vzájemné propojení aplikace a projektu — tj. Python kód se v anketní aplikaci bude odkazovat na adresář mysite.polls. Později si ukážeme, jak aplikaci od projektu oddělit a umožnit jeho distribuci.

Pro vytvoření aplikace se ujistěte, že se nacházíte v adresáři mysite a zadejte tento příkaz:

python manage.py startapp polls

Vytvoří se adresář polls s následující strukturou:

polls/
    __init__.py
    models.py
    views.py

Tato struktura se stane domovem naší anketní aplikace.

Prvním krokem při vytváření databázové Django aplikace je definice modelů — tj. struktury databáze s dodatečnými metadaty.

Filozofie

Model je jedinečným popisem vašich dat. Obsahuje všechna nezbytná pole a chování ukládaných dat. Django ctí DRY princip (Dont Repeat Yourself — neopakuj se). Cílem je definovat datový model na jediném místě a ostatní věci z něj odvozovat automaticky.

V naší jednoduché anketní aplikaci si vytvoříme dva modely — ankety a její volby. Anketa bude složena z otázky a data zveřejnění. Volba bude mít dvě pole: textový popis a záznam hlasování. Každá volba bude spojena s anketou.

Nyní si slovní popis naší aplikace přepíšeme do jednoduchých Python tříd. Upravte soubor polls/models.py, aby vypadal takto:

from django.db import models

class Poll(models.Model):
    question = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')

class Choice(models.Model):
    poll = models.ForeignKey(Poll)
    choice = models.CharField(max_length=200)
    votes = models.IntegerField()

Uvedený kód je poměrně přímočarý. Každý model je potomkem třídy django.db.models.Model a jeho proměnné definují jednotlivá databázová pole.

Každé pole je reprezentováno instancí třídy Field — např., CharField pro znaková pole a DateTimeField pro datum a čas.

Pojmenováním jednotlivých polí Field (např. question nebo pub_date) se definuje tzv. “strojové” (machine-friendly) jméno. Přes ně budete v Pythonu přistupovat k hodnotě pole a v databázi bude využito pro pojmenování sloupce.

Pokud chcete, můžete pomocí prvního argumentu v Field vytvořit “lidsky čitelné” (human-readable) jméno. Pokud jej nezadáte, Django si ho odvodí ze strojového jména. V našem příkladě jsme “lidské” jméno definovali pouze pro pole Poll.pub_date.

Některé Field třídy mají povinné parametry. Např. CharField vyžaduje specifikaci max_length (maximální délka řetězce). Zadaná hodnota se použije během vytváření databázové tabulky, ale jak si brzy ukážeme, poslouží stejně dobře i pro validaci hodnoty.

Nakonec si všimněte definice vztahu s pomocí výrazu ForeignKey. Ten Djangu říká, že každá volba se vztahuje k jedné anketě. Django podporuje všechny obvyklé databázové relace: M:N (many-to-many), 1:N (many-to-one) a 1:1 (one-to-one).

Aktivace modelů

Uvedený kousek kódu poskytuje Djangu spoustu informací o modelu. Díky němu Django může:

  • Vytvořit databázové schéma (výrazy CREATE TABLE) pro naši aplikaci.
  • Vytvořit databázové API v Pythonu pro přístup k objektům Poll (anketa) a Choice (volba).

Ale nejprve musíme našemu projektu říct, že aplikace polls je nainstalována.

Filozofie

Django aplikace jsou “zásuvné”: aplikace může být použita ve více projektech a aplikace mohou být distribuovány.

Znovu upravte soubor settings.py a změňte nastavení INSTALLED_APPS tak, aby obsahovalo řetězec ‘mysite.polls’. Takže bude vypadat následovně:

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

Django teď ví, že mysite obsahuje aplikaci polls. Spusťme další příkaz:

python manage.py sql polls

Měli by jste vidět něco podobného (SQL příkazy CREATE TABLE pro naši anketní aplikaci):

BEGIN;
CREATE TABLE "polls_poll" (
    "id" serial NOT NULL PRIMARY KEY,
    "question" varchar(200) NOT NULL,
    "pub_date" timestamp with time zone NOT NULL
);
CREATE TABLE "polls_choice" (
    "id" serial NOT NULL PRIMARY KEY,
    "poll_id" integer NOT NULL REFERENCES "polls_poll" ("id"),
    "choice" varchar(200) NOT NULL,
    "votes" integer NOT NULL
);
COMMIT;

Mějte na paměti:

  • Přesný výstup se bude lišit podle použité databáze.
  • Jména tabulek jsou automaticky generována ze jména aplikace (polls) a jména modelu — poll a choice. (Toto chování můžete změnit.)
  • Primární klíče (ID) jsou přidány automaticky. (Také lze změnit.)
  • Django samo od sebe přilepuje suffix “_id” ke jménům polí s cizími klíči (foreign keys). I toto můžete změnit.
  • Relace foreign key je vytvořena explicitním výrazem REFERENCES.
  • Výstup je přizpůsoben použité databázi, takže typy polí specifické pro databáze, jako auto_increment (MySQL), serial (PostgreSQL), nebo integer primary key (SQLite) jsou automaticky zpracovány. To samé platí i o ohraničování jmen polí do uvozovek — dvojitých nebo jednoduchých. Autor tohoto návodu používá PostgreSQL, takže výstup je psán v syntaxi PostgreSQL.
  • Příkaz sql nespouští SQL v databázi — pouze je vypíše na obrazovku, takže můžete zkontrolovat, co se Django chystá udělat. Pokud chcete, můžete SQL příkazy zkopírovat do databázového interpreteru a nechat provést. Jak ale později uvidíme, existuje pohodlnější varianta…

Pro zajímavost zadejte následující příkazy:

Když si prostudujete jednotlivé výstupy, pomůže vám to pochopit co se děje pod pokličkou.

Teď opět spusťte příkaz syncdb, aby se nám vytvořily potřebné tabulky modelů v databázi:

python manage.py syncdb

Příkaz syncdb spustí ve vaší databázi SQL příkazy z výstupu ‘sqlall’ pro všechny aplikace uvedené v INSTALLED_APPS. Vytvoří tabulky, indexy a naplní je počátečními daty — ale pouze pro ty aplikace, které jste do svého projektu přidali od posledního spuštění syncdb. syncdb můžete volat jak často chcete, a vždy budou vytvořeny jen ty tabulky, které v databázi ještě neexistují.

Další podrobnosti a příkazy utility manage.py načtete v dokumentaci pro django-admin.py.

Hrajeme si s API

Tak. Hupsněme pro změnu do interaktivního shellu Pythonu a pohrajme si s API, které nám Django automaticky vytvořilo. Pro spuštění interpreteru použijte následující příkaz:

python manage.py shell

Místo jednoduchého “python” používáme tento příkaz, protože manage.py nastaví nezbytné prostředí projektu. “Nastavování” zahrnuje dvě věci:

  • Vloží mysite do sys.path. Díky tomu se Django může “dotečkovat” k našemu projektu (např. ‘mysite.polls.models’). Aby to fungovalo, balíček mysite musí ležet na cestě sys.path.

    Podobnou notaci jsme už viděli: nastavení INSTALLED_APPS je vlastně seznam balíčků v tečkovaném zápisu cest.

  • Nastavení proměnné prostředí DJANGO_SETTINGS_MODULE, které Djangu ukáže cestu k souboru settings.py.

Obcházení manage.py

Pokud nechcete používat manage.py, nemusíte. Ujistěte se však, že mysite leží na PYTHONPATH (tj. příkaz import mysite bude fungovat) a nastavte proměnnou prostředí DJANGO_SETTINGS_MODULE na mysite.settings.

Pro více informací nahlédněte do dokumentace django-admin.py.

Jakmile budete v konzoli, vyzkoušejte si databázové API:

>>> from mysite.polls.models import Poll, Choice # Naimportuje modelove tridy, ktere jsme pred chvili napsali.

# V systemu jeste nejsou zadne ankety.
>>> Poll.objects.all()
[]

# Vytvorime novou anketu.
>>> import datetime
>>> p = Poll(question="What's up?", pub_date=datetime.datetime.now())

# Ulozime objekt do databaze. Prikaz save() se musi volat explicitne.
>>> p.save()

# Ted ma anketa ID. Python muze vratit "1L" misto "1" v zavislosti na
# pouzite databazi.
>>> p.id
1

# Hodnoty jednotlivych poli jsou zpristupneny pres atributy objektu.
>>> p.question
"What's up?"
>>> p.pub_date
datetime.datetime(2007, 7, 15, 12, 00, 53)

# Upravte hodnoty zmenou atributu a potom zavolejte save().
>>> p.pub_date = datetime.datetime(2007, 4, 1, 0, 0)
>>> p.save()

# objects.all() zobrazi vsechny ankety v databazi.
>>> Poll.objects.all()
[<Poll: Poll object>]

Počkat. Reprezentace objektu výrazem <Poll: Poll object> je přinejmenším nicneříkající. Napravíme to úpravou modelu anket (v souboru polls/models.py) přidáním metody __unicode__() k Poll i Choice:

class Poll(models.Model):
    # ...
    def __unicode__(self):
        return self.question

class Choice(models.Model):
    # ...
    def __unicode__(self):
        return self.choice

Když __unicode__() nefunguje

Pokud jste k vašim modelům přidali metodu __unicode__() a nevidíte žádnou změnu ve způsobu jejich reprezentace, nejspíš používáte starou verzi Djanga. (Tato verze návodu je napsána pro nejnovější vývojovou verzi Djanga.)

Pokud chcete pracovat se starší verzí Djanga, měli by jste přejít k návodu pro Django 0.96, protože tento návod popisuje některé funkce, které jsou obsaženy pouze ve vývojové verzi.

Metoda __unicode__() je důležitá nejen kvůli přehlednosti při práci v interaktivním režimu, ale také proto, že reprezentace objektů jsou použity v automaticky generovaném administračním rozhraní.

Proč __unicode__() a ne __str__()?

Pokud znáte Python, asi jste si zvykli přidávat k vaším třídám metody django.db.models.Model.__str__() a ne __unicode__(). Používáme __unicode__(), protože Django modely standartně pracují s Unicode. Všechna data ve vaší databázi jsou při čtení převáděna do Unicode.

Django modely mají přednastavenou metodu __str__(), která volá __unicode__() a ta převádí výsledky do UTF-8 bytestringu. Jinými slovy, volání unicode(p) vrátí Unicode řetězec, a str(p) vrátí normální řetězec se znaky kódovanými jako UTF-8.

Pokud je pro vás výše uvedené španělskou vesnicí, zapamatujte si jediné — ke každému modelu přidávejte metodu __unicode__().

Je třeba zdůraznit, že se celou dobu bavíme o normálních metodách Pythonu. Pro ukázku si přidáme ještě jednu vlastní metodu:

import datetime
# ...
class Poll(models.Model):
    # ...
    def was_published_today(self):
        return self.pub_date.date() == datetime.date.today()

Poznámka: import datetime natáhne standardní Pythoní modul datetime.

Uložte tyto změny a nastartujte nový interaktivní shell Pythonu spuštěním python manage.py shell:

>>> from mysite.polls.models import Poll, Choice

# Ujisteme se, ze pridani __unicode__() funguje
>>> Poll.objects.all()
[<Poll: What's up?>]

# Django nabizi rozsahle API pro vyhledavani v databazich
# prostrednictvim keyword argumentu
>>> Poll.objects.filter(id=1)
[<Poll: What's up?>]
>>> Poll.objects.filter(question__startswith='What')
[<Poll: What's up?>]

# Najdi anketu publikovanou v roce 2007.
>>> Poll.objects.get(pub_date__year=2007)
<Poll: What's up?>

>>> Poll.objects.get(id=2)
Traceback (most recent call last):
    ...
DoesNotExist: Poll matching query does not exist.

# Vyhledavani podle primarniho klice se pouziva pomerne casto,
# proto Django nabizi zkratku.
# Nasledujici prikaz je stejny jako Poll.objects.get(id=1).
>>> Poll.objects.get(pk=1)
<Poll: What's up?>

# Ujisteme se, ze nami definovana metoda funguje.
>>> p = Poll.objects.get(pk=1)
>>> p.was_published_today()
False

# Definujme ankete nekolik voleb. Volani create vytvori novy objekt choice,
# provede SQL prikaz INSERT, prida volbu do sady voleb a vrati vysledny
# objekt Choice (volba). Django automaticky vytvori sadu objektu a API
# rozhrani pro pristup k objektum z opacne strany vazby FK (v tomto
# pripade volby ankety).
>>> p = Poll.objects.get(pk=1)

# Zobrazme si volby, ktere jsou propojeny s anketou -- zatim nic.
>>> p.choice_set.all()
[]

# Nyni vytvorme 3 volby.
>>> p.choice_set.create(choice='Not much', votes=0)
<Choice: Not much>
>>> p.choice_set.create(choice='The sky', votes=0)
<Choice: The sky>
>>> c = p.choice_set.create(choice='Just hacking again', votes=0)

# Objekty voleb maji pres API pristup k souvisejicim objektum anket.
>>> c.poll
<Poll: What's up?>

# A naopak: objekty anket maji pristup ke svym volbam.
>>> p.choice_set.all()
[<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]
>>> p.choice_set.count()
3

# API automaticky sleduje relace tak, jak potrebujete.
# Pouzijte dvojite podtrzitka pro oddeleni jednotlivych vztahu.
# Toto funguje tolik urovni do hloubky, kolik chcete; bez omezeni.
# Najdi vsechny volby pro jakoukoli anketu, ktera byla publikovana
# v roce 2007.
>>> Choice.objects.filter(poll__pub_date__year=2007)
[<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]

# Smazme jednu z voleb s pouzitim delete().
>>> c = p.choice_set.filter(choice__startswith='Just hacking')
>>> c.delete()

Veškeré podrobnosti o vazbách mezi modely a databázovém API naleznete na samostatných stránkách.

Až si zvyknete na API, přejděte k druhé části tohoto návodu, kde zprovozníme automatické administrační rozhraní Djanga.