dikamilo.net

Kolejny blog w sieci...

Mapowanie adresów URL w Django

W każdej aplikacji internetowej dostęp do zasobów mamy poprzez adres URL. Lista ostatnich wpisów, archiwum kategorii czy konkretny wpis, do każdego z tych elementów odwołujemy się poprzez odpowiedni adres. Dodatkowo często przez adres przekazujemy dodatkowe parametry jak np. identyfikator wpisu, rok, miesiąc czy numer strony.

Django posiada wygodny system mapowania adresów URL w którym możemy wykorzystać wyrażenia regularne czy podzielić reguły na moduły i podłączyć je do dowolnego miejsca w adresie.

Reguły adresów URL są parsowane w chwili ich pierwszego użycia co nie ma znacznego wpływu na szybkość wykonywania.

URL dispatcher

Gdy tworzymy nowy projekt Django, automatycznie zostaje wygenerowany plik url.py bez konfiguracji adresów URL:

from django.conf.urls.defaults import *
urlpatterns = patterns('',)

Tworzenie nowych reguł jest bardzo proste. Dodajemy do urlpatterns kolejne wpisy w postaci:

("reguła_url", "nazwa_widoku", (), "nazwa_reguły"),

Dwa ostatnie parametry są opcjonalne. Trzeci parametr jest krotką w której przekazujemy dodatkowe parametry do mapowanego widoku.

Jak już wspominałem, Django pozwala na korzystanie z wyrażeń regularnych w regułach URL. Dzięki temu nie musimy wpisywać osobnych reguł dla każdego zapytania. Przykładowo jeżeli chcieli byśmy wygenerować adres w postaci /archiwum/2010/03/06 to możemy to zapisać w następujący sposób:

(r'^archiwum/(\d{4})/(\d{2})/(\d{2})/$', 'myapp.views.archiwum'),

Dzięki temu wszystkie adresy pasujące do tej reguły wywołają odpowiedni widok. Jednak aby parametry były dostępne w widoku muszą być jakoś nazwane i przekazane. Robi się to dopisując ich nazwy w regule URL:

(r'^archiwum/(?P<rok>\d{4})/(?P<miesiac>\d{2})/(?P<dzien>\d{2})/$',
'myapp.views.archiwum'),

Nagłówek funkcji widoku przyjmuje następującą postać:

def archiwum(request, rok, miesiac, dzien)

Mapując wiele widoków z tego samego pliku można się pokusić o skrócony zapis. Zamiast pisać:

urlpatterns = patterns('',
   ('reguła_url', 'myapp.views.widok1',),
   ('reguła_url2', 'myapp.views.widok2',),
   ('reguła_url3', 'myapp.views.widok3',),
)

Możemy to zapisać skrótem:

urlpatterns = patterns('myapp.views',
   ('reguła_url', 'widok1',),
   ('reguła_url2', 'widok2',),
   ('reguła_url3', 'widok3',),
)

Gdy korzystamy np. z widoków generycznych to zapis skrócony nie jest możliwy bo raz odwołujemy się do naszych widoków a raz do widoków generycznych. W takim wypadku możemy rozdzielić reguły na kilka urlpatterns.

### nasze widoki
urlpatterns = patterns('myapp.views',
   ('reguła_url', 'widok1',),
   ('reguła_url2', 'widok2',),
   ('reguła_url3', 'widok3',),
)

### widoki generyczne
urlpatterns += patterns('django.views.generic.list_detail',
   ('reguła_url4', 'object_detail', parametry),
   ('reguła_url5', 'object_detail', parametry),
)

Rozdzielanie reguł na kilka plików

Gdy tworzymy dużą aplikację, trzymanie wszystkich reguł w jednym pliku jest niewygodne. Szczególnie gdy chcieli byśmy dany moduł odseparować i wykorzystać w innym projekcie. W każdej aplikacji możemy stworzyć moduł urls w którym będziemy trzymać wszystkie reguły wykorzystywane w danej aplikacji.

Przykładowo ja w aplikacji bloga mam zdefiniowane trzy pliki urls, tags.py, categories.py oraz entries.py. Zawartość tych plików jest w zasadzie taka sama jak głównego pliku urls.py po za tym że są tam inne reguły.

Podłączanie tych reguł jest bardzo proste, django udostępnia nam do tego funkcję include która przyjmuje jako parametry nazwę pliki z regułami URL. Możemy podłączyć reguły do dowolnej ścieżki w projekcie, przykładowo chcieli byśmy aby wszystkie reguły zdefiniowane w pliku tags.py były dostępne pod adresem domena.com/tags/. Możemy to zrobić następująco:

urlpatterns = patterns('',
    (r'^tags/', include('myapp.urls.categories') ),
)

Nazywanie reguł i generowanie linków

Mimo że nazywanie reguł URL jest opcjonalne, warto to zrobić gdyż dzięki temu można w bardzo łatwy sposób generować linki.

W szablonach możemy to zrobić wykorzystując tag url.

{% url nazwa_url parametry %}

Parametry są potrzebne gdy reguła ich wymaga, jak w powyższym przykładzie parametrami są rok, miesiąc oraz dzien.

W widokach możemy generować adresy wykorzystując skrót reverse.

from django.core.urlresolvers import reverse
reverse('nazwa_url', args=[])

Gdzie args przyjmuje listę parametrów.

W modelach możemy generować adresy wykorzystując dekorator @models.permalink oraz skrót reverse. W przypadku linków do takich elementarnych rzeczy jak wpis na blogu czy kategoria które są modelami warto stosować zalecenia Django i zdefiniować funkcję get_absolute_url bezpośrednio w modelu. Django używa tej funkcji w panelu administracyjnym oraz w pakiecie django.contrib.syndication do generowania kanałów RSS.

@models.permalink
def get_absolute_url(self):
    return ('nazwa_url', [])

Drugi parametry jest listą parametrów.

Funkcję get_absolute_url możemy również używać w szablonach wywołując na konkretnym obiekcie, ale zastosowanie tagu url wydaje mi się wygodniejsze.

Nazewnictwo URL oraz generowanie na ich podstawie linków ma jedną zasadniczą zaletę. W przypadku zmiany adresów w naszej aplikacji nie musimy robić zmiany w każdym miejscu gdzie generujemy linki.

Autoryzacja na poziomie URL dispatcher'a

Czasami zachodzi potrzeba sprawdzania autoryzacji użytkowników. Chcieli byśmy zrobić aby dana podstrona była dostępna tylko dla zalogowanych użytkoników. Możemy to zrobić na poziomie reguł URL importując dekorator login_required. Dekorator jako parametr przyjmuje obiekt naszego widoku.

from django.contrib.auth.decorators import login_required
from myapp.views import archiwum

(r'^archiwum/(?P<rok>\d{4})/(?P<miesiac>\d{2})/(?P<dzien>\d{2})/$',
login_required(archiwum)),

Jak zwykle zachęcam do korzystania z dokumentacji django gdzie znajdziesz obszerniejszy opis oraz kilka przykładów.

Tagi