Wstęp do Django 1.0 - artykuł

2009-01-31 // Kategoria: Artykuł // Tagi: Django, Python, Programowanie

Artykuł opisuje Django jako narzędzie do tworzenia prostych i zaawansowanych aplikacji webowych. Django cechuje szybkość i łatwość tworzenia aplikacji oraz czysta i pragmatyczna architektura. Artykuł przedstawia historię powstawania frameworku, jego główne funkcjonalności jak również architekturę. W części praktycznej zostanie opisany proces tworzenia prostej aplikacji webowej przy użyciu Django. Udowodnimy w ten sposób, że pisząc niecałe 100 linii kodu jesteśmy w stanie stworzyć kompletną aplikację webową.

Poniższy artykuł został opublikowany w książce "PyCon PL 2008", która została wydana w październiku 2008

Wstęp

Django jest frameworkiem wysokiego poziomu, napisanym w języku Python, który pomaga tworzyć proste i zaawansowane aplikacje webowe. Cechuje go szybkość i łatwość tworzenia aplikacji, oraz czysta i pragmatyczna architektura, oparta na regule DRY (ang. "Don't Repeat Yourself").

Historia

Zanim zagłębimy się w szczegóły techniczne i architekturę, warto zatrzymać się na chwilę, aby dowiedzieć się skąd się wzięło Django i co wpłynęło na jego obecny kształt. Historia Django sięga roku 2003, kiedy grupa programistów (Adrian Holovaty, Simon Willison i Jacob Kaplan-Moss) związana z gazetą Lawrence Journal-World w Kansas zaczęła tworzyć wersję internetową tejże gazety. Przez dwa lata programiści pisali na własne potrzeby biblioteki i narzędzia, z których ostatecznie wyodrębnił się samodzielny framework. W roku 2005 postanowiono otworzyć kod źródłowy frameworku i udostępnić go na licencji BSD pod nazwą Django. Nazwa frameworku została nadana od imienia cygańskiego gitarzysty Django Reinhardta, który jest jednym z ulubionych wykonawców Adriana Holovaty. Od tamtego czasu Django bardzo się rozpowszechniło, zdobyło dużą popularność i jest powszechnie używane do tworzenia stron i aplikacji webowych. Jego walory wykorzystywane są zarówno w prostych blogach, jak również w zaawansowanych serwisach Web 2.0 (przykładem jest polski serwis społecznościowy Grono.net).

Aktualnie dostępna wersja Django to 1.0.2.

Główne funkcje

Django jest kompletną i samodzielną platformą do tworzenia aplikacji webowych. Jego podstawowymi elementami są: serwer HTTP, interaktywna konsola (działająca bezpośrednio w środowisku aplikacji), skrypty (automatyzujące takie czynności jak tworzenie bazy danych, importowanie i eksportowanie danych) oraz duży zbiór funkcji i bibliotek.

W skład samego frameworku wchodzą:

  • mechanizm mapowania obiektowo-relacyjnego (ang. ORM - object-relational mapping), który odwzorowuje obiekty na strukturę bazy danych,
  • automatyczny interfejs administracyjny - moduł do zarządzania danymi przechowywanymi w bazie danych (przeglądanie, dodawanie, usuwanie i edycja),
  • zarządzania adresami URL – przyjazny i oparty na wyrażeniach regularnych mechanizm do mapowania adresów URL do konkretnych widoków URL,
  • system szablonów – prosty i jednocześnie funkcjonalny język dla grafików i programistów do generowania plików (HTML, XML, CSV, itp),
  • formularze – mechanizm do tworzenia i przetwarzania HTML-owych formularzy,
  • uwierzytelnianie i autoryzacja – mechanizm logowania do systemu i zarządzanie dostępem do zasobów (stron lub danych),
  • cache – buforowany dostęp do danych,
  • lokalizacja - wsparcie dla wielojęzycznych aplikacji.

Architektura

W procesie tworzenia architektury Django jego autorzy korzystali z koncepcji wzorca projektowego MVC (ang. Model-View-Controller). Idea ta determinuje podział frameworku na trzy niezależne warstwy: model danych, widok i logikę. Tym samy także w Django możemy wyodrębnić trzy niezależne warstwy. Jednakże autorzy frameworku pozwolili sobie na małą modyfikację tworząc własny wzorzec Model-View-Template. W modelu tym każdej z warstw przypisujemy integralny zakres odpowiedzialności:

  • model - odpowiedzialny jest za zarządzanie danymi (dodawanie, czytanie,

    usuwanie i modyfikowanie), które są przechowywane w bazie danych.

  • widok - odpowiedzialny za wyciągnięcie odpowiednich danych,

    przetworzenie ich i przekazanie do użytkownika (za pośrednictwem szablonów).

  • szablon - odpowiedzialny za prezentację wyników widoków.

Pomiędzy poszczególnymi warstwami następuje nieustanny obieg informacji. Proces ten opisać można w kilku krokach:

  1. Przeglądarka pyta serwer o konkretny adres URL.
  2. Django przetwarza plik urls.py w poszukiwaniu wzorca pasującego do adresu URL.
  3. Kiedy wzorzec zostanie dopasowany, Django przekazuje zapytanie do odpowiadającego widoku.
  4. Widok przetwarza dane z bazy danych i wygenerowany wynik przekazuje do szablonu.
  5. Szablon wyświetla dane dostarczone przez widok.

Graficzna prezentacja tego procesu zilustrowana jest poniżej:

/site_media/apps/archive/schemat.png

Instalacja

Przystępując do instalacji Django należy zwrócić uwagę na fakt, że Django jest dystrybuowane w kilku różnych wersjach. Zatem jego instalacji można wykonać z gotowych pakietów dla określonej dystrybucji, albo ściągając gotową paczkę z oficjalnej strony lub bezpośrednio z repozytorium wersję rozwojową. W zależności od wybranej wersji, proces instalacji frameworku będzie różnił się nieznacznie. Więcej szczegółów na ten temat można znaleźć na oficjalnej stronie projektu. Poniżej znajduje się przykład, w jaki sposób zainstalować Django bezpośrednio z repozytorium Subversion.

W celu pobrania Django na lokalny dysk wykonujemy następujące polecenie:

~$ svn co http://code.djangoproject.com/svn/django/trunk/ django

Następnie za pomocą polecenia setup.py install instalujemy Django w katalogu Pythona site-packages.

~$ cd django
~/django$ sudo python setup.py install

Ostatnim krokiem jest pobranie odpowiedniego sterownika bazy danych. Dla każdego rodzaju bazy należy zainstalować odpowiedni pakiet:

  • dla bazy PostgreSQL wymagany jest pakiet psycopg,
  • dla bazy MySQL wymagany jest pakiet MySQLdb,
  • dla bazy SQLite wymagany jest pakiet pysqlite,
  • dla bazy Oracle wymagany jest pakiet ox_Oracle.

Pierwsza aplikacja

Aby przybliżyć faktyczne możliwości Django, stworzymy przykładową aplikację. Będzie to prosty blog składający się z dwóch modułów:

  • moduł publiczny, którego zadaniem jest wyświetlanie listy

    wpisów oraz szczegółów o danym wpisie,

  • moduł administracyjny, który jest dostępny wyłącznie dla

    zalogowanych użytkowników i jest odpowiedzialny za zarządzania wpisami (dodawanie, edycja i usuwanie).

Let's get ready to rumble!

Na początku stworzymy podstawową strukturę naszego projektu z domyślnymi plikami. W tym celu wykorzystamy skrypt django-admin.py, który jest dostarczony razem z Django i znajduje się w katalogu bin.

Wykonujemy następujące polecenie:

~$ django-admin.py startproject webapp

Skrypt tworzy katalog webapp służący do przechowywać kodu źródłowego aplikacji. Ponadto wygenerowane są cztery pliki:

~$ ls -1 webapp/
__init__.py
manage.py
settings.py
urls.py

Już na tym etapie możemy uruchomić aplikację. Aby tego dokonać wykonujemy polecenie manage.py runserver w następujący sposób:

~$ cd webapp
~/webapp$ python manage.py runserver
Validating models...
0 errors found

Django version 1.0.2 final, using settings 'webapp.settings'
Development server is running at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
[29/Jul/2008 10:02:36] "GET / HTTP/1.1" 200 2049
/site_media/apps/archive/01_it-worked.png

Moduł Administracyjny

Kolejnym krokiem jaki wykonamy jest konfiguracja modułu administracyjnego. Stworzenie tego modułu jest niezbędne w celu zarządzania użytkownikami oraz zawartością bloga. Aby stworzyć moduł administracyjny wystarczy:

  • skonfigurować bazę danych (parametry DATABASE_ENGINE

    i DATABASE_NAME w pliku settings.py),

  • dodać panel administracyjny (należy dodać jedną linię w pliku

    settings.py i odkomentować trzy linie w pliku url.py.

settings.py:

# Silnik bazy danych
DATABASE_ENGINE = 'sqlite3'

# Nazwa bazy danych (dla SQLite jest to ścieżka do pliku bazy)
DATABASE_NAME = 'database.db'

# Ustawienia domyślnego języka dla aplikacji
LANGUAGE_CODE = 'pl'

# Lista modułów
INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.admin',
)

urls.py:

from django.conf.urls.defaults import *

# Uncomment the next two lines to enable the admin:
from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns('',
    # Example:
    # (r'^webapp/', include('webapp.foo.urls')),

    # Uncomment the next line to enable admin documentation:
    #(r'^admin/doc/', include('django.contrib.admindocs.urls')),

    # Uncomment the next line for to enable the admin:
    (r'^admin/(.*)', admin.site.root),
)

Następnie tworzymy strukturę bazy danych ponownie korzystając z pomocy skryptu manage.py. W tym procesie tworzymy także pierwszego użytkownika z prawami administratora. Skrypt zapyta nas o parametry konta administratora, czyli nazwę użytkownika, hasło oraz adres email.

~/webapp$ python manage.py syncdb

Creating table auth_permission
Creating table auth_group
Creating table auth_user
Creating table auth_message
Creating table django_content_type
Creating table django_session
Creating table django_site
Creating table django_admin_log

You just installed Django's auth system, which means you don't have any superusers defined.
Would you like to create one now? (yes/no): yes
Username: root
E-mail address: root@root.com
Password:******
Password (again): ******
Superuser created successfully.

Installing index for auth.Permission model
Installing index for auth.Message model
Installing index for admin.LogEntry model

Możemy teraz uruchomić naszą aplikację. Aby podglądnąć efekt dotychczasowej pracy uruchamiamy manage.py runserver i pod adresem http://127.0.0.1:8000/admin/ pojawi się panel administracyjny. Już na tym etapie dostępne są dla nas funkcje zarządzania użytkownikami.

/site_media/apps/archive/02_login.png /site_media/apps/archive/03_admin.png

Moduł publiczny

Pierwszą część (moduł administracyjny) mamy już za sobą. Teraz możemy przystąpić do napisania kodu właściwego, który będzie odpowiedzialny za wyświetlanie wpisów i ich szczegółów na stronie bloga. Złożą się na to trzy podstawowe etapy:

  • stworzenie modelu,

  • stworzenie widoku, który pozwala na wydobycie odpowiednich

    danych z modelu,

  • stworzenie szablonu do wyświetlenia danych, zaprojektowanie

    adresów URL dla poszczególnych stron.

Zaczynamy od stworzenia modułu i w tym celu ponownie skorzystamy ze skryptu manage.py:

~webapp$ python manage.py startapp blog
~webapp$ ls -1 blog/
__init__.py
models.py
views.py

Skrypt utworzył katalog blog a w nim trzy puste pliki: __init__.py, models.py (model danych) i views.py (widok). Należy teraz zaprojektować model danych (plik models.py). Model ten będzie zawierał tytuł ("title"), unikalna etykieta ("slug"), treść ("body") i datę publikacji ("pub_date"). Przykład zawartości pliku blog/models.py znajduje się poniżej:

from django.db import models
from django.contrib import admin

class Entry(models.Model):
    """ Model danych reprezentujący wpis """

    # Tytuł wpisu
    title = models.CharField('Tytul', max_length=200)

    # Unikalna (dla daty) etykieta wpisu
    slug = models.SlugField(unique_for_date='pub_date')

    # Treść wpisu
    body = models.TextField('Tresc')

    # Data publikacji
    pub_date = models.DateField('Data publikacji')

    # Nazwa pod jaką będzie wyświetlany wpis w panelu administracyjnym
    def __unicode__(self):
       return u'%s' % (self.title)

    # URL wpisu (np. /2008/08/03/pycon-pl/)
    def get_absolute_url(self):
            return "/%s/%s/" % (self.pub_date.strftime("%Y/%m/%d"),
                  self.slug)

    class Meta:
        """ Metadane – opis i konfiguracja modelu """

        # Nazwa obiektu czytelna dla człowieka
        verbose_name = 'Wpis'

        # Nazwa obiektu czytelna dla człowieka w liczbie mnogiej
        verbose_name_plural = 'Wpisy'


class EntryAdmin(admin.ModelAdmin):
    """ Model danych aby był widoczny w panelu administracyjnym musi mieć
    zarejestrowaną klasę ModelAdmin """

    # Automatyczne generowanie pola 'slug' na podstawie pola 'title'
    prepopulated_fields = {'slug': ('title',)}

# Rejestracja klasy EntryAdmin aby była widoczna w panelu administracyjnym
admin.site.register(Entry, EntryAdmin)

Teraz rejestrujemy panel publiczny poprzez dodanie modułu blog do pliku settings.py (parametr INSTALLED_APPS). Jest to operacja analogiczna do tej jaką wykonaliśmy przy dodawaniu panelu administracyjnego (django.contrib.admin).

settings.py:

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.admin',
    'blog',
)

W kolejnym etapie skupimy się na tworzeniu widoku (plik views.py), który będzie bezpośrednio korzystał z modelu danych (plik models.py). W przykładzie tworzonego bloga w widoku wyróżnimy trzy funkcje:

  • strona główna (latest) – wyświetlanie najnowszy wpisów,
  • archiwum (archive) – archiwum wpisów (lata, miesiące i dni),
  • szczegóły (detail) – wyświetlanie szczegółów wpisu.

Zawartość pliku blog/views.py znajduje się poniżej:

from webapp.blog.models import Entry
from django.shortcuts import render_to_response, get_object_or_404
from django.db.models import Q

import datetime

def latest(request):
    """ Lista ostatnich wpisów """

    # Pobranie z bazy danych ostatnich pięciu wpisów
    entries = Entry.objects.all().order_by('-pub_date')[:5]

    # Przekazanie list wpisów (entries) do szablonu list.html
    return render_to_response('list.html', {'entries': entries })

def archive(request, year, month = 0, day = 0):
    """ Archiwum wpisów """

    # Tworzenie zapytania do bazy danych, które pobierze wpisy opublikowane
    # w podanym roku – parametr year
    query = Q(pub_date__year = int(year))

    # Dodanie zapytania o konkretny miesiąc
    if month != 0:
        query &= Q(pub_date__month = int(month))

    # Dodanie zapytania o konkretny dzień
    if day != 0:
        query &= Q(pub_date__day = int(day))

    # Pobranie z bazy danych wpisów pasujących do zapytania
    entries = Entry.objects.filter(query)

    # Przekazanie list wpisów (entries) do szablonu list.html
    return render_to_response('list.html', {'entries': entries })

def detail(request, year, month, day, slug):
    """ Wyświetlanie szczegółów wpisu """

    date = datetime.date(int(year), int(month), int(day))

    # Pobranie z bazy danych wpisu pasującego do podanej daty i sluga
    entry = get_object_or_404(Entry, pub_date = date, slug = slug)

    # Przekazanie pojedynczego wpisu (entry) do szablonu detail.html
    return render_to_response('detail.html', {'entry': entry })

Pozostało nam już tylko zaprojektowanie interfejsu użytkownika. Aby tego dokonać tworzymy szablony, które generują strony HTML. Dla potrzeb bloga w katalogu template tworzymy dwa szablony: listę oraz szczegóły wpisów. Każdy z tych szablonów jest odrębnym plikiem:

  • listę wpisów bloga zapisujemy w pliku template/list.html.

    Zawiera on: tytuł, data publikacji, skrót treści (pierwsze 20 wyrazów) a także link do szczegółów.

  • szczegóły wpisu znajdują się w pliku template/detail.html.

    Zawiera on: tytuł, data publikacji oraz pełną treść wpisu.

Przykład zawartości pliku template/list.html:

{% for entry in entries %}
    <h2>{{ entry.title }}</h2>
    <p>{{ entry.pub_date|date:"d F Y" }}</p>
    {{ entry.body|truncatewords_html:"20"|safe }}
    <p><a href="{{entry.get_absolute_url}}">czytaj więcej...</a></p>
{% endfor %}

Przykład zawartości pliku template/detail.html:

<h1>{{ entry.title }}</h1>
<p>{{ entry.pub_date|date:"d F Y" }}</p>
{{ entry.body|safe }}

Następnie w pliku settings.py odszukujemy zmienną TEMPLATE_DIRS i dodajemy do niej katalog template. W ten sposób wskazujemy Django gdzie ma szukać stworzonych wcześniej szablonów list.html i detail.html.

settings.py:

TEMPLATE_DIRS = (
    'template',
)

Jedną z unikalnych funkcji Django jest możliwość projektowania adresów URL. Do tego celu Django udostępnia nam gotowe narzędzie (plik urls.py), które do mapowania adresów URL na konkretne widoki wykorzystuje wyrażenia regularne. Na przykład adresowi /2008/08/12/ będzie odpowiadała funkcja detail w pliku views.py.

Na potrzeby bloga stworzyliśmy plik blog/urls.py zawierający mapowanie adresów URL na widoki. Przykład znajduje się poniżej:

from django.conf.urls.defaults import *

urlpatterns = patterns('webapp.blog.views',
    # Szczegóły (np. /2008/08/03/pycon-pl/)
    (r'(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/(?P<slug>[-\w]+)/$', 'detail'),

        # Archiwum dla konkretnej daty (np. /2008/08/03/)
    (r'(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/$', 'archive'),

    # Archiwum dla konkretnego roku i miesiąca (np. /2008/08/)
    (r'(?P<year>\d{4})/(?P<month>\d{2})/$', 'archive'),

    # Archiwum dla konkretnego roku (np. /2008/)
    (r'(?P<year>\d{4})/$', 'archive'),

    # Głowna strona (/)
    (r'^$', 'latest'),
)

Aby zakończyć proces tworzenia blogu niezbędna jest jeszcze jedna mała modyfikacja. Do głównego pliku urls.py dodajemy referencje do wcześniej stworzonych adresów URL (plik blog/urls.py).

urls.py:

from django.conf.urls.defaults import *

# Uncomment the next two lines to enable the admin:
from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns('',
    # Uncomment the next line to enable admin documentation:
    #(r'^admin/doc/', include('django.contrib.admindocs.urls')),

    # Uncomment the next line for to enable the admin:
    (r'^admin/(.*)', admin.site.root),

    # Referencja do pliku blog/urls.py
    (r'^', include('webapp.blog.urls')),

System Message: WARNING/2 (<string>, line 567)

Explicit markup ends without a blank line; unexpected unindent.

)

Na koniec uaktualniamy strukturę bazy danych o świeżo stworzony model Entry. W tym celu uruchamiamy skrypt manage.py syncdb, który na podstawie zdefiniowanego modelu utworzy niezbędne tabele w bazie danych.

~/webapp$ python manage.py syncdb

Creating table blog_entry
Installing index for blog.Entry model

Teraz pozostaje nam już tylko uruchomić aplikację wykonując polecenie manage.py runserver.

~/webapp$ python manage.py runserver

Validating models...
0 errors found

Django version 1.0-alpha-SVN-unknown, using settings 'webapp.settings'
Development server is running at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
[29/Jul/2008 10:02:36] "GET / HTTP/1.1" 200 2049

Po wprowadzeniu adresu http://127.0.0.1:8000/ w przeglądarce internetowej dostępna jest nasza nowo napisana aplikacja. A pod adresem http://127.0.0.1:8000/admin/ mamy dostęp do Panelu Administracyjnego, gdzie możemy wprowadzać nowe dane.

Panel Administracyjny - głowne okno

/site_media/apps/archive/04_admin_2.png

Panel Administracyjny - wprowadzanie danych

/site_media/apps/archive/05_wpis.png

Głowna strona - lista wspisów

/site_media/apps/archive/06_lista.png

Szczegóły wspisu

/site_media/apps/archive/07_szczegoly.png

Podsumowanie

Przedstawiony pokrótce proces tworzenia aplikacji z wykorzystaniem frameworku Django pokazuje jedynie mały fragment możliwości jakie oferuje to narzędzie. Artykuł udowadnia, że pisząc niecałe 100 linii kodu jesteśmy w stanie stworzyć kompletną aplikację webową. To powoduje, że z roku na rok Django zyskuje kolejnych zwolenników wśród firm i indywidualnych programistów systemów internetowych.

Warto zobaczyć

Aby dobrze poznać Django i wszystkie oferowane przez niego funkcje, warto korzystać z dokumentacji i przykładów gotowych aplikacji i bibliotek. Poniżej przedstawiam listę stron, które warto regularnie odwiedzać:



Marcin Mierzejewski, 18 październik 2008