Znów straciłem wiele godzin w poszukiwaniu błędu, opiszę ten przykład, bo jest pouczający. Robiłem program partnerski do pewnego przedsięwzięcia. Jak to w tego typu programach bywa, trzeba było pobrać pewien kod z formularza i go zwalidować.
Jedna z funkcji, które to robiły, miała postać:
def _evaluate_partner_code(self, code): try: partner = Partner.objects.get(code=code) # prevent from using your own partner code to discount your own # order if partner.user == request.user: return False self.partner = partner self.amount = DEFAULT_PARTNER_DISCOUNT return True except: return False
Funkcja mimo zastosowania kodu, który znajdował się w bazie dawała False. Oczywiście cały czas podświadomie zakładałem, że błąd powoduje Partner.objects.get(code=code)
. Badałem czy code jest odpowiedniego typu, próbowałem konwertować na stringa, włączałem shell, sprawdzałem czy jest taki partner... Wszystko niby działa a jednak nie działa.
W końcu, nic już nie rozumiejąc, postanowiłem obejrzeć jaki wyjątek jest wyrzucany i wstawiłem asercje:
def _evaluate_partner_code(self, code): try: partner = Partner.objects.get(code=code) # prevent from using your own partner code to discount your own # order if partner.user == request.user: return False self.partner = partner self.amount = DEFAULT_PARTNER_DISCOUNT self.komunikat = 'ok' return True except Exception, e: self.komunikat = 'wyjatek: %s' % e return False
Okazało się, że... nie zdefiniowana jest nazwa "request"!!!
Wystarczyło oczywiście dopisać "request" do argumentów funkcji. Działa!
Wniosek z tego taki, że zawsze opłaca się raportować rodzaj zwracanych błędów!
Przy okazji warto zwrócić uwagę na sposób testowania aplikacji internetowych. Django daje świetne narzędzie jakim jest shell, ale jeśli błąd jest w funkcjach obsługujących żądanie http, w szczególności w funkcjach widoku, to użycie shella jest ograniczone - w shellu nie ma właśnie żądania. Tak było w powyższym przykładzie, żądanie http jest ukryte właśnie w zmiennej request.
Ja sobie z tym radzę w ten sposób, że korzystam z funkcji HttpResponse, która generuje bezpośrednio odpowiedź http.
Np. jeśli mam wstawioną asercję, która ustawia zmienną 'komunikat', to żeby ją wyświetlić można dać w funkcji widoku:
return HttpResponse(komunikat)
.