Zacznę od czegoś związanego z optymalizacją. Optymalizacja to ostatni etap rozwoju aplikacji, ale kto powiedział że zaczynamy :).
Standardowo do renderowania strony z szablonu używa się funkcji render_to_response. Jest ona prosta i wygodna w użyciu. Jednak nie jest ona optymalna. Za każdym razem pliki z szablonami są ładowane z dysku i parsowane, a dopiero następnie strona jest renderowana. Więc, aby przyśpieszyć proces renderowania, wystarczy zapamiętywać wczytane i sparsowane szablony do późniejszego użycia. Aby najpierw załadować plik z dysku i go sparsować wystarczy napisać:
from django.template import loader
temp = loader.get_template(template)
Ta funkcja zwraca nam klasę. Wystarczy wywołać funkcję render z tej klasy, aby wygenerować stronę z danym kontekstem (czyli listą asocjacyjną ze zmiennymi).
temp.render(context)
Gdzieś trzeba zapisać taki szablon. Najlepszym wydaje się być globalna lista asocjacyjna. Używanie w tym przypadku pamięci cache, może być nieoptymalne, a nawet kłopotliwe. Zwrócona klasa z szablonem to nie jest jedna klasa, a całe drzewo ze wszystkimi elementami które są w szablonie. Serializacja takiej ogromnej struktury danych na pewno nie przebiega zbyt szybko. Używanie globalnej zmiennej nie jest w tym przypadku niebezpieczne, nawet w wielowątkowej aplikacji, ponieważ zapis do niej robimy tylko raz, przy pierwszym wywołaniu szablonu. Potem idą tylko odczyty.
No to zobaczmy jak wygląda moja funkcja z cachem:
templates = {}
def django_template_to_string( request, variables, template ):
if not DEVEL:
temp = templates.get( template )
if temp is None:
temp = loader.get_template(template)
templates[template] = temp
else:
temp = loader.get_template(template)
c = RequestContext(request)
c.update(variables)
return temp.render(c)]
Należy pamiętać, że teraz szablony są trzymane w pamięci, co jest niewygodne podczas rozwijania aplikacji. Jeżeli zmienimy coś w pliku, to nie zostanie to zauważone, bo to nie jest plik kodu Pythona tylko szablon. Dlatego też pojawiła się tam zmienna DEVEL. Należy ją ustawić aby było tak jak poprzednio. Funkcja ta z ustawioną tą zmienną, działa prawie identycznie co stare dobre render_to_response.
Dla ułatwienia pracy warto jest użyć dekorator, aby wszystko się działo automatycznie. Jest to o tyle wygodne, że nie musimy używać żadnych dodatkowych funkcji, które pojawiały się przy render_to_response. W przypadku dekoratora dzieje się to w nim. Użycie go jest bardziej elastyczne, więc gorąco go polecam.
decorator_with_args = lambda decorator: lambda *args, **kwargs:
lambda func: decorator(func, *args, **kwargs )
@decorator_with_args
def django_template(func, template=None):
def wrapper(request,*args,**kwargs):
variables = func(request, *args, **kwargs )
string = django_template_to_string( request, variables, template )
return HttpResponse( string )
wrapper.__name__ = func.__name__
wrapper.__dict__ = func.__dict__
wrapper.__doc__ = func.__doc__
return wrapper</code>
Teraz użycie jest prostsze niż domyślnie, zobaczmy:
@django_template("moja_strona.html")
def master_home(request):
variables = { 'title' : "Hello World!" }
return variables
Żadnego render_to_response. Ok, zobaczmy jakie jest przyśpieszenie dla dość skomplikowanej strony:
render_to_response: Time per request: 223ms django_template.py: Time per request: 101ms
Jak widać jest to bardzo znacząca różnica.To są moje wyniki dla mojej strony. Pełny kod źródłowy: django_template.py
Jak kolorujecie kod :P?
" Ta funkcja zwraca nam klasę. "
Tak z ciekawości - nie powinno być "ta metoda zwraca nam obiekt (ew. obiekt klasy)"?
Pewnie kolorują VIM-em i konwertują do HTML.
Można też zaprządz Django do tego: <a href="http://www.djangoproject.com/documentation/cache/">Django’s cache framework</a>
Kolorujemy popularną biblioteką do pythona o nazwie pygmentize. Potrafi ona wygenerować kod HTML dla dowolnego języka i zwrócić osobno style CSS.
A w Django można używać cache, ale o doświadczeniach z tym jeszcze napiszemy :). Do tego przypadku nie warto tego używać, bo serializacja wygenerowanych obiektów zajęła by sporo czasu, niepotrzebnie zresztą. Tak jest łatwiej, wygodniej i szybciej :)
Ciekawy sposób na przyspieszenie pracy, jednak zignorowanie tego, co zwraca View niesie za sobą pewien problem, mianowicie np. HttpResponseRedirect czy HttpResponse wewnątrz widoku zostaje zignorowany i zawsze (nawet jeśli View niczego nie zwraca) jest renderowany podany w argumencie dekoratora szablon. Jak np. w tym momencie przekierować user'a na inną stronę po pomyślnym zalogowaniu?
To nie jest trudne. W dekoratorze wystarczy sprawdzić czy zwrócona wartość to jest słownik, czy też jest to instancja obiektu HttpResponse. Dzięki temu będziemy mogli używać tego w standardowy sposób.
Coś w stylu:
variables = func(request, *args, **kwargs ) if isinstance( variables, HttpResponse): return variables else: ...Lub też w tychże miejscach możemy zrezygnować z używania dekoratora i robić to po staremu. Użycie tego sposobu nie ogranicza nas :)
Faktycznie działa - i faktycznie proste. Dzięki za megaszybką odpowiedź ;-)
fajnie by było gdyby można było to podpiąć pod generic views. moze da sie to jakos zrobic bez ingerowania w django?
też tak bym chciał ;]