Django, Rails i współdzielenie danych

Opublikowane przez Jarosław Zabiełło Wed, 19 Jul 2006 19:22:00 GMT

Osoby mające wcześniej do czynienia z innymi frameworkami, gdy sięgną do Django mogą być trochę zdezorientowane sposobem w jaki należy przekazywać zmienne do wspólnych części serwisu. Aby lepiej zrozumieć problem, podam wpierw jak jest on rozwiązywane w Railsach.

Otóż, Railsy zakładają, że każdy kontroler dziedziczy po wspólnym kontrolerze zwanym ApplicationControler. A że jest to normalna klasa Rubiego, więc nic dziwnego, że wszelkie jej metody i zmienne (instancji) są automatycznie dziedziczone we wszystkich jej klasach potomnych. To bardzo intuicyjne rozwiązanie. Jeśli chcemy przekazać jakieś wspólne dane do wszystkich kontrolerów i ich szablonów, wystarczy te dane zdefiniować w tym miejscu. Weźmy np. poniższy kod.

# plik: app/controllers/application.rb
class ApplicationController < ActionController::Base
  before_filter :defaults
  def initialize
    @name = "Jarek"
  end
  def defaults
    @msg = "Hello!"
  end
end

# plik: app/controllers/home_controller.rb
class HomeController < ApplicationController 
end

# plik: app/controllers/about_controller.rb
class AboutController < ApplicationController 
  def index
    @dodatkowa = "wartość"
  end
end

Zakładając, że nie zmienialiśmy domyślnego działania resolvera adresów URL, wpisanie strony http://localhost:3000/home wywoła domyślną metodę index() z klasy HomeController. Jeśli jej nie ma (jak na naszym przykładzie), to Rails zakłada, że chcemy wywołać jej szablon tak, jakby ta metoda istniała. Wczytywany jest zatem plik app/views/home/index.rhtml. Dzięki temu, że w klasie ApplicationController zdefiniowaliśmy 2 zmienne instancji (@name i @msg) automatycznie są one dziedziczone przez klasę HomeController i tym samym dostępne w w/w szablonie. Identycznie będzie z drugą klasą, jej szablon również będzie miał obie, zdefiniowane wyżej, zmienne1.

W wypadku Django, sprawy wyglądają inaczej dlatego, że kontrolery3 nie są metodami klas Pythona! Są one metodami modułu Pythona. Co prawda moduł też jest obiektem, ma własną przetrzeń nazw itp. ale posiada jedną zasadniczą różnicę wobec klasy – nie można dziedziczyć modułu od modułu. Tym samym próba współdzielenia zmiennych musi być zrobiona zupełnie inną metodą.

Trochę z tym problemem się męczyłem, bo w dokumentacji mało na ten temat piszą. Dopiero na IRC podsunięto mi link do artykułu Django tips: Template context processors, który wszystko ładnie wyjaśnił2. Okazuje się, że jest kilka sposobów rozwiązania tego problemu. Napisanie własnego MiddleWare (ech, nie chciało mi się, to armata na muchę), napisanie własnego znacznika dla szablonów (w wypadku mojej małej aplikacji, to też byłaby przesada) lub wykorzystanie tzw. procesora kontekstu (Context Processor). I to było to!

Wystarczy stworzyć sobie plik (o nazwie np. context_processors.py) z funkcjami implementującymi to, co chcemy wrzucić do wszystkich szablonów.

def name(request):
  return 'Jarek'

def msg(request):
  return 'Hello!'

Następnie trzeba dodać w settings.py informację że będziemy tego używać:

TEMPLATE_CONTEXT_PROCESSORS = (
  'myapp.context_processors.name',
  'myapp.context_processors.msg',
  ) 

W wypadku generic views to wszystko, bo one automatycznie wciągają RequestContext dla swoich szablonów. W wypadku naszych kontrolerów (zakładając, że używamy render_to_response() aby wyświetlać szablon) trzeba na końcu metody render_to_response() dodać jeden parametr ‘context_instance’. Czyli:

# wpierw wciągamy RequestContext
from django.template import RequestContext

def home(request):
  return render_to_response(
    'home.html', 
    context_instance=RequestContext(request))

def about(request):
  return render_to_response(
    'about.html', 
    {'dodatkowa': 'wartość'},
    context_instance=RequestContext(request))

Tak coraz bardziej wgłębiając się w Rails i Django widzę, że Rails jest jednak bardziej intuicyjny i łatwiej się go nauczyć. Jednakże Django wydaje się mieć sporo potężnych mechanizmów, których odkrywanie (mam nadzieję) się uprości jak tylko jego twórcy opublikują pierwszą książkę na jego temat (ma być dostępna także w wersji online za darmo!)

-

1 W podanym wyżej przykładzie wykorzystałem dwa różne sposoby przekazania zmiennych. Za pomocą konstruktora i za pomocą metody ‘before_filter’. Wiadomo, że konstruktor nie nadaje się do przekazywania wszystkiego. Czasami musimy przekazać coś, co istnieje już po wywołaniu konstruktora. Wtedy przydaje się metoda ‘before_filter’. To jedna z wielu sztuczek jakie ma w swym arsenale Rails aby kod był czysty, klarowny i zgodny z zasadą DRY (nie powtarzania się).

2 Zobacz też artykuł How Django processes a request?.

3 W Django nazywane są one metodami widoku. Osobiście nie podoba mi się zamieszanie terminologiczne jakie Django zrobiło z modelem MVC. Kontroler to dla nich Widok (View), zaś Widok to Szablon. Trochę to bez sensu, bo nikt tak tych rzecz nie nazywa. Czyli djangowe metody widoku to nic innego jak akcje kontrolera dla Rails i reszty świata.

Posted in ,  | Tagi ,  | 2 comments

Django i Rails biją PHP

Opublikowane przez Jarosław Zabiełło Fri, 14 Jul 2006 15:34:00 GMT

Porównanie wydajności trzech frameworków: Symfony, Ruby on Rails i Django pokazuje że Rails jest znacznie szybszy od pehapowego Symfony, a Django znacznie szybszy od Railsów. Co ciekawe, PHP5 używał akceleratora.

Posted in , ,  | Tagi , ,  | 19 comments