Inici de sessió d'usuari


Capítol 8: Vistes i URLconfs avançades


En el capítol 3, hem explicat les bases de les funcions vista de Django i de les URLconf. Aquest capítol entra en més detall en la funcionalitat avançada d'aquestes dues parts del marc-de-treball.

Trucs URLconf

Millorar l'importació de funcions

Considera aquesta URLconf, que hem fet en l'exemple del capítol 3:

from django.conf.urls.defaults import *
from mysite.views import current_datetime, hours_ahead, hours_behind, now_in_chicago, now_in_london

urlpatterns = patterns('',
    (r'^now/$', current_datetime),
    (r'^now/plus(\d{1,2})hours/$', hours_ahead),
    (r'^now/minus(\d{1,2})hours/$', hours_behind),
    (r'^now/in_chicago/$', now_in_chicago),
    (r'^now/in_london/$', now_in_london),
)

Com hem explicat en el capítol 2, cada entrada de la URLconf inclou la seva funció vista associada, passada directament com una funció objecte. Això vol dir que necessàriament cal importar les funcions vista al principi del mòdul.

Però com que una aplicació Django creix de forma complexa, les seves URLconf també creixen, i mantenir aquests imports pot ser tediós de gestionar. (Per cada nova funció vista, hauràs de recordar d'importar-ho, i la línia import tendeix a créixer molt si ho utilitzes així). És possible evitar aquesta tediosa forma de treballar important el propi mòdul views. Aquest exemple de URLconf és equivalent a l'anterior:

from django.conf.urls.defaults import *
from mysite import views

urlpatterns = patterns('',
    (r'^now/$', views.current_datetime),
    (r'^now/plus(\d{1,2})hours/$', views.hours_ahead),
    (r'^now/minus(\d{1,2})hours/$', views.hours_behind),
    (r'^now/in_chicago/$', views.now_in_chicago),
    (r'^now/in_london/$', views.now_in_london),
)

Django ofereix altres formes d'especificar les funcions vista d'un patró en particular en un URLconf: Pots passar-li una cadena de text que contingui el nom del mòdul i el nom de la funció en comptes de l'objecte funció. Continuant amb l'exemple tenim:

from django.conf.urls.defaults import *

urlpatterns = patterns('',
    (r'^now/$', 'mysite.views.current_datetime'),
    (r'^now/plus(\d{1,2})hours/$', 'mysite.views.hours_ahead'),
    (r'^now/minus(\d{1,2})hours/$', 'mysite.views.hours_behind'),
    (r'^now/in_chicago/$', 'mysite.views.now_in_chicago'),
    (r'^now/in_london/$', 'mysite.views.now_in_london'),
)

Utilitzant aquesta tècnica, no caldrà importar les funcions vista; Django importa automàticament les funcions de vista apropiades el primer com que les necessitin, d'acord amb les cadenes de text que descriuen el nom i camí de la funció vista.

Una drecera més enllà podria usar-se quan en la tècnica de cadenes de text poguéssim treure un prefix comú. En el nostre exemple URLconf, cada una de les cadenes de la vista comencen amb 'mysyte.views', que és redundant. Podem factoritzar aquest prefix i passar-lo com a primer argument de la funció patterns(), així:

from django.conf.urls.defaults import *

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'),
)

Fixa't que no posem el punt (".") en el prefix, ni en la resta de cadenes de les vistes. Django el posa automàticament.

Amb aquestes dues aproximacions en ment, quina és millor? Realment depèn de les teves necessitats personals i del teu estil de codificació.

Els avantatges que proporciona l'aproximació de cadena de text són:

  • És més compacte, perquè no cal que importis les funcions de vista.
  • Les resultats són més llegibles manejables si les teves funcions de vista si els teus mòduls Python són molt diferents.

Els avantatges de l'aproximació de l'objecte funció són:

  • Permet "disfressar" fàcilment les funcions vista. Mira-ho en un capítol posterior "Disfressant les funcions de vista".
  • És molt més "Pythonic" -- és a dir, està més d'acord amb les tradicions de python, com a forma de passar funcions com a objectes.

Ambdues aproximacions són vàlides, i pots fins i tot barrejar-les amb el mateix URLconf. Has d'escollir.

Múltiples prefixos de vistes

En la pràctica, si utilitzes la tècnica de les cadenes de text, probablement acabaràs barrejant vistes fins al punt que aquestes vistes no tindran un prefix comú. No obstant, pots aprofitar la drecera del prefix vista per eliminar duplicacions. Només has d'afegir múltiples objectes patterns(), com aquests:

Vell:

from django.conf.urls.defaults import *

urlpatterns = patterns('',
    (r'^/?$', 'mysite.views.archive_index'),
    (r'^(\d{4})/([a-z]{3})/$', 'mysite.views.archive_month'),
    (r'^tag/(\w+)/$', 'weblog.views.tag'),
)

Nou:

from django.conf.urls.defaults import *

urlpatterns = patterns('mysite.views',
    (r'^/?$', 'archive_index'),
    (r'^(\d{4})/([a-z]{3})/$','archive_month'),
)

urlpatterns += patterns('weblog.views',
    (r'^tag/(\w+)/$', 'tag'),
)

Totes les crides a l'objecte patterns() treballen sobre la mateix variable anomenada urlpatterns. Aquesta variable pot construir-se dinàmicament, com en aquest example.

Grups amb nom

En tots els exemples de l'URLconf, hem usat grups d'expressions regulars simples sense nom -- p.e. hem posat parèntesi al voltant de les parts de la URL que volíem capturar, i Django ha passat el text captura a la funció vista com un argument posicional. En usos més avançats, és possible usar grups d'expressions regulars amb noms per capturar trossos d'URL i passar-los a arguments clau a la vista

Arguments amb nom versus arguments posicionals
Una funció Python pot cridar-se utilitzant arguments amb nom, especificant el nom dels arguments junt amb els seus valors. En una crida per posició d'argument, simplement passes els arguments sense explicitar quin argument equival a cada valor; l'associació és implícita a l'ordre dels arguments.

Per exemple, considera aquesta funció simple

def sell(item, price, quantity):
    print "Selling %s unit(s) of %s at %s" % (quantity, item, price)

Per cridar-la amb arguments posicionals, has d'especificar els arguments en l'ordre en que s'han llistat en la definició de la funció:

sell('Socks', '$2.50', 6)

Per cridar amb arguments amb nom, especifica els noms dels arguments amb els valors. Les següents sentències són equivalents:

sell(item='Socks', price='$2.50', quantity=6)
sell(item='Socks', quantity=6, price='$2.50')
sell(price='$2.50', item='Socks', quantity=6)
sell(price='$2.50', quantity=6, item='Socks')
sell(quantity=6, item='Socks', price='$2.50')
sell(quantity=6, price='$2.50', item='Socks')

A Python les expressions regulars, la sintaxi per a grups d'expressions amb nom és (?pattern), on name és el nom del grup i pattern és algun patró que hi encaixi.

Aquest és un exemple URLconf que utilitza grups sense nom:

month_archive(request, '2006', '03')

Aquest és el mateix exemple URLconf que utilitza grups amb nom:

month_archive(request, year='2006', month='03')

A la pràctica, utilitzar grups amb nom fa que les teves URLconf siguin més explícites i menys limitades a errors per l'ordre dels arguments -- i pots reodernar els arguments en les definicions de les teves funcions de vista. Seguint amb l'exemple anterior, si volem canviar les URL per incloure el més abans que l'any, i estem usant grups sense nom, haurem de recordar-nos de canviar l'ordre dels arguments en la vista month_archive. Si estem usant grups amb nom, canviant l'ordre dels paràmetres capturats en la URL no ens afectarà a la vista.

Per descomptat, els beneficis dels grups amb nom són a costa de la brevetat; alguns desenvolupadors troben la sintaxi de grups amb nom lletja i massa explícita.

L'algoritme de equivalència/agrupació

Si utilitzes grups amb nom i sense en el mateix patró en el teu URLconf, hauràs d'anar amb compte com Django tracta aquest cas especial. Aquí hi ha l'algorisme que segueix URLconf per analitzar-lo, respecte els grups anomenats vers els no anomenats en les expressions regulars:

  • Si hi ha algun argument amb nom, s'usarà aquell, ignorant els que no tenen nom.
  • Altrament, se li passarà els arguments sense nom per la seva posició.
  • En ambdós casos, es passen els arguments extra com a arguments amb nom. Mireu "Passant opcions extra en funcions vista" a continuació

Passant opcions extra en funcions vista

Algunes vegades et trobarà escrivint funcions vista que són molt semblants, amb només unes petites diferències. Per exemple, diguem que tens dues vistes que el contingut és idèntic excepte per l'ús de la plantilla:

# urls.py

from django.conf.urls.defaults import *
from mysite import views

urlpatterns = patterns('',
    (r'^foo/$', views.foo_view),
    (r'^bar/$', views.bar_view),
)

# views.py

from django.shortcuts import render_to_response
from mysite.models import MyModel

def foo_view(request):
    m_list = MyModel.objects.filter(is_new=True)
    return render_to_response('template1.html', {'m_list': m_list})

def bar_view(request):
    m_list = MyModel.objects.filter(is_new=True)
    return render_to_response('template2.html', {'m_list': m_list})

Hem repetit aquest codi, que no és massa elegant. Primer, pots pensar en eliminar la redundància utilitzant la mateixa URL en ambdós vistes, posant parèntesi en la URL per capturar-ne el tipus, i comprovant la URL dins de la vista per determinar la plantilla, així:.

# urls.py

from django.conf.urls.defaults import *
from mysite import views

urlpatterns = patterns('',
    (r'^(foo)/$', views.foobar_view),
    (r'^(bar)/$', views.foobar_view),
)

# views.py

from django.shortcuts import render_to_response
from mysite.models import MyModel

def foobar_view(request, url):
    m_list = MyModel.objects.filter(is_new=True)
    if url == 'foo':
        template_name = 'template1.html'
    elif url == 'bar':
        template_name = 'template2.html'
    return render_to_response(template_name, {'m_list': m_list})

El problema amb aquesta solució, és que s'acobla la teva URL en el teu codi. Si decideixes canviar el nom de la URL /foo/ a /fooey/, hauràs de recordar a canviar el codi de la vista.

Una solució elegant implica una característica anomenada opcions extra de l'URLconf. Cada patró en un URLconf pot incloure un tercer element -- un diccionar amb arguments amb clau per passar a la funció vista.

Amb això en ment, pots re-escriure el nostre exemple així:

# urls.py

from django.conf.urls.defaults import *
from mysite import views

urlpatterns = patterns('',
    (r'^foo/$', views.foobar_view, {'template_name': 'template1.html'}),
    (r'^bar/$', views.foobar_view, {'template_name': 'template2.html'}),
)

# views.py

from django.shortcuts import render_to_response
from mysite.models import MyModel

def foobar_view(request, template_name):
    m_list = MyModel.objects.filter(is_new=True)
    return render_to_response(template_name, {'m_list': m_list})

Com pots veure, l URLconf en aquest exemple especifica un template_name en la URLconf. La funció vista es crida amb un altre paràmetre.

Aquesta tècnica d'opció extra és una bona manera d'enviar informació addicional a les nostres funcions vista amb el mínim d'inconvenients. Tot just hem utilitzat algunes formes complexes de Django, més complexitat hi haurà en el sistema de les vistes genèriques, que tractarem en el capítol 9.

Aquestes són uns quantes idees de com pots utilitzar la tècnica de les opcions extres de l'URLconf en els teus propis projecte:

Falsificar els valors capturats en les URLconf

Diguem que tens un conjunt de vistes que encaixen amb un patró, a més de tenir unes URL que no encaixen amb el patró però que la lògica de la vista és la mateixa. En aquest cas, pots "falsificar" els valors capturats a la URL utilitzant les opcions extra de l'URLconf per capturar aquesta URL extra amb la mateixa vista.

Per exemple, pots tenir una aplicació que mostra dades d'un dia en particular, amb URLs com aquestes:

/mydata/jan/01/
/mydata/jan/02/
/mydata/jan/03/
# ...
/mydata/dec/30/
/mydata/dec/31/

Aquest és bastant senzill de tractar; pots capturar-lo en una URLconf com aquesta (utilitzant la sintaxi de grups amb nom):

urlpatterns = patterns('',
    (r'^mydata/(?P<month>\w{3})/(?P<day>\d\d)/$', views.my_view),
)

I la firma de la funció vista seria quelcom així:

def my_view(request, month, day):
    # ....

Això és directe -- no és res que no hàgim vist abans. El truc ve en quan pots afegir una altra URL que utilitza my_view però aquesta URL no inclou un month i/o day.

Per exemple, podries voler afegir una altra URL, /mydata/birthday/, que podria ser equivalent a /mydata/jan/06/. Podem aprofitar-nos de l'opció extra de l'URLconf així:

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

Lo "guay" aquí és que no hem hagut de canviar la nostra funció vista. La funció vista només es preocupa que tingui els paràmetres month i/o day -- aquesta no es preocupa si provenen de la captura de la URL de paràmetres extra.

Fent una vista genèrica

És una bona pràctica de programació, fer resums en el codi. Per exemple, amb aquestes dues funcions de Python:

def say_hello(person_name):
    print 'Hello, %s' % person_name

def say_goodbye(person_name):
    print 'Goodbye, %s' % person_name

...nosaltres podem resumir-ho amb una salutació com a paràmetre:

def greet(person_name, greeting):
    print '%s, %s' % (greeting, person_name)

Pots aplicar aquesta mateixa filosofia a les teves vistes Django utilitzant paràmetres extra.

Amb això en ment, pots començar a fer abstraccions d'alt nivell de les teves vistes. En comptes de personar-ho tu mateix, "Aquesta vista mostra una llista d'objectes Event", i "Aquesta vista mostra una llista d'objecte BlogEntry", veiem que són ambdós casos de "Una vista que mostra una llista d'objectes, on el tipus de l'objecte és variables".

Utilitza aquest codi, per exemple:

# urls.py

from django.conf.urls.defaults import *
from mysite import views

urlpatterns = patterns('',
    (r'^events/$', views.event_list),
    (r'^blog/entries/$', views.entry_list),
)

# views.py

from django.shortcuts import render_to_response
from mysite.models import Event, BlogEntry

def event_list(request):
    obj_list = Event.objects.all()
    return render_to_response('mysite/event_list.html', {'event_list': obj_list})

def entry_list(request):
    obj_list = BlogEntry.objects.all()
    return render_to_response('mysite/blogentry_list.html', {'entry_list': obj_list})

Les dues vistes fan essencialment el mateix: mostres una llista d'objectes. Així pots factoritzar el tipus d'objecte que es mostraran:

# urls.py

from django.conf.urls.defaults import *
from mysite import models, views

urlpatterns = patterns('',
    (r'^events/$', views.object_list, {'model': models.Event}),
    (r'^blog/entries/$', views.object_list, {'model': models.BlogEntry}),
)

# views.py

from django.shortcuts import render_to_response

def object_list(request, model):
    obj_list = model.objects.all()
    template_name = 'mysite/%s_list.html' % model.__name__.lower()
    return render_to_response(template_name, {'object_list': obj_list})

Amb aquests petits canvis, tenim un codi de vista re-usable, independent del model! A part d'ara, qualsevol cop que necessitem una vista que llisti un conjunt d'objectes, podem simplement re-utilitzar la vista object_list en comptes de re-escriure el codi de la vista. Aquí hi ha un conjunt de notes sobre hem fet:

  • Estem passant directament les classes del model, com a paràmetre model. El diccionari d'opcions extra pot passar qualsevol tipus d'objecte Python -- no només cadenes.
  • La línia model.objects.all() és un exemple de "mecanografia d'ànec": "Si camina com un ànec i parla com un ànec, podem tractar-lo com un ànec". Fixa't que el codi no sap quin tipus de objecte model és; l'únic requeriment és que model tingui un atribut objects, que a l'hora cridi al mètode all().
  • Utilitzem model.__name__.lower() per determinar el nom de la plantilla. Cada classe Python té un atribut __name__ que retorna el nom de la classe. Aquesta característica és útil en aquestes ocasions, quan no saps el tipus de classe fins a l'hora d'execució.
    Per exemple, el __name__ de la classe BlogEntry és la cadena 'BlogEntry'
  • Amb una lleu diferència entre aquest exemple i l'exemple previ, hem passat el nom de la variable genèrica object_list a la plantilla. Podríem fàcilment canviar aquest nom de variable per a ser blogentry_list o event_list, però ho hem deixat com un exercici pel lector.

Com que els llocs web basats en base de dades tenen molts patrons comuns, Django ve amb un conjunt de "vistes genèriques" que utilitzen aquesta mateixa tècnica per estalviar-te temps. Nosaltres tractarem aquestes vistes genèriques en el pròxim capítol.

Donant opcions a la configuració de les vistes

Si estàs distribuint una aplicació Django, els teus usuaris volen poder canviar algunes configuracions. En aquest cas, és una bona idea afegir ganxos en les teves vistes per qualsevol opció de configuració que pensis que la gent podria voler canviar. Pots usar paràmetres extra per a aquest propòsit.

Un exemple comú d'un canvi configurable d'aplicació és el nom de la plantilla:

def my_view(request, template_name):
    var = do_something()
    return render_to_response(template_name, {'var': var})

Preferència de valors capturats vers opcions extra

Quan hi ha un conflicte, els paràmetres extra poden precedir les paràmetres capturats. En altres paraules, si les teves URLconf capturen una variable amb nom i un paràmetre extra inclou una variable amb el mateix nom, el valors del paràmetre extra serà l'utilitzat.

Per exemple, considera aquesta URLconf:

from django.conf.urls.defaults import *

urlpatterns = patterns('',
    (r'^mydata/(?P<id>\d+)/$', views.my_view, {'id': 3}),
)

Aquí, tant l'expressió regular com el diccionari extra inclouen un id. El id codificat té preferència. Això significa que qualsevol petició -- p.e. /mydata/2/ or /mydata/432432/ -- serà tractada com si id fos 3, descartant el valor capturat en la URL.

Els lectors astuts s'hauran fixat que en aquest cas, és una pèrdua de temps capturar l'identificador en l'expressió regular, perquè el seu valor sempre serà substituït per valor del diccionari. Aquests lectors estarien en lo cert. Només ho diem per ajudar-te a evitar fer aquestes equivocacions.

Usant els argument de vista per defecte

Un altre truc interessant és especificar paràmetres per defecte per una vista. Això diu a la vista quins valors utilitzar en un paràmetre si aquest no s'especifica.

Per exemple:

# urls.py

from django.conf.urls.defaults import *

urlpatterns = patterns('',
    (r'^blog/$', views.page),
    (r'^blog/page(?P<num>\d+)/$', views.page),
)

# views.py

def page(request, num="1"):
    # Output the appropriate page of blog entries, according to num.
    # ...

Aquí, ambdós patrons apunten a la mateixa vista -- views.page -- però el primer patró no captura res de la URL. Si el primer patró encaixa la funció page() s'utilitzarà amb l'argument per defecte. Si el segon patró encaixa, page() utilitzarà el valor num que s'ha capturar.

És normal utilitzar aquesta tècnica juntament amb les opcions de configuració, explicades anteriorment. Aquest exemple fa un lleuger canvi en l'exemple de "Donant opcions a la configuració de les vistes" per proporcionar un valor per defecte al nom de la plantilla template_name:

def my_view(request, template_name='mysite/my_view.html'):
    var = do_something()
    return render_to_response(template_name, {'var': var})

Vistes amb opcions especials

Algunes vegades tindreu un patró en el teu URLconf que tracti un gran conjunt de URLs però necessitarem un cas especial per a ells. En aquest cas, aprofitem la forma lineal on una URLconf es processada i posa un primer cas especial.

Per exemple, les pàgines "afegeix un objecte" del lloc d'administració de Django es representen per aquesta línia URLconf:

urlpatterns = patterns('',
    # ...
    ('^([^/]+)/([^/]+)/add/$', 'django.contrib.admin.views.main.add_stage'),
    # ...
)

Aquesta URL encaixa amb /myblog/entries/Add/ i /auth/group/add/. Tanmateix, la pàgina "add" per a un objecte usuari (/auth/user/Add/) és un cas especial -- aquest no mostra tots els camps del formulari, sinó que mostra dos camps de password, etc. Podríem solucionar aquest cas especial de la vista, així:

def add_stage(request, app_label, model_name):
    if app_label == 'auth' and model_name == 'user':
        # do special-case code
    else:
        # do normal code

...però això no és elegant per una raó que s'ha tractat múltiples vegades en aquest capítol: això posa lògica URL en la vista. Per a una solució més elegant, podem aprofitar del fet que les URLconf es processen en ordre de dalt a baix:

urlpatterns = patterns('',
    # ...
    ('^auth/user/add/$', 'django.contrib.admin.views.auth.user_add_stage'),
    ('^([^/]+)/([^/]+)/add/$', 'django.contrib.admin.views.main.add_stage'),
    # ...
)

Amb això, una petició /auth/user/add/ serà tractat per la vista user_add_stage. Encara que aquesta URL encaixi amb el segon patró, primer ho farà amb el primer.

Notes en la captura de text en les URL

Cada argument capturat s'envia a la vista com a una cadena plana Python, sense preocupar-nos de quin tipus d'expressió regular fem. Per exemple, en aquesta línia URLconf:

(r'^articles/(?P<year>\d{4})/$', views.year_archive),

... l'argument
year enviat a views.year_archive() serà una cadena, no un enter, encara que \d{4} només encaixi amb enters.

Això és important per tenir-ho present quan escrivim el codi de la vista. Moltes funcions Python són punyeteres (i legítimes també) sobre acceptar només objectes d'un cert tipus. Un error normal és intentar crear un objecte datetime.date amb valors de cadena de text en comptes de valors enters:

>>> import datetime
>>> datetime.date('1993', '7', '9')
Traceback (most recent call last):
    ...
TypeError: an integer is required
>>> datetime.date(1993, 7, 9)
datetime.date(1993, 7, 9)

Traslladat a una URLconf i vista, l'error seria com aquest:

# urls.py

from django.conf.urls.defaults import *

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)

En comptes de day_archive() pot escriure's correctament així:

def day_archive(request, year, month, day)
    date = datetime.date(int(year), int(month), int(day))

Fixa't que el propi int() llença un ValueError quan li passes una cadena que no està composada només per dígits, però evita aquest error en aquest cas perquè l'expressió regular en la nostra URLconf s'ha assegurat que només les cadenes que contenen fígits poden passar-se a la funció vista.

Que busca URLconf

Quan arriba una petició. Django intenta encaixar-la en un patró URLconf, com una cadena de text Python normal (no com una cadena Unicode). Això no inclou els paràmetres GET i POST, o el nom del domini. Tampoc s'hi inclou la primera barra, perquè cada URL té una primera barra.

Per exemple, en la petició http://www.example.com/myapp/, Django intentarà encaixar myapp/.

En una petició http://www.example.com/myapp/?page=3, Django intentarà encaixar myapp/.

El mètode de la petició -- p.e. POST, GET, HEAD -- no es tenen en compte quan passem per la URLconf. En altres paraules, totes les peticions s'enrutaran a la mateixa funció de la mateixa UTL. És responsabilitat d'una funció de vista realitzar els salts basats en aquest mètode.

Incloent altres URLconf

En qualsevol moment, la teva URLconf pot "include" incloure altres mòduls URLconf. Això, essencialment arrela un conjunt d'URLs sota altres.

Per exemple, aquesta URLconf inclou altres URLconf:

from django.conf.urls.defaults import *

urlpatterns = patterns('',
    (r'^weblog/', include('mysite.blog.urls')),
    (r'^photos/', include('mysite.photos.urls')),
    (r'^about/$', 'mysite.views.about'),
)

Hi ha un tema important aquí: Les expressions regulars en aquest exemple que apunten a un include() no tenen el $ (el caràcter de final de cadena) però inclouen la barra. Sempre que Django troba un include(), extreu qualsevol part de la URL que hi encaixa i envia la resta de la cadena a la URLconf de l'include.

Continuant amb aquest exemple, aquí ha ha la URLconf mysite.blog.urls:

from django.conf.urls.defaults import *

urlpatterns = patterns('',
    (r'^(\d\d\d\d)/$', 'mysite.blog.views.year_detail'),
    (r'^(\d\d\d\d)/(\d\d)/$', 'mysite.blog.views.month_detail'),
)

Amb aquestes dues URLconf, aquí veiem uns quants exemples de com les peticions seran tractades:

  • /weblog/2007/ -- En la primera URLconf, el patró r'^weblog/' encaixa. Com que és un include(), Django elimina tot el text que hi encaixa, el qual en aquest cas és weblog/. La part que queda de la URL és 2007/, que equival a la primera línia en la URLconf mysite.blog.urls.
  • /weblog//2007/ -- En la primera URLconf, el patró r'^weblog/' encaixa. Com que és un include(), Django elimina tot el text que hi encaixa, el qual en aquest cas és weblog/. La part que queda de la URL és /2007/ (amb una barra inicial), que no encaixa amb cap línia en la URLconf mysite.blog.urls.
  • /about/ -- Encaixa al vista mysite.views.about e la primera URLconf. Això demostra que pots barrejar patrons include() amb patrons no-include().

Com funcionen els paràmetres capturats amb include()

Un URLconf inclòs rep qualsevol paràmetre capturat del URLconf pare. Per exemple:

# root urls.py

from django.conf.urls.defaults import *

urlpatterns = patterns('',
    (r'^(?P<username>\w+)/blog/', include('foo.urls.blog')),
)

# foo/urls/blog.py

from django.conf.urls.defaults import *

urlpatterns = patterns('',
    (r'^$', 'foo.views.blog_index'),
    (r'^archive/$', 'foo.views.blog_archive'),
)

En aquest exemple, la variable username capturada es passa a la URLconf inclosa i, per tant, a cada funció vista dins d'aquesta URLconf.

Fixa't que els paràmetres capturats sempre es passen a cada línia de l'URLconf inclosa, encara que la vista no accepti aquests paràmetres com a vàlids. Per aquesta raó, aquesta tècnica només és útil si estàs segur que totes les vistes en la URLconf incloses accepten els paràmetres que els hi passes.

Com funcionen les opcions extra de l'URLconf amb include()

De forma semblant, pots passar opcions extra a l'include(), igual que pots passar opcions a una vista normal -- com un diccionari. Quan ho fas, cada línia en l'URLconf inclosa serà passat a les opcions extra.

Per exemple, aquests dos conjunt d'URLconf funcionen de forma idèntic:

Conjunt ú:

# urls.py

from django.conf.urls.defaults import *

urlpatterns = patterns('',
    (r'^blog/', include('inner'), {'blogid': 3}),
)

# inner.py

from django.conf.urls.defaults import *

urlpatterns = patterns('',
    (r'^archive/$', 'mysite.views.archive'),
    (r'^about/$', 'mysite.views.about'),
    (r'^rss/$', 'mysite.views.rss'),
)

Conjunt dos:

# urls.py

from django.conf.urls.defaults import *

urlpatterns = patterns('',
    (r'^blog/', include('inner')),
)

# inner.py

from django.conf.urls.defaults import *

urlpatterns = patterns('',
    (r'^archive/$', 'mysite.views.archive', {'blogid': 3}),
    (r'^about/$', 'mysite.views.about', {'blogid': 3}),
    (r'^rss/$', 'mysite.views.rss', {'blogid': 3}),
)

Fixa't que les opcions extra sempre seran passades a cada línia del URLconf inclòs, encara que les vistes acceptin les opcions com a vàlides. Per aquesta raó, aquesta tècnica només és útil si estàs segur que totes les vistes en el URLconf inclòs accepta les opcions extra que estàs passant.

Trucs de vista

Aquest capítol no s'ha acabat.