Konfiguracja Apache'a w Django

Dotąd w zasadzie nie zastanawiałem się nad sposobem konfiguracji Apache'a w Django, stosowałem po prostu przepis z dokumentacji. Że zastanowić się warto, uświadomił mi kolega Jakub.
Zwrócił mi on uwagę, że roboty wyszukujące spodziewają się często znaleźć plik favicon.ico w głównym katalogu czyli DocumentRoot witryny.
Zastanówmy się się więc punkt po punkcie nad przepisem ze wspomnianej dokumentacji. Zakładam, że mamy porawnie zainstalowanego apache'a i mod-pythona (jeśli nie, to tu jest instrukcja po polsku jak to zrobic pod ubuntu).

W dokumentacji Django znajdziemy mniej więcej następujący przykład:

<Location "/">
    SetHandler python-program
    PythonHandler django.core.handlers.modpython
    SetEnv DJANGO_SETTINGS_MODULE mysite.settings
    PythonPath "['/path/to/project'] + sys.path"
</Location>
 
<Location "/media">
    SetHandler None
</Location>
 
<LocationMatch "\.(jpg|gif|png|ico)$">
    SetHandler None
</LocationMatch>

Przede wszystkim ktoś może zapytać gdzie ten kod umieścić. Możliwe jest to nawet w plikach .htaccess, ale nie polecam tego rozwiązania, choć kiedyś je z powodzeniem stosowałem. Obecnie jednak serwery VPS są już tak tanie, że nie widzę potrzeby męczenia się z jakimś dzielonym hostingiem. Tak więc możemy umieścić ten kod albo w /etc/apache2/sites-available/default (tak jest standardowo), albo jeśli mamy większą ilość projektów i chcemy zachowac porządek, możemy tworzyć dodatkowe pliki apache'a.
Najczęściej będziemy używali virtualhosta, czyli we wspomnianym pliku default napiszemy:

<VirtualHost *:80>
      ServerName jakas.tam.domena.com
      # w tym miejscu wstawiamy cały kod, który przytoczyłem powyżej
</VirtualHost>

Czas zastanowić się co tak naprawdę zrobiliśmy. Samej sekcji VirtualHost komentować nie będę, zakładam, że każdy wie co to jest. Następnie w ramach tego serwera wirtualnego mamy czyli swoisty "katalog główny" Dyrektywy tu umieszczone bedą stosowane, gdy żądanie http odnosić się będzie do głównego katalogu tej domeny, czyli http://jakas.tam.domena.com/.
Co nakazaliśmy zrobić apache'owi? W dwóch następnych linijkach przekazujemy to żądanie do obsługi przez mod_python a następnie przez Django. Trzecia linia
SetEnv DJANGO_SETTINGS_MODULE settings

jest bardzo istotna. Przekazuje ona informację o pliku settings.py odpowiedzialnym za naszą domenę. Ma to ścisły związek z następną linią:
PythonPath "['/path/to/project'] + sys.path"

Jest to apache'owy odpowiednik systemowego PYTHONPATH. Ścieżka "/path/to/project" powinna wskazywać na katalog, w którym znajduje się plik settings.py. Teoretycznie powinno działać również inne rozwiązanie, mianowicie jeśli plik settings.py znajduje się w jednym z podkatalogów projektu np. w katalogu app, to ścieżka w PythonPath może nadal wskazywać na główny katalog projektu, ale w SetEnv powinniśmy napisać:
SetEnv DJANGO_SETTINGS_MODULE app.settings

Jest to zgodne z notacją kropkową Pythona, w której hierarchię modułu oznacza się pisząc od strony lewej co prawej coraz niższe stopnie i rozdzielając kropkami.

Dalej mamy

<Location "/media">
    SetHandler None
</Location>

oraz

<LocationMatch "\.(jpg|gif|png|ico)$">
    SetHandler None
</LocationMatch>

Te elementy w moim przekonaniu nie są w dokumentacji Django opisane wystarczająco jasno. Jest tak zapewne dlatego, że twórcy dokumentacji Django założyli, że czytelnik umie konfigurować Apache'a...

I to jest właśnie moment, od którego rozpocząłem i ku któremu zmierzam.
Jeśli ktoś konfigurację Apache'a zna, to zauważył na pewno, że w konfiguracji tego virtualhosta w ogóle nie ma dyrektywy DocumentRoot, a mimo to taka konfiguracja działa, z wyjątkiem... no właśnie: z wyjątkiem plików statycznych, bez których żaden serwis nie może przecież funkcjonować! Plikami statycznymi są przede wszystkim arkusze stylów i obrazki, mogą jednak być nimi inne pliki specyficzne dla danego serwisu - np. dokumenty pdf.
Żeby udostępniać pliki statyczne, trzeba wybrać katalog na dysku, który będzie je przechowywał (oczywiście może on zawierać podkatalogi). Załóżmy, że ten katalog, to /var/www/jakas.tam.domena.com (można wybrac dowolny katalog).

Przed znacznikiem wstawmy linię

Alias /media "/var/www/jakas.tam.domena.com"

Teraz już serwer wie skąd brać statykę - żądanie http://jakas.tam.domena.com/media będzie skierowane do katalogu /var/www/jakas.tam.domena.com. Do tego zdaje się zmierzać dokumentacja Django.

Wydaje się, że wszystko jest ok. Tym bardziej, że znacznik <LocationMatch "\.(jpg|gif|png|ico)$"> ,
wraz z dyrektywą SetHandler None powinien nam zapewnić, że nawet jeśli w głównym katalogu serwisu pojawi się jakiś obrazek, powiedzmy logo.jpg, to zostanie on poprawnie wyświetlony... Opsss! Zaraz! W głównym katalogu? A gdzie tu jest główny katalog?

No właśnie. Nie ma. Nie istnieje!

Dlatego ja jestem za utworzeniem dyrektywy:

DocumentRoot /var/www/jakas.tam.domena.com/

Wtedy wystarczy w katalogu /var/www/jakas.tam.domena.com/ utworzyć podkatalog media. Tworzenie aliasu nie jest nam potrzebne, a z katalogu głównego możemy np udostępniać plik favicon.ico ku radości tych co lubią robić dobrze robotom ;)

Całość konfiguracji wygląda ostatecznie tak:

<VirtualHost *:80>
      ServerName jakas.tam.domena.com
      DocumentRoot /var/www/jakas.tam.domena.com/
      <Location "/">
          SetHandler python-program
          PythonHandler django.core.handlers.modpython
          SetEnv DJANGO_SETTINGS_MODULE mysite.settings
          PythonPath "['/path/to/project'] + sys.path"
       </Location>
 
       <Location "/media">
          SetHandler None
       </Location>
 
       <LocationMatch "\.(jpg|gif|png|ico)$">
           SetHandler None
        </LocationMatch>
</VirtualHost>

Odpowiedzi

Pączkowanie...

Sprawa miała być pierwotnie banalna, ale widzę, że trzeba było się trochę natrudzić.
Przy okazji jest oczywiście sporo dodatkowych zastosowań umieszczania obiektów (plików) w katalogu głównym (np. pliki weryfikacji wyszukiwarek - nie każdy lubi zaśmiecać kod strony dodatkowymi tagami meta).