En el capítol anterior, hem explicat com crear un projecte de Django i executar el servidor de desenvolupament. Per descomptat, aquest lloc no fa res que sigui útil -- tot el que fa és mostrar el missatge "it worked!" "(Funciona!). Mirem de canviar-ho.
Aquest capítol explica com crear pàgines web dinàmiques amb Django.
Com a primera fita, crearem una pàgina web que mostre la data i hora actual. Aquest és un bon exemple de pàgina web dinàmica, ja que els continguts de la pàgina no són estàtics -- és a dir, els continguts canvien d'acord amb els resultats d'un càlcul. (en aquest cas, el càlcul de l'hora actual).
Aquest exemple tant simple no involucra cap base de dades ni cap conjunt de dades d'usuari -- només la sortida de rellotge intern del teu servidor.
Per crear aquesta pàgina, escriurem una funció vista. Un funció vista, ó vista per ser més escuets, és una funció simple de Python que agafa una petició web i retorna una resposta web. Aquesta resposta pot ser un contingut HTML o una pàgina web, o una redirecció, o un error 404, o un document XML, o una imatge... o qualsevol cosa. La pròpia vista conté la lògica necessària per retornar aquesta resposta.
Aquí hi ha una vista que retorna la data i hora actual, com un document HTML:
from django.http import HttpResponse
import datetime
def datahora_actual(request):
ara = datetime.datetime.now()
html = "<html><body>Ara són les %s.</body></html>" % ara
return HttpResponse(html)
Anem pas a pas per aquest tros de codi:
Recapitulant, aquesta funció vista retornar una pàgina HTML que inclou la data i hora actual. Però on ha de residir aquest codi? com explicar a Django que l'utilitzi?
La resposta a la primera pregunta és: El codi pot ser on vulguis, tant enllà com el teu propi cami Python. No hi ha cap altre requeriment -- cap manera "màgica" de dir-ho. Per aquest motiu, crearem un fitxer anomenat views.py, copieu el codi en aquest fitxer i guardeu-lo en el directori el_meu_lloc que heu creat en el capítol anterior.
El teu camí Python
El camí Python és el llistat de directoris del teu sistema on Python mira quan utilitzes la clàusula import de Python.Per exemple, diguem que el teu camí Python és ['','/usr/lib/python2.4./site-packages','/home/el_meu_codi']. Si executes el codi Python from foo import bar. Python primer comprova per un mòdul anomenat foo.py en el directori actual. (La primera entrada en el camí de Python, una cadena buida, significa "el directori actual"). Si aquest fitxer no existeix, Python mira pel fitxer /usr/lib/python2.4./site-packages/foo.py. Si aquest tampoc existeix, ho provarà a /home/el_meu_codi/foo.py. Finalment, si tampoc existeix, enviarà un ImportError.
Si estàs interessat per veure el valor del teu camí Python, engega l'intèrpret interactiu de Python i escriu import sys, seguit per print sys.path.
Generalment, no t'hauràs de preocupar de el camí de Python -- Python i Django s'ocupen d'aquestes coses automàticament. (Si ets curiós, configurar el camí Python, és una de les coses que el fitxer manage.py realitza).
Com podria explicar-te la forma en que Django utilitza el codi vista? Aquí és on entra en acció dels URLconfs.
Un URLconf és com una taula de continguts per al teu lloc web fet amb Django, és un mapejat entre els patrons URL i les funcions vista que haurien de cridar-se per aquestes URL. És com li dius a Django "Per aquesta URL, crida aquest codi, i per aquesta URL, crida aquest altre".
Quan executes el django-admin.py startproject del capítol anterior, l'escript crea un URLconf per tu automàticament: el fitxer urls.py. Edita'l. Per defecte, hi ha quelcom semblant a això:
from django.conf.urls.defaults import *
urlpatterns = patterns('',
# Example:
# (r'^mysite/', include('mysite.apps.foo.urls.foo')),
# Uncomment this for admin:
# (r'^admin/', include('django.contrib.admin.urls')),
)
Passem per cada línia d'aquest codi:
La principal cosa per veure és la variable urlpatterns. Aquesta defineix el mapa entre URLs i el codi que les enllaça.
Per defecte, tot està comentat -- la teva aplicació Django està en blanc. (Amb una nota a part, Django sap que ha de mostrar la pàgina "Funciona!" de l'anterior capítol: Si el teu URLconf és buid, Django assumeix que has començat un nou projecte, i et mostra el missatge).
Edita aquest fitxer per mostra la vista datahora_actual:
from django.conf.urls.defaults import *
from el_meu_lloc.views import datahora_actual
urlpatterns = patterns('',
(r'^ara/', datahora_actual),
)
Hem fet dos canvis. Primer, hem importat la vista datahora_actual del mòdul (el_meu_lloc/views.py). Després, afegim la línia (r'^ara/', datahora_actual), . Aquesta línia es refereix a un patróURL -- és una tupla Python en que el primer element és una expressió regular simple i el segon element és la funció vista a utilitzar en aquest patró.
En resum, li hem dit a Django que qualsevol petició a la URL /ara/ ha d'enllaçar amb la funció vista datahora_actual.
Unes quantes coses que cal dir:
Per provar els nostres canvis a la URLconf, engega el servidor de desenvolupament de Django, com s'ha fet en el capítol 1, executant la comanda python manage.py runserver. (si ja el tenies en marxa. El servidor automàticament detecta els canvis en el teux codi Python i el recarrega quan cal, no has de re-iniciar el servidor a cada canvi). El servidor s'executa en l'adreça http://127.0.0.1:8000/, així obre el navegador web i ves a la direcció http://127.0.0.1:8000/ara/ -- i podràs veure la sortida de la teva vista de Django.
Visca! Ja has fet la teva primera pàgina web amb Django.
Hauríem de dir algunes coses sobre el que ha passat. Aquí hi ha una explicació de que passa quan s'executa el servidor de desenvolupament Django i es fan peticions a pàgines web:
Amb aquest coneixement, coneixes les bases de com es fan pàgines amb Django. És realment simple -- només escriu les funcions vista i les mapejes a les URL via el fitxer URLconfs.
Ara és un bon moment per explicar una de les claus de la filosofia de les URLconfs, i de Django en general: el principi de l'acoblament dèbil. Per resumir, un acoblament dèbil és una aproximació de desenvolupament de programari que valora la importància de fer peces intercanviables. Si dues de peces de codi estan "dèbilment acoblades", llavors fer canvies a una de les peces tindran un petit o cap efecte sobre les altres.
URLconfs de Django és un bon exemple d'aquest principi posat en pràctica. En un aplicació web Django, les definicions de les URL i les funcions de vista a qui criden estan dèbilment acoblades; això és, la decisió que una URL ha de prendre per una funció donada, i la implementació de la pròpia funció, resideix en dos llocs diferents. Això permet al desenvolupador canviar una peça sense afectar-ne a una altra.
En canvi, altres plataformes de desenvolupament web acoblen la URL al programa. La base del PHP (http://www.php.net/, per exemple, la URL de la teva aplicació és designada per el lloc del codi del teu sistema de fitxers. En el marc-de-treball web CherryPy (http://cherrypy.org/, la URL de la teva aplicació correspón a el nom del mètode on el teu codi resideix. Això podria semblar com una drecera convenient, però pot ser immanejable a llarg temps.
Per exemple, considera la funció vista que hem escrit abans, que mostra la data i l'hora actual. Si canviem la URL de l'aplicació -- diguem, que ho movem de /ara/ a /horaactual/ -- nosaltres només haurem de fer un petit canvi en el URLconf, sense haver de preocupar-nos de la implementació interna de la funció. De forma semblant, si hem fet un canvi a la funció vista -- alternant-ne la seva lògica -- podríem fer que sense que afectés a la URL. Tanmateix, si nosaltres volem mostrar la funcionalitat de la data actual en diverses URL, només haurem de modificar el URLconf sense haver de tocar el codi de la vista.
Això és una acció d'acoblament dèbil. I continuarem mostrant exemples d'aquesta important filosofia en aquest llibre.
En el nostre URLconf només hem definit una única URLpattern -- la que enllaça amb la petició a la URL /ara/. Que passa quan es demanin diferents URL?
Per trobar-les, intenta executar el servidor de desenvolupament Django i obre la pàgina http://127.0.0.1:8000/hello/ o http://127.0.0.1:8000/no-existeix/ o fin i tot http://127.0.0.1:8000/ (l'arrel del lloc).
Podràs veure el missatge "Page not found" (Pàgina no trobada). (Maco, oi? La la gent de Django els agraden els colors pastel). Django mostra aquest missatge perquè la teva petició d'URL no està definida en el teu URLconf.
La utilitat d'aquesta pàgina va més enllà dels missatges d'error 404. També t'explica precisament quin URLconf utilitzes i cada patró que hi ha. A partir d'aquesta informació, hauries de poder descobrir perquè la petició a la URL envia un error 404.
Naturalment, aquesta informació només t'interessa a tu, el desenvolupador web. Si estàs desenvolupant la web de producció a Internet, no hauríem de mostrar aquesta informació al públic. Per aquesta raó, la pàgina "Page not fount" només es mostra en el teu projecte Django en mode debug. Ja explicarem com desactivar el mode de debug més endavant. Per ara, només cal que sàpigues que cada cop que crees un projecte Django aquest comença en mode debug.
En la teva primera vista d'exemple, els continguts de la pàgina -- la data i hora actual -- eren dinàmiques, però la URL ("/ara/") era estàtica. En la majoria d'aplicacions web dinàmiques, en la URL conté paràmetres que influencien la sortida de la pàgina.
Un altre (lleugerament canviat) exmeple, et permetrà crear una segona vista, que mostri la data i hora actuals desplaçats un cert nombre d'hores. L'objectiu és fer-ho d'una manera més artística de manera que la pàgina /ara/mes1hora/ mostri la data i hora, una hora en el futur, la pàgina /ara/mes2hores/ mostri la data i hora, dues hores en el futur, la pàgina /ara/mes3hores/ mostri la data i hora, tres hores en el futur, i continuant.
Una persona novell pensaria codificar una funció vista per a cada hora desplaçada, resultat que podria veure's en aquest URLconf:
urlpatterns = patterns('',
(r'^ara/$', current_datetime),
(r'^ara/mes1hora/$', una_hora_endavant),
(r'^ara/mes3hores/$', dues_hores_endavant),
(r'^ara/mes3hores/$', tres_hores_endavant),
(r'^ara/mes4hores/$', quatre_hores_endavant),
)
Centament, aquestes línies fan mal als ulls. No només seria un resultat redundant, ja que l'aplicació està limitada a suportar només els rangs d'hores prefixades -- una, dues, tres o quatre hores. Si, de cop i volta, nosaltres volem crear una pàgina que mostri el temps de cinc hores en el futur, haurem de crear una altra vista separada i una línia al URLconf, fomentem la duplicació i la no és sa. Necessitem fer una mica d'abstracció.
Si has experimentat amb altres plataformes de desenvolupament web, com PHP o Java, pots estar pensant: "Ei, utilitzen paràmetres en la URL!". Que que són quelcom així /ara/mes?hores=3, on les hores han d'assignar-se en el paràmetre hores en la cadena de la URL (la part després de ?).
Pots fer això amb Django -- i t'ho explicarem més tard, si realment vols saber-ho -- però una de les filosofies del cor de Django és que les URLs ha de ser precioses. La URL /ara/mes3hores/ és més neta, simple, més llegible, facil de recitar per qualsevol persona i ... són molt més maques les url planes que els basades en paràmetres. Els URLS maques són un signe de qualitat en una aplicació Web.
El sistema de URLconf de Django encoratja a fer URLs maques fent que sigui més senzill fer-ho així que no fer-ho.
Continuant amb l'exemple de hores_endavant, posarem comodins en els patrons URL. Com hem dit abans, els patrons URL són una expressió regular, i, per tant, podem utilitzar els patrons de les expressions regulars \d+ per encaixar amb dos o més dígits:
from django.conf.urls.defaults import *
from mysite.views import current_datetime, hours_ahead
urlpatterns = patterns('',
(r'^ara/$', datahora_actual),
(r'^ara/mes\d+hores/$', hores_endavant),
)
Aquest patró URL encaixa amb qualsevol URL com /ara/mes2hores/, /ara/mes25hores/ o fins i tot /ara/mes100000000000hores/. Pensa, si vols que el límit siguin 99 hores. Això vol dir que nosaltres no hem de permetre més de 2 dígits; en la sintaxi d'expressió regular, això és trasllada en \d{1,2}:
(r'^ara/mes\d{1,2}hores/$', hores_endavant),
(Quan construïm aplicacions web, és important considerar el màxim de dades d'entrada possible, i decidir si l'aplicació ha de suportar aquestes entrades o no. Nosaltres hem limitat el nombre d'hores a 99.
Expressions regulars
Les expressions regulars (o "regexes") són una forma compacta d'especificar patrons de text. Encara que el URLconfs de Django permete gualsevol regexe, probablement només n'utilitzaràs uns quants en la pràctica. Aquí hi ha una selecció dels patrons més comuns:
Simbol Equivalència .(punt) Qualsevol caràcter \d Qualsevol dígit [A-Z] Qualsevol caràcter entre A-Z (majúscules) [a-z] Qualsevol caràcter entre a-z (minúscules) [A-Za-z] Qualsevol caràcter entre a-z (majúscules/minúscules) [^/]+ Qualsevol caràcter fins a la barra (excloent-la) + Un o més dels caràcters prèvis (\d+ equival a un o més dígits) ? Zero o un dels caràcters prèvis (\d? equival a zero o un dígits) * Zero o més dels caràcters prèvis (\d* equival a zero o més dígits) {1,3} Entre un i tres (nclosos) dels caràcters prèvis Per saber-ne més sobre les expressions regulars, mira l'Apendix XXX, Expressions Regulars.
Ara que ja hem fet un resum sobre les URL, necessitem un forma de passar aquestes dades a la funció vista, per a que poguem utilitzar una única funció vista per a qualsevol marge d'hores arbitrari. Això ho farem situant parèntesis entre les dades en el patró URL que hem guardat. En el cas del nostre exemple, volem guardar el nombre entrat dins la URL -- així que posarem els parèntesi al voltant de \d{1,2}:
(r'^ara/mes(\d{1,2})hores/$', hores_endavant),
Si estàs familiaritzat amb les expressions regulars, estaràs com a casa; estem utilitzant parèntesis per capturar les dades que hi equivalen.
Al final del URLconf, hi inclourem la nostra vista datahora_actual, i queda més o menys així:
from django.conf.urls.defaults import *
from el_meu_lloc.views import datahora_actual, hores_endavant
urlpatterns = patterns('',
(r'^ara/$', datahora_actual),
(r'^ara/mes(\d{1,2})hores/$', hores_endavant),
)
Amb això, ara hem d'escriure la vista.
Ordre de codificació
En aquest cas, hem escrit primer el patró URL i després la vista, però en l'exemple anterior, ho hem escrit al revés. Quin tècnica és la millor?
Bé, cadascú és diferents.
Si, és un tipus de persona visionaria, potser t'interessa escriure tots els patrons URL de la teva aplicació, al començament del projecte, i després codificar les vistes. Això, té l'avantatge que et dona una llista PER FER clara, i essencialment et defineix el paràmetres requerits per les funcions vista que et caldrà escriure.
Si, ets més aviat, un programador de petites coses, potser prefereixes escriure primer les vistes, i després ancorar-les a les URL. També és correcte.
En definitiva, acostuma al que et vagi millor a la teva manera de pensar. Qualsevol aproximació és vàlida.
La vista hores_endavant és molt semblant a datahora_actual que hem escrit abans, amb una diferència: aquesta pren un argument extra, el nombre d'hores de diferència. Així:
from django.http import HttpResponse
import datetime
def hores_endavant(request, offset):
offset = int(offset)
dt = datetime.datetime.now() + datetime.timedelta(hours=offset)
html = "D'aquí %s hores, seran les %s." % (offset, dt)
return HttpResponse(html)
Passem per cada línia del codi:
Amb aquesta funció vista i URLconf escrita, engeguem el servidor de desenvolupament Django (si no està en marxa), i visita http://127.0.0.1:8000/ara/mes3hores/ per verificar la feina. Després prova http://127.0.0.1:8000/ara/mes5hores/. Després http://127.0.0.1:8000/ara/mes24hores/. Finalment, visita http://127.0.0.1:8000/ara/mes100hores/ per verificar que el patró només accepta número d'un o dos dígits. Django hauria de mostrar un error "Pagina no trobada", igual que els que hem vist "4040 errors" en la secció anterior. La URL http://127.0.0.1:8000/ara/meshores/ (sense especificar hora) també hauria de retornar un error 404.
Si has seguit tot el que hem codificat, ara el teu fitxer views.py ara conté dues vistes. (Hem omès la vista datahora_actual dels exemples per claredat). Tot junt, seria això:
from django.http import HttpResponse
import datetime
def current_datetime(request):
now = datetime.datetime.now()
html = "It is now %s." % now
return HttpResponse(html)
def hores_endavant(request, offset):
offset = int(offset)
dt = datetime.datetime.now() + datetime.timedelta(hours=offset)
html = "D'aquí %s hores, seran les %s." % (offset, dt)
return HttpResponse(html)
Atura't un moment per admirar l'aplicació que hem fet... i prou!
Ara introdueix deliberadament un error Python en el fitxer views.py, comentant la línia offset = int(offset) de la vista hores_endavant:
def hores_endavant(request, offset):
# offset = int(offset)
dt = datetime.datetime.now() + datetime.timedelta(hours=offset)
html = "D'aquí %s hores, seran les %s." % (offset, dt)
return HttpResponse(html)
Ara re-iniciem el servidor de desenvolupament i carreguem l'adreça /ara/mes3hores/. Hi veuràs una pàgina d'error amb un munt d'informació molt significativa, incloent-hi el missatge TypeError mostrat a dalt de tot: "unsupported type for timedelta hours component: str".
Que ha passat?
Bé, la funció datetime.timedelta esperava que el paràmetre hours fos un enter, i comentat la línia, no l'hem convertit a enter. Això ha causat que datetime.timedelta aixequés un error TypeError. És el típic tipus d'error petit que tot programació fa algun cop.
Aquest exemple mostra les pàgines d'error de Django. Preneu-vos una mica de temps per explorar la pàgina d'error i coneixeu la diversa informació que et mostra.
Algunes coses a destacar:
No ho hem traduït: