Django handler500, request.is_ajax() i AngularJS

Kolejny wypadek Django przy pracy, który powinien przekonać niedowiarków i wyznawców Django, że ten framework nadaje się do stawiania blogasków, ale niespecjalnie jako server side dla aplikacji.

handler500 i DEBUG=True

Przy włączonym DEBUG handlery 403,404 i 500 nie są używane. Do akcji wkraczają tzw. technical responses, które generują specjalny output dla poszczególnych błędów. Z reguły są one bardzo przydatne, ale pisząc server pod klienta w AngularJS w response otrzymujemy... piękny html ze stylami i css. Przeczytanie błędu graniczy z cudem, a zdevelopowanie poprawnej obsługi błędu w kliencie jest niemożliwe w trybie DEBUG. Niemożliwe, bo nie da się podmienić technical responses. Są one wołane w core handlerze, więc nie ma na to szans.

Możemy w konfiguracji podmienić exception filter, ale on jest używany przez exeption reporter a to na jego wymianie lub rozszerzeniu nam zależy, jeśli chcemy cokolwiek sensownego zrobić z obsługą błędów 403,404,500 w trybie DEBUG!

AngularJS i request.is_ajax()

Implementacja is_ajax() opiera się na jakiejś konwencji. Po pierwsze - skoro to konwencja, to powinno dać się ją zmienić na potrzeby projektu. Oczywiście nie da się, podobnie jak nie da się prosto wymienić klasy HttpRequest, w której podmieniłbym is_ajax (trzeba grzebać low-level na poziomie handlerów wsgi). Monkey patching mnie nie interesuje - readability counts.

Natomiast można skonfigurować AngularJS do współpracy ze zmaszczonym niespecjalnie rozsądnymi rozwiązaniami Django, wystarczy skonfigurować nagłówek:

angular.module('myApp').config(function($httpProvider) {
    $httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
});

A co zrobili Django developers?

Ano dodali kiedyś takiego paszkwila w odpowiedzi na zgłoszenie:

if request.is_ajax():
    text = reporter.get_traceback_text()
    return HttpResponseServerError(text, content_type='text/plain')

Świetnie, ale to nie rozwiązuje problemu, bo:

  • HTTP_ACCEPT mam application/json i chcę mieć w response JSON z błędem (!!!)
  • niewiadomo jaki tekstowy response jest słabo parsowalny przez jakąkolwiek implementację JSON-a

Django developers są szczęśliwi i dumni ze swojego podejścia znanego jako "Django ethos". Sukcesów życzę i omijania Django w dużych projektach.

Django CSRF + AngularJS

Zapisuję w formie notatki, aby nikogo (w tym mnie) nie kusiło użycie csrf_exempt ;)

Dodanie obsługi CSRF w AngularJS 1.1.5+ polega na skonfigurowaniu nazw nagłówka i cookie:

.config(function($httpProvider) {
    $httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
    $httpProvider.defaults.xsrfCookieName = 'csrftoken';
})

Responsive design

Jako przeciwnik responsive i zwolennik dedykownych wersji mobilnych stron, serwisów czy aplikcji ostatecznie uznaję, że responsive design w podejściu mobile-first jest w pewnych zastosowaniach nie tylko rozwiązaniem dobrym, ale przede wszystkim wyrazem szacunku dla czytelnika/użytkownika. Szczególnie dotyczy to blogów, portali, ale także nieskomplikowanych aplikacji web czy choćby pojedynczych modułów.

Stawiam jednocześnie tezę, że istnieje rozwiązanie dla pewnej grupy aplikacji web, które z perspektywy UI oraz usability sprawdzi się na każdym urządzeniu, począwszy od telefonu a skończywszy na desktopie. Wkrótce napiszę coś więcej w temacie, wraz z przykładem.

Cóż, tylko krowa nie zmienia poglądów...

RQ - Alternatywa dla Celery Task Queue?

Celery

Celery był obiecującym pakietem umożliwiającym kolejkowanie i asynchroniczne wykonywanie zadań, oraz w pewnym stopniu rozproszenie systemu. Kiedyś udało mi się nawet pracować z w miarę stabilną wersją, lecz wskutek błędu związanego z task.retry zostałem zmuszony poszukać rozwiązania w upgrade pakietu. Obecnie major release Celery nosi numer 3 i niestety niewiele się zmieniło w kwestii stabilności.

Autor pakietu, Ask Solem, ma łeb na karku ale wziął sobie na niego za dużo projektów. Mam wrażenie, że facet już nad tym nie panuje. Celery opiera się na jego dwóch innych produktach: Kombu i Billiard. Kombu zastępuje kilka starszych pakietów (również jego autorstwa). Billiard zastępuje standardowy multiprocessing (autor chciałby, aby kiedyś znalazł się w dystrybucji Pythona). Niestety Billiard również jest delikatnie zmaszczony.

Za tymi pakietami głównie włóczy się smród Django, choć Ask Solem stara się od dawna pozbyć tejże zależności. Włóczy się też atmosfera niestabilności mimo zaklęć w setup.py.

Główne wady Celery:

  • brak stabilności mimo intensywnego rozwoju, nieoczekiwane zachowanie podczas pracy
  • przeładowanie features, z których podstawowe nadal nie działają stabilnie
  • morderstwo standardowego loggera
  • zbyt dużo punktów podatnych na błędy i awarie (celery -> kombu -> billiard -> amqplib -> erlang -> rabbitmq)
  • zależności z django na każdym kroku
  • trudność w debugowaniu
  • brak możlwości samodzielnej poprawy przez zbyt skomplikowaną budowę pakietu.

RQ

RQ jest prostą implementacją kolejkowania zadań dla Pythona. Autor Vincent Driessen postawił na prostotę i czas wdrożenia. Przede wszystkim nie ma zależności od Django, choć istnieje pakiet ułatwiający taką integrację. Widać tu wyraźnie, jak wyglądał proces tworzenia pakietu. Nie ma mowy o zaszłościach i nieuzasadnionych zależnościach.

Zasada działania RQ jest prostsza od Celery, a punktów podatnych na wywalenie jest o wiele mniej - właściwie sam RQ albo Redis. Uruchomienie workerów pod kontrolą supervisord jest dziecinnie proste. Wielki plus dla łatwej integracji z Sentry.

Funkcje uruchamiane przez workery nie muszą być opakowywane żadnymi dekoratorami. Są zwykłymi funkcjami, którym worker przekaże argumenty. Jedyny warunek to konieczność umieszczenia funkcji w module (nie da się zdefiniować funkcji w __main__, albo użyć jakiegoś builtina), ale to ma marginalne znaczenie. Na koniec warto zauważyć, że pakiet nie ma idiotycznej nazwy - spróbujcie poszukać w Google czegoś o Celery.

Wady i zalety

Wady:

  • Python only
  • instancja Redis per projekt
  • rozpraszanie workerów za pomocą dostawiania instancji Redisa
  • brak wbudowanego mechanizmu task.retry
  • wolniejsze uruchamianie zadań (process fork)

Zalety:

  • szybkość i prostota wdrożenia
  • podobne high-level API do Celery, które pozwala na łatwą migrację
  • mało potencjalnych miejsc awarii
  • odporność na wycieki pamięci tasków