Ajax w Django (ver. 2)

Półtora roku temu opisałem jak zastosować AJAX w aplikacji napisanej w Django. Wybrałem wtedy tworzoną w Pythonowym stylu bibliotekę mochikit i Django 0.96. Opis cieszy się dużą popularnością, ale bogatszy o ponad rok doświadczeń wiem, że teraz zrobiłbym to wszystko o wiele lepiej.

Kiedyś jako przykład użycia ajax stworzyłem proste wiki, ale nie chciało mi się tego opisać. Tym razem wyjaśnię jak napisać prymitywny blog w Django (a jakże!). Pominę jednak kwestię przyjemnego interfejsu i skupię się na jak największej ilości JavaScriptowych wodotrysków.

Wybór biblioteki JavaScript

Swój pierwszy kod w JavaScript napisałem chyba ponad trzy lata temu. Jedyne co pamiętam to biblioteka której użyłem - mootools. Potem zacząłem używać mochikit, bo wydawało mi się, że nic wygodniejszego nie znajdę.

Obecnie, wszędzie gdzie mogę stosuję jQuery. Biblioteka z dobrą dokumentacją, która wiele prostych zadań pozwala wykonać zaskakująco szybko. A ponieważ jest popularna, na stronie z pluginami odnaleźć można wszystko, czego nie znajdzie się w core .

Dlaczego nie wybrałem X albo Y ? Nie potrzebuje niczego więcej poza łatwym w użyciu selektorem i paroma manipulatorami. Wszystko to zapewnia mi jQuery. Bardzo ważna okazała się również jakość kodu jaki powstaje. Używając jQuery, kod (o ile nie pisało go stado wściekłych małp) jest zaskakująco zwięzły i czytelny, przez to szybko można zmodyfikować lub poprawić cudzą pracę.

Aplikacja - blog

Tak jak pierwszym programem w nowym języku musi być Hello world!, tak pierwszą aplikacją jaką napisze się w nowym frameworku webowym jest blog. Efektem końcowym będzie moduł o tej samej funkcjonalności co ostatnio, jednak tym razem wykonany to o wiele lepiej.

Blog bez AJAX

Podstawowa wersja aplikacji bez ajax do pobrania z github. Jaki kod jest, każdy widzi , więc nie będę go opisywał linijka po linijce. Aplikacja jest wręcz prymitywna, aby nawet Ci mniej doświadczeni nie mieli problemów ze zrozumieniem co się dzieje.

Blog z AJAX

Pod tym samym adresem, jednak z innym numerem rewizji, dostępna jest wersja z ajax. Jest to ta sama aplikacja, wzbogacona głównie o parę linijek JavaScript. Wyrzuciłem także automatyczne generowanie forularza komentarzy, co jest całkiem dobrym sposobem na spamboty (i skutecznie blokuje możliwość komentarzy użytkownikom z wyłączonym JavaScript).

To czym jest AJAX pisano już tyle razy, że chyba nie ma potrzeby abym i ja to robił. Mimo, że słowo AJAX brzmi super fajnie i każda poważna strona musi tego używać (bo inaczej nie jest poważna i super fajna), warto wiedzieć że nie jest to żadna magia.

Porównanie najlepiej zacząć od diff obu wersji. W rewizji z ajax dodane zostały dwa pliki z kodem JavaScript (z czego jeden z nich to biblioteka jQuery), niewielkie zmiany zaszły w szablonach i urls.py. Pojawiły się nowe widoki i moduł utils/ajax.py.

blog/templates/blog/entry.html, templates/base.html

Ponieważ o wiele łatwiej znaleźć odpowiednie obiekty DOM gdy posiadają unikalne id, zmodyfikowałem szablon wstawiając parę pustych elementów. Dołączyłem pliki z kodem JavaScript.

utils/ajax.py

Pisząc w JavaScript asynchroniczne zapytania, zamiast XML wolę używać JSON. Wszystkie widoki którymi obsługuję takie zadania zwracają obiekt HttpResponse z danymi w odpowiednim formacie. Ponieważ nie chce powtarzać kodu i za każdym razem sprawdzać czy zapytanie rzeczywiście jest asynchroniczne, a potem konwertować obiekty Pythona na JSON i zwracać HttpResponse, napisałem dekorator json_response. Jedyne o czym trzeba pamiętać, to żeby funkcja dekorowana zwracała łatwo parsowalny obiekt.

blog/views.py, blog/urls.py

Pojawiły się trzy nowe widoki - get_comment_form, post_comment oraz comment_preview, jednak tylko dwa z nich są bezpośrednio dostępne poprzez url.

Ponieważ chcę aby komentarze dodawać można było jedynie w asynchroniczny sposób, zmieniłem widok entries_list tak, aby wszystkie zapytania POST obsługiwane były przez post_comment.

Zadaniem get_comment_form jest zwrócenie formularza do komentarzy. Widok dekorowany jest z użyciem json_response dlatego zwraca słownik. Dzięki temu że do renderowania formularza użyłem oddzielnego szablonu i napisałem dla niego osobny templatetag, mogę go teraz użyć i pod kluczem form trzymać gotowy HTML, który potem wrzucam na stronę.

post_comment obsługuje POST, sprawdzając poprawność danych i zapisując przesyłane komentarze. Zwraca słownik danych z kluczem status dzięki któremu w JavaScript łatwo mogę się dowidzieć jak serwer zareagował na przesłane informacje oraz w zależności od rezultatu. Jeśli formularz zawierał błędy, form zawiera wyrenderowany formularz z informacją co należy poprawić. Jeśli jednak dane były poprawne i zostały zapisane, comment zawiera wyrenderowany komentarz. Komentarze, podobnie jak formularz, obsługiwane są osobnym templatetagiem.

comment_preview to po prostu podgląd. Użytkownik przesyła dane, a serwer niezależnie od ich poprawności próbuje utworzyć z tego komentarz, wrzuca do szablonu, renderuje html i wysyła wynik.

statics/javascript/ajax_comments.js

ajax_comments.js zawiera cały kod JavaScript jaki napisałem na potrzeby tej aplikacji. Jeśli nie znasz JavaScript to najwyższy czas to zmienić. Warto też zapoznać się z możliwościami biblioteki jQuery.

Pierwsza funkcja która wykonana zostanie po załadowaniu strony to getCommentForm, która umieszcza na stronie formularz do komentowania. Używając jQuery.get, wysyłam do serwera GET, a obsługę odpowiedzi jaką ten udzieli przekazuje do funkcji getCommentCallback. Jeśli wszystko działa jak należy, showCommentForm umieści w odpowiednim miejscu przekazany kod HTML, wzbogacając go o przycisk podglądu. Ponieważ wysyłanie danych z formularza ma być obsługiwane przez JavaScript, do obudwu przycisków podpinam odpowiednie funkcje - submitForm oraz previewSubmit, które wyglądają bardzo podobnie. Obie przesyłają POST z danymi z formularza, korzystając z jQuery.post. Różnią się jednak url pod jaki kierowane jest żądanie oraz funkcjami obsługującymi odpowiedź serwera. previewSubmitCallback wrzuca otrzymane dane do kontenera comment-preview. submitFormCallback jest niewiele bardziej skomplikowana, ponieważ w zależności od otrzymanej odpowiedzi wyświetla nowy formularz, bądź chowa formularz i wyświetla wysłany przez użytkownika komentarz. Aby przeglądarka nie wysłała formularza w tradycyjny sposób, z przeładowaniem strony, funkcja zwracać musi wartość false.

Debugowanie

Na koniec mały hint . Pewnie każdy zna plugin firebug. Otóż, jednym z wielu jego możliwości jest logowanie komunikatów. Zamiast używać co najmniej niewygodnego alert(), lepiej zastosować logowania na jakie pozwala wtyczka ;)

Tomasz Elendt

24.08.2009

Radzę nie używać const. opis const w dokumentacji JS 1.5 na MozDev.