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

Facebook z G+ zabijają RSS-y

Nigdy nie było zbyt różowo, jeśli chodzi o publiczny content dostarczany kanałami RSS na wielu stronach, mimo że implementacja (szczególnie RSS 1.0) sprowadza się do przekształcenia "newsów" (bo o nich głównie myślę) na listę elementów składających się z trzech pól: tytułu, opisu i adresu URL. Ale w czasach popularyzacji Facebooka oraz próbującego go dogonić Google+ mamy do czynienia z postępującym zanikiem dystrybucji informacji przez RSS.

Doliczyłem się trzech sposobów publikacji informacji na strumieniach portali społecznościowych: ręczny przez właściciela (wpisanie treści wiadomości na ścianie), ręczny przez innych na ścianie właściciela, automatyczny (integracja za pomocą FB API, najpierw post pisany jest na stronie właściciela i automatycznie postowany na FB). Za wyjątkiem postowania przez innych na ścianie właściciela pozostałe sposoby są podobnie pracochłonne, więc przyczyna postowania na FB musi być jednak inna i nie zależy od czasu przygotowania i wysłania informacji. Może to wynika z lepiej widocznej audiencji, a może po prostu z faktu, iż na FB ludzie są z reguły zalogowani stale i nie muszą logować się do nieprzyjemnego panelu CMS-a, mogą wpisać co myślą a co nie jest jednocześnie oficjalnym newsem, albo po prostu często współdzielą jednym kliknięciem treść pisaną przez kogoś innego. Sęk w tym, że Facebook i G+ zachowują te informacje wyłącznie dla siebie, mimo że właściciel publikuje publicznie.

Dobijanie RSS następuje dokładnie w tym momencie. Ani FB ani G+ nie udostępniają contentu na zewnątrz, choćby właśnie w omawianych kanałach RSS. Jakiś czas temu FB wyłączył feedy rss/atom. Zmuszają przez to do rejestracji i korzystania z ich nie-do-końca-darmowych usług. Na dowód tego Google zniszczyło jeden ze swoich lepszych produktów - Google Reader. Interfejs FB/G+ nie jest tym, jaki lubię, a do tego nie chcę rejestrować się i zgadzać na nieodpowiadające mi warunki tylko dlatego, że chcę czytać publiczne newsy, które mogą być emitowane przecież w tradycyjny sposób bez dodatkowego nakładu pracy. Niestety wina leży też po stronie odbiorców, którzy nie domagają się RSS, ale też uznają ten kanał za używany wyłącznie przez geeków (niektórzy mylą geeków z hipsterami, ale to przejaw ignorancji i drwin z ludzi używających wygodnego narzędzia). Znam osoby korzystające z RSS nie będące geekami.

Jak temu zaradzić? Mam dwa pomysły, obydwa mające jednak spore wady. Pierwszy to zgłaszanie próśb do administratorów/webmasterów stron o dodanie kanałów RSS, a drugi to utworzenie projektu "news4rss", który byłby wrapperem dla stron typu "aktualności" czy wpisów blogowych nie posiadających kanałów RSS. Wady to odpowiednio brak zainteresowania ze strony właścicieli stron (dodatkowe koszty, dodatkowa praca - mimo że relatywnie niewielka!), oraz w drugim przypadku dokładanie swoich "trzech groszy" do zabijania RSS (poprzez brak zainteresowania tym kanałem ze strony właścicieli). Może działanie na tych dwóch płaszczyznach przyniesie pozytywny efekt?