W poprzednim rozdziale wyjaśniliśmy, w jaki sposób utworzyć projekt Django i uruchomić serwer w wersji deweloperskiej. W tym rozdziale zajmiemy się podstawami tworzenia w Django dynamicznych stron WWW. Twoja pierwsza strona w Django: Witaj, świecieJak przystało na książkę informatyczną, rozpoczniemy przygodę od utworzenia strony z tekstem "Witaj, świecie!". Gdybyśmy chcieli utworzyć taką stronę bez stosowania frameworka, po prostu umieścilibyśmy tekst "Witaj, świecie!" w pliku tekstowym, nazwalibyśmy plik hello.html i umieścilibyśmy go na serwerze WWW. Zauważ, że w tym procesie określiliśmy dwa bardzo istotne elementy tworzonej strony WWW: jej zawartość (tekst "Witaj, świecie!") i jej adres URL (http://www.example.com/hello.html lub http://www.example.com/files/hello.html, gdyby umieścić plik w podfolderze). W Django również trzeba określić obie informacje, ale czyni się to w inny sposób. Zawartość strony tworzy funkcja widoku natomiast adres URL określa system URLconf. Najpierw napiszmy funkcję widoku wyświetlającą tekst "Witaj, świecie!". Pierwszy widokWewnątrz folderu mysite utworzonego w poprzednim rozdziale przez polecenie django-admin.py startproject, utwórz plik o nazwie views.py. Plik ten, w rzeczywistości moduł Pythona, będzie zawierał widoki tworzone w tym rozdziale. W samej nazwie views.py nie ma nic szczególnego — Django nie zwraca uwagi na nazwę pliku, o czym się wkrótce przekonasz — ale jako konwencję warto wybrać nazwę views.py, choćby ze względu na innych programistów analizujących kod. Pierwszy widok z tekstem "Witaj, świecie!" jest bardzo prosty. Poniżej znajduje się cała funkcja wraz z instrukcją importu, którą powinieneś wpisać do pliku views.py: from django.http import HttpResponse
def hello(request):
return HttpResponse("Witaj, świecie!")
Przeanalizujmy kod wiersz po wierszu:
Z tej lekcji należy wynieść następującą informację: widok jest zwykłą funkcją Pythona, która jako pierwszy parametr przyjmuje obiekt HttpRequest, a jako odpowiedź zwraca obiekt HttpResponse. Aby funkcja Pythona mogła być widokiem, musi spełnić oba warunki. (Istnieją wyjątki od tej reguły, ale wrócimy do nich w dalszej części książki). Pierwszy URLconfJeśli w tym momencie uruchomiłbyś ponownie polecenie python manage.py runserver, nadal widziałbyś komunikat powitalny Django bez jakiegokolwiek śladu widoku "Witaj, świecie!". Wynika to z faktu, iż projekt mysite jeszcze nic nie wie na temat widoku hello; musimy jawnie wskazać Django, że chcemy pod wskazanym adresem URL wywoływać określony widok. (Stosując analogię do statycznych plików HTML, możemy powiedzieć, że utworzyliśmy plik HTML, ale jeszcze nie umieściliśmy go na serwerze). Aby połączyć adres URL i widok, użyj systemu URLconf. System URLconf to jak spis treści dla witryny Django. Najprościej rzecz ujmując, to odwzorowanie szablonów adresów URL na funkcje widoków wywoływane dla tych wzorców. Informujemy Django: dla tego URL, wywołaj tę funkcję, a dla tamtego, tamtą funkcję. Przykładowo możemy napisać: gdy ktoś odwiedzi adres /foo/, wywołaj funkcję foo_view(), który znajduje się w module views.py. Po wykonaniu polecenia django-admin.py startproject w poprzednim rozdziale, skrypt automatycznie utworzył plik konfiguracji URLconf o nazwie urls.py. Domyślnie wygląda on następująco: from django.conf.urls.defaults import *
# Uncomment the next two lines to enable the admin:
# from django.contrib import admin
# admin.autodiscover()
urlpatterns = patterns('',
# Example:
# (r'^mysite/', include('mysite.foo.urls')),
# Uncomment the admin/doc line below and add 'django.contrib.admindocs'
# to INSTALLED_APPS to enable admin documentation:
# (r'^admin/doc/', include('django.contrib.admindocs.urls')),
# Uncomment the next line to enable the admin:
# (r'^admin/(.*)', admin.site.root),
)
Domyślny URLconf zawiera kilka często używanych elementów Django umieszczonych w komentarzach, więc aktywacja tych funkcji wymaga tylko usunięcia kilku znaków. Gdy pominiemy kod umieszczony jako komentarz, pozostała część będzie bardzo krótka: from django.conf.urls.defaults import *
urlpatterns = patterns('',
)
Przeanalizujmy kod wiersz po wierszu:
Najważniejszym elementem pliku jest zmienna urlpatterns, którą Django oczekuje odnaleźć w module URLconf. Zmienna definiuje odwzorowanie między adresami URL i kodem, który ma za zadanie obsłużyć żądanie. Domyślnie URLconf jest pusty — aplikacja Django jest czysta. (Warto wspomnieć, że to właśnie dlatego Django wiedziało, że powinno wyświetlić stronę powitalną. Jeśli URLconf jest pusty, Django zakłada, że dopiero rozpoczynasz tworzenie projektu i wyświetla przyjazny komunikat). Aby dodać adres URL i widok do URLconf, dopisz Pythonową krotkę zawierającą wzorzez adresu URL i funkcję widoku. Oto w jaki sposób wyglądałoby to dla widoku hello: from django.conf.urls.defaults import *
from mysite.views import hello
urlpatterns = patterns('',
('^hello/$', hello),
)
(Zauważ, że dla zwiększenia czytelności usunęliśmy komentarze. Jeśli chcesz, możesz je pozostawić). Dokonaliśmy dwóch zmian:
W dużym skrócie możemy powiedzieć, że poinstruowaliśmy Django, aby żądania dotyczące adresu URL /hello/ obsługiwało za pomocą funkcji widoku hello. Ścieżka wyszukiwania Pythona Ścieżka wyszukiwania Pythona to lista folderów w systemie, które Python przeszukuje, gdy korzystamy z polecenia importowania. Przypuśćmy, że ścieżka wyszukiwania jest ustawiona na '', '/usr/lib/python2.4/site-packages', '/home/username/djcode']. Jeśli wykonamy polecenie from foo import bar, Python będzie szukał modułu o nazwie foo.py w aktualnym folderze (pusty tekst jako pierwszy element oznacza, aktualny folder). Jeśli plik tam nie istnieje, Python poszuka pliku w /usr/lib/python2.4/site-packages/foo.py. Jeśli i tam go nie znajdzie, poszuka w /home/username/djcode/foo.py. Jeśli tam także go nie odnajdzie, zgłosi wyjątek ImportError. Jeśli chciałbyś sprawdzić zawartość ścieżki wyszukiwania Pythona, uruchom interaktywny interpreter Pythona i wpisz następujący kod: >>> import sys >>> print sys.path Najczęściej nie musisz się martwić o ustawienie poprawniej ścieżki wyszukiwania — Python i Django zajmują się tym automatycznie. (Dodanie odpowiedniej ścieżki wyszukiwania do jedno z zadań skryptu manage.py. Warto choć pokrótce omówić składnię odwzorowania adresów URL, bo może nie być to oczywiste. Chcemy uzyskać dopasowania do adresu /hello/, ale wzorzec jest nieco bardziej złożony. Oto dlaczego:
Zauważ również, że w URLConf przekazaliśmy funkcję widoku hello jako obiekt nie powodując jej wywołania. To jedna z kluczowych cech języka Python (i wielu innych dynamicznych języków programowania): funkcje to pełnoprawne obiekty, więc można je przekazywać w taki sam sposób jak jakiekolwiek zmienne. Wspaniale, prawda? Aby przetestować zmiany w URLconf, uruchom serwer w wersji deweloperskiej w taki sam sposób, jak w rozdziale 2., czyli poleceniem python manage.py runserver. (Jeśli nie wyłączyłeś go od rozdziału 2., nie musisz nic robić. Serwer deweloperski automatycznie wykryje zmiany w kodzie i przeładuje kod. Ręcznie przeładowanie serwera nie jest potrzebne). Serwer działa pod adresem http://127.0.0.1:8000/, więc otwórz przeglądarkę internetową i odwiedź adres http://127.0.0.1:8000/hello/. Powinieneś zobaczyć tekst "Witaj, świecie!" — wynik wykonania widoku Django. Hura! Udało nam się stworzyć pierwszą stronę WWW korzystającą z Django. Wyrażenia regularne Wyrażenia regularne to bardzo zwięzły sposób określania wzorców w tekście. Choć Django umożliwia stosowanie dowolnych elementów wyrażeń regularnych w odwzorowaniach adresów URL na funkcje, prawdopodobnie wystarczy, że zapamiętasz tylko kilka najprostszych symboli wyrażeń regularnych, by objąć większość możliwych sytuacji. Oto kilka popularnych symboli:
Więcej informacji na temat wyrażeń regularnych znajdziesz na stronie http://www.djangoproject.com/r/python/re-module/. Krótka uwaga na temat błędów 404W tym momencie plik URLconf definiuje tylko jeden wzorzec adresu URL; tylko ten obsługujący żądania dla adresu /hello/. Co się stanie, jeśli zażądamy innego adresu URL? Aby się o tym przekonać, uruchom serwer deweloperski i odwiedź stronę, która nie istnieje: http://127.0.0.1:8000/goodbye/, http://127.0.0.1:8000/hello/subdirectory/, lub nawet http://127.0.0.1:8000/ (główny folder witryny). Powinien pojawić się komunikat o braku strony (patrz rysunek 3.2). Django wyświetla ten komunikat, ponieważ żądany adres nie został zdefiniowany w pliku URLconf.
Zwrócona strona zawiera znacznie więcej informacji niż tylko prosty komunikat o błędzie 404. Informuje dokładnie, które wzorce z konfiguracji adresów zostały sprawdzone przez Django. Dzięki tym informacjom łatwo stwierdzić, dlaczego żądany adres zgłosił błąd 404. Oczywiście taka informacja jest kierowana jedynie do Ciebie, twórcy aplikacji. Jeśli witryna została umieszczona na serwerze produkcyjnym i jest publicznie dostępna, zdecydowanie nie chcesz prezentować takiej informacji wszystkim osobom. Z tego powodu prezentowany błąd zostaje wyświetlony tylko w sytuacji, gdy aplikacja znajduje się w trybie testowym. Wkrótce wyjaśnimy, jak wyłączyć tryb testowy. Na razie pamiętaj, że każdy nowoutworzony projekt znajduje się w trybie testowym. Jeśli tryb testowy jest wyłączony, Django wyświetla inny komunikat 404. Jak Django przetwarza żądanie?Zanim przejdziemy do drugiej funkcji widoku, zatrzymajmy się na chwilę i wyjaśnijmy podstawowy sposób działania Django. W szczególności opiszmy, jak to się dzieje, że po odwiedzeniu adresu http://127.0.0.1:8000/hello/ w przeglądarce internetowej, otrzymaliśmy tekst "Witaj, świecie!". Wszystko zaczyna się od pliku ustawień. Po uruchomieniu polecenia python manage.py runserver, skrypt poszukuje pliku nazwanego settings.py w tym samym folderze co manage.py. Plik zawiera różnego rodzaju ustawienia dla tego konkretnego projektu Django, wszystkie zapisane dużymi literami: TEMPLATE_DIRS, DATABASE_NAME itp. Najważniejszym ustawieniem jest prawdopodobnie ROOT_URLCONF. Wartość ta informuje Django, który moduł Pythona powinien być stosowany jako główna konfiguracja adresów URL dla tej witryny. Przypomnij sobie, że polecenie django-admin.py startproject utworzyło pliki settings.py i urls.py? Automatycznie wygenerowany plik settings.py zawiera wartość ROOT_URLCONF wskazującą na automatycznie wygenerowany plik urls.py. Otwórz plik settings.py i obacz to na własne oczy: ROOT_URLCONF = 'mysite.urls' Odpowiada to plikowi mysite/urls.py. Gdy przesyłamy żądanie pobrania zawartości konkretnego adresu URL — na przykład żądania dla /hello/ — Django wczytuje plik URLconf wskazywany przez ustawienie ROOT_URLCONF. Następnie sprawdza wszystkie zawarte tam wzorce w podanej kolejności, porównując za każdym razem adres URL z podanym wzorcem aż do momentu odnalezienia dopasowania. Gdy znajdzie dopasowanie, wywołuje powiązaną z nim funkcję widoku, przekazując obiekt HttpRequest jako pierwszy parametr. (Wkrótce dokładniej omówimy szczegóły obiektu HttpRequest). Jak pokazaliśmy w pierwszej funkcji widoku, musi ona zwrócić obiekt HttpResponse. Gdy to uczyni, Django zajmie się resztą, konwertując obiekt Pythona na odpowiednią odpowiedź serwera z odpowiednimi nagłówkami HTTP oraz treścią (czyli zawartością strony WWW). Podsumowując:
Teraz wiesz już, jak tworzyć strony WWW za pomocą Django. Nie jest to trudne: wystarczy napisać funkcje widoków i odwzorowania adresów na otworzone funkcje w pliku URLconf. Drugi widok — dynamiczna zawartośćPrzykładowy widok "Witaj, świecie!" stanowił dobrą podstawę do pokazania sposobu działania Django, ale nie był przykładem dynamicznej strony WWW, ponieważ zwracana zawartość zawsze była identyczna. Za każdym razem, gdy odwiedzisz stronę /hello/ zobaczysz dokładnie ten same tekst, jakby to była zwykła, statyczna strona HTML. Jako drugi widok utwórzmy coś bardziej dynamicznego — stronę WWW wyświetlającą aktualną godzinę i czas. To kolejny bardzo prosty krok, gdyż nie wymaga bazy danych lub informacji od użytkownika — po prostu zwrócimy wewnętrzną godzinę serwera. Choć sama użyteczność przykładu jest niewiele większa od "Witaj, świecie!", zapewni wprowadzenie kilku nowych elementów. Widok wymaga dwóch rzeczy: obliczenia aktualnej daty i czasu oraz zwrócenia HttpResponse zawierającego te informacje. Jeśli masz doświadczenie z językiem Python, wiesz, że język ten zawiera moduł datetime do obliczania dat. Oto jak z niego skorzystać: >>> import datetime >>> now = datetime.datetime.now() >>> now datetime.datetime(2008, 12, 13, 14, 9, 39, 2731) >>> print now 2008-12-13 14:09:39.002731 Użycie jest bardzo proste i nie ma nic wspólnego z Django. To zwykły kod Pythona. (Chcemy byś wiedział dokładnie, gdzie kod to zwykły Python, a gdzie dodatki wprowadzane przez Django. Poznając Django, będziesz mógł część wiedzy zaaplikować do innych projektów Pythona, które niekoniecznie stosują Django). Aby utworzyć widok Django, który wyświetla aktualną datę i czas, wystarczy dołączyć do widoku wywołanie datetime.datetime.now(), a następnie zwrócić obiekt HttpResponse. Oto wynik tej operacji: from django.http import HttpResponse
import datetime
def current_datetime(request):
now = datetime.datetime.now()
html = "<html><body>Teraz jest %s.</body></html>" % now
return HttpResponse(html)
Podobnie jak wcześniejsza funkcja widoku, także ta powinna znaleźć sięw pliku views.py. Zauważ, że w tym przykładzie z racji przejrzystości pominęliśmy funkcję hello, ale w rzeczywistości plik views.py będzie miał następująca zawartość: from django.http import HttpResponse
import datetime
def hello(request):
return HttpResponse("Hello world")
def current_datetime(request):
now = datetime.datetime.now()
html = "<html><body>Teraz jest %s.</body></html>" % now
return HttpResponse(html)
(Od tej pory w kolejnych przykładach nie będziemy umieszczać prezentowanych wcześniej fragmentów kodu, chyba że okaże się to niezbędne. Z kontekstu łatwo będzie można wywnioskować, czy dany fragment jest nowy, czy stary). Prześledźmy zmiany wprowadzone w views.py prowadzące do powstania widoku current_datetime.
Po dodaniu kodu do views.py, musimy jeszcze dodać wzorzec do pliku urls.py, by poinformować Django o adresie URL, dla którego ma wywołać widok. Użyjmy adresu w postaci /time/: from django.conf.urls.defaults import *
from mysite.views import hello, current_datetime
urlpatterns = patterns('',
('^hello/$', hello),
('^time/$', current_datetime),
)
Dokonaliśmy w tym miejscu dwóch zmian. Po pierwsze, zaimportowaliśmy funkcję current_datetime. Po drugie i ważniejsze, dodaliśmy odwzorowanie adresu URL na nowy widok. Proste, prawda? Po napisaniu widoku i uaktualnieniu URLconf, uruchom serwer i odwiedź adres http://127.0.0.1:8000/time/ w swojej przeglądarce. Powinieneś zobaczyć aktualną datę i czas. Django i strefy czasowe W zależności od komputera, data i czas mogą różnić się o kilka godzin od rzeczywistych wartości. Wynika to z faktu, iż Django jest świadome stref czasowych i domyślnie stosuje strefę America/Chicago. (Musieliśmy wybrać jakąś domyślną strefę, więc wybraliśmy tę, w której mieszkali pierwsi programiści Django). Jeśli mieszkasz w innym rejonie świata, zmień wartość w pliku settings.py. Dokładniejsze informacje o dostępnych strefach czasowych znajdują się w komentarzu powyżej zmiennej. Pliki URLconf i luźne powiązanie komponentówNadszedł właściwy czas, by przyjrzeć się dokładniej kluczowej filozofii kryjącej się za URLconf i Django jako takim: zasadzie luźnego powiązania komponentów. Mówiąc krótko, luźne powiązanie komponentów to technika tworzenia oprogramowania, która ceni sobie łatwość wymiany poszczególnych komponentów. Jeśli dwa komponenty są luźno powiązane, zmiana w jednym z nich będzie miała niewielki lub żaden wpływ na pozostałe elementy. Pliki URLconf z Django to dobry, praktyczny przykład tej zasady. W aplikacji Django, definicje adresów URL i wywoływane przez nie funkcje widoków są luźno powiązane; decyzja co do adresu URL wybranej funkcji i sama funkcja znajdują się w dwóch różnych miejscach. Pozwala to zmienić jeden z elementów bez wpływania na drugi. Przyjrzyjmy się widokowi current_datetime. Gdybyśmy chcieli zmienić adres URL tego elementu aplikacji — np. z /time/ na /current-time/ — wystarczyłoby wprowadzić modyfikację w pliku URLconf bez martwienia się o funkcję widoku. Podobnie, gdybyśmy chcieli zmienić funkcję widoku — zmodyfikować jej logikę lub przenieść do innego modułu — możemy to zrobić bez wpływania na adres URL, do którego funkcja przynależy. Co więcej, gdybyśmy chcieli udostępnić tę samą funkcjonalność związaną z datą pod kilkoma różnymi adresami URL, moglibyśmy to zrobić edytując plik URLconf bez jakichkolwiek zmian w kodzie widoku. W poniższym przykładzie funkcja current_datetime jest dostępna pod dwoma różnymi adresami URL. To bardzo uproszczony przykład, ale sama technika bywa ogromnie pomocna: urlpatterns = patterns('',
('^hello/$', hello),
('^time/$', current_datetime),
('^another-time-page/$', current_datetime),
)
Luźne powiązanie widoków i adresów URL pokazaliśmy na praktycznym przykładzie. Inne przykłady tej bardzo istotnej filozofii pojawią się w dalszych rozdziałach książki. Trzeci widok — dynamiczny adres URLW widoku current_datetime zawartość strony — aktualna data i czas — były dynamiczne, ale same adres URL (/time/) pozostawał statyczny. W większości dynamicznych aplikacji WWW, adres URL zawiera parametry wpływające na wynik działania strony. Przykładowo internetowy sklep z książkami może każdej książce przypisać własny adres URL, na przykład /books/243/ lub /books/81196/. Utwórzmy trzeci widok, który będzie wyświetlał aktualną datę i czas przesuniętą o określoną liczbę godzin. Naszym celem jest stworzenie strony, która po użyciu adresu /time/1/ wyświetli czas o jedną godzinę w przyszłość, dla adresu /time/2/ wyświetli czas o dwie godziny w przyszłość, dla /time/3/ trzy godziny w przyszłość itd. Nowicjusz mógłby zechcieć utworzyć osobną funkcję widoku dla każdego przesunięcia, co spowodowałoby powstanie konfiguracji URL o następującej treści: urlpatterns = patterns('',
(r'^time/$', current_datetime),
(r'^time/plus/1/$', one_hour_ahead),
(r'^time/plus/2/$', two_hours_ahead),
(r'^time/plus/3/$', three_hours_ahead),
(r'^time/plus/4//$', four_hours_ahead),
)
Oczywiście ten kod ma poważne wady. Nie tylko doprowadziłby do powielenia funkcji widoków, ale również sama aplikacja byłaby mocno ograniczona ze względu na obsługę tylko kilku predefiniowanych przesunięć godziny. Gdybyśmy w przyszłości zechcieli utworzyć stronę, która wyświetla przesunięcie o pięć godzin w przyszłość, musielibyśmy utworzyć osobny widok i wpis w URLconf, jeszcze bardziej pogłębiając duplikację kodu. Potrzebujemy abstrakcyjnego rozwiązania. Kilka słów na temat ładnych adresów URL Jeśli tworzyłeś aplikacje dla innej platformy WWW, na przykład PHP lub Java, zapewne myślisz "Hej, użyjmy parametrów adresu URL" i masz na myśli zapis adresu w postaci /time/plus?hours=3, w którym to przesunięcie jest podawane jako jako część parametrów adresu (po znaku ?). W Django również możesz tak zrobić (w dalszej części pokażemy, jak tego dokonać, jeśli rzeczywiście chcesz wiedzieć), ale jedną z podstawowych filozofii Django są ładne adresy URL. Adres URL /time/plus/3/ jest czystszy, prostszy, bardziej czytelny, łatwiejszy do podyktowania i po prostu ładniejszy od swojego parametrowego odpowiednika. Ładne adresu URL to cecha charakterystyczna wysokiej jakości aplikacji WWW. System URLconf Django zachęca do tworzenia ładnych adresów URL, gdyż ich obsługa jest prostsza niż innych rozwiązań. W jaki sposób zaprojektować aplikację, by używała dowolnych przesunięć godzinowych? Kluczem okazuje się użycie znaków wieloznacznych we wzorcu adresu URL. Jak wspomnieliśmy wcześniej, wzorzec jest wyrażeniem regularnym, więc wystarczy, że zastosujemy element \d+, by dopasować się do jednej lub większej liczb cyfr: urlpatterns = patterns('',
# ...
(r'^time/plus/\d+/$', hours_ahead),
# ...
)
(Używamy konstrukcji # ..., by wskazać, że mogą istnieć inne wzorce nie przedstawione w tym przykładzie). Nowy wzorzec dopasuje się do dowolnego adresu URL o strukturze /time/plus/2/, /time/plus/25/ a nawet /time/plus/100000000000/. Ograniczmy więc maksymalne przesunięcie do 99 godzin. Oznacza to, że będziemy dopuszczać wprowadzenie jednej lub dwóch cyfr, więc fragment wyrażenia regularnego przekształcimy w \d{1,2}: (r'^time/plus/\d{1,2}/$', hours_ahead),
Uwaga Gdy projektujesz aplikację WWW, zawsze warto zastanowić się nad najbardziej wymyślnymi danymi wejściowymi i zastanowić się, czy aplikacja powinna je obsługiwać. W tym przypadku ucinamy wszystkie dziwaczne wartości, ograniczając przesunięcie do 99 godzin. Po określeniu elementu wieloznacznego w adresie URL, musimy w jakiś sposób przekazać tę informację do funkcji widoku, by móc wykorzystać jedną funkcję do obsługi dowolnych przesunięć godziny. Zadanie to wykonamy, umieszczając nawiasy okrągłe wokół danych we wzorcu adresu. W tym konkretnym przypadku jako dane do przekazania do funkcji chcemy potraktować część wieloznaczną adresu, więc otocz nawiasami fragment \d{1,2}: (r'^time/plus/(\d{1,2})/$', hours_ahead),
Jeśli używałeś wcześniej wyrażeń regularnych, z pewnością znasz znaczenie nawiasów okrągłych: stosuje się je do zapamiętania danych z dopasowanego tekstu. Końcowa wersja URLconf wraz z dwoma poprzednimi widokami wygląda następująco: from django.conf.urls.defaults import *
from mysite.views import hello, current_datetime, hours_ahead
urlpatterns = patterns('',
('^hello/$', hello),
('^time/$', current_datetime),
(r'^time/plus/(\d{1,2})/$', hours_ahead),
)
Po zakończeniu tematu adresów, możemy przejść do tworzenia widoku hours_ahead. Kolejność pisania kodu W tym przykładzie najpierw napisaliśmy wzorzec adresu URL, a dopiero później widok, ale we wcześniejszych przykładach najpierw napisaliśmy widok, a dopiero później wzorzec adresu. Która technika jest lepsza? Każdy programista pracuje inaczej. Jeśli jesteś osobą dobrze radząca sobie z ogólnym widokiem sytuacji, być może dobrze będzie, jeśli za jednym zamachem, na początku projektu napiszesz wszystkie wzorce adresów, a dopiero później zajmiesz się widokami. Ma to tę zaletę, że dosyć szybko uzyskuje się prostą listę zadań do wykonania wraz z parametrami wymaganymi dla wszystkich tworzonych widoków. Jeśli wolisz pracować od szczegółu do ogółu, możesz najpierw napisać widoki, a następnie dołączyć je do adresów URL. Nic nie stoi na przeszkodzie, by wykonać to właśnie w ten sposób. Wybierz technikę, która pasuje do Ciebie jak najlepiej. Oba podejścia są prawidłowe. Funkcja hours_ahead jest bardzo podobna do napisanej wcześniej funkcji current_datetime z jedną istotną różnicą: przyjmuje dodatkowy argument, liczbę godzin przesunięcia. Oto kod widoku: def hours_ahead(request, offset):
offset = int(offset)
dt = datetime.datetime.now() + datetime.timedelta(hours=offset)
html = "<html><body>Za %s godzin(y) będzie %s.</body></html>" % (offset, dt)
return HttpResponse(html)
Przeanalizujmy kod wiersz po wierszu:
Po napisaniu funkcji widoku i URLconf, uruchom serwer deweloperski Django (jeśli jeszcze nie został uruchomiony) i odwiedź adres http://127.0.0.1:8000/time/plus/3/, by przekonać się czy działa. Następnie odwiedź adresy http://127.0.0.1:8000/time/plus/5/ i http://127.0.0.1:8000/time/plus/24/. Na końcu sprawdź adres http://127.0.0.1:8000/time/plus/100/, by upewnić się, że adres obsługuje tylko jedno lub dwucyfrowe wartości przesunięcia. W ostatnim przypadku Django powinien zgłosić błąd o nieodnalezieniu strony, podobny do tego opisywanego w notce o błędach 404. Adres URL http://127.0.0.1:8000/time/plus/ (bez podania przesunięcia) również zgłosi błąd 404. Ładne strony błędów DjangoPopodziwiaj przez chwilę tę wspaniałą aplikację, która udało się nam napisać... bo za chwilę ją zepsujemy! Celowo wprowadźmy błąd w pliku views.py, tworząc z wiersza offset = int(offset) widoku hours_ahead komentarz: def hours_ahead(request, offset):
#offset = int(offset)
dt = datetime.datetime.now() + datetime.timedelta(hours=offset)
html = "<html><body>Za %s godzin(y) będzie %s.</body></html>" % (offset, dt)
return HttpResponse(html)
Uruchom serwer deweloperski i przejdź do adresu /time/plus/3/. Pojawi się strona błędu z mnóstwem informacji, włącznie z komunikatem o błędzie TypeError umieszczonym na samym początku strony: "unsupported type for timedelta hours component: str". Co się stało? A więc, funkcja datetime.timedelta oczekuje, że parametr hours będzie liczbą całkowitą, a my zamieniliśmy kod dokonujący takiej konwersji na komentarz. Z tego powodu datetime.timedelta zgłosiło błąd. To rodzaj błędu, który wcześniej czy później pojawia się u każdego programisty. Celem tego przykładu było zaprezentowanie stron błędu Django. Dokładnie przeanalizuj stronę, by poznać wszystkie udostępniane przez nią informacje. Zwróć uwagę na kilka interesujących elementów:
Strony błędów są w stanie wyświetlić dodatkowe informacje w niektórych szczególnych sytuacjach, na przykład jeśli wystąpi błąd składniowy szablony. Zagadnieniem tym zajmiemy się w momencie omawiania systemu szablonów Django. Na razie usuń znak komentarza z wiersza offset = int(offset), by funkcja widoku znowu działała prawidłowo. Czy jesteś programistą, który lubi szukać błędów za pomocą odpowiednio umieszczonych instrukcji print? Możesz w tym celu wykorzystać strony błędów Django, ale bez instrukcji print. W dowolnym miejscu widoku tymczasowo umieść polecenie assert False, by wymusić wyświetlenie strony błędu. Następnie możesz przejrzeć zmienne lokalne i stan programu. Oto przykład używający widoku hours_ahead: def hours_ahead(request, offset):
offset = int(offset)
dt = datetime.datetime.now() + datetime.timedelta(hours=offset)
assert False
html = "<html><body>Za %s godzin(y) będzie %s.</body></html>" % (offset, dt)
return HttpResponse(html)
Warto pamiętać, że większość prezentowanych informacji jest bardzo ważna — pojawiają się fragmenty kodu Pythona oraz konfiguracji Django. Byłoby wielką nierozwagą pokazywanie takich informacji publicznie. Nieprzyjazna osoba mogłaby wykorzystać te dane do poznania sposobu działania witryny i zaatakowania jej. Z tego powodu opisywane strony błędów wyświetlane są tylko w trybie testowania projektu Django. Wkrótce wyjaśnimy, jak wyłączyć tryb testowy. Na razie pamiętaj, że każdy nowy projekt znajduje się domyślnie w trybie testowym. (Brzmi znajomo? Opisywane wcześniej strony błędów działają w podobny sposób). Co dalej?Do tej pory tworzyliśmy funkcje widoków z zakodowanym na stałe w Pythonie kodem HTML. Ułatwiało to zaprezentowanie pierwszych przykładów, ale w rzeczywistych aplikacjach to bardzo rzadko jest słuszne rozwiązanie. Django zawiera prosty, ale bardzo użyteczny system szablonów, który umożliwia separację projektu strony od kodu. System szablonów omówimy w następnym rozdziale. |
Rysunek 3.2. Strona błędu 404 z Django