Table of Contents
Django
Installare i pacchetti Debian
- python-django
- libapache2-mod-wsgi
Vedere il tutorial Writing your first Django app.
Progetto e applicazioni
- Progetto
- Applicazione: Contenitore di viste (pagine web) e modelli di dati (tabelle del database).
Per creare un nuovo progetto:
mkdir ~/django_projects cd ~/django_projects django-admin startproject my_project cd ~/django_projects/my_project
Creare un database vuoto e configurare il file settings.py
con le credenziali di accesso. Per popolare il database con le tabelle di servizio lanciare il comando:
python manage.py syncdb
Le tabelle create dipendono anche dalle INSTALLED_APPS
definite in settings.py
. Alcune apps fanno delle domande durante l'installazione iniziale, ad esempio installando django.contrib.auth
verrà chiesto se attivare un account di amministratore.
Per creare un'applicazione all'interndo del progetto:
python manage.py startapp my_app
Il progetto grossomodo corrisponde al sito web, dentro il progetto ci sono una o più applicazioni, cioè unità funzionali (moduli Python, in effetti) che possono eventualmente essere riutilizzate tra un progetto e l'altro. Ad esempio l'applicazione django.contrib.auth
fornita con Django stesso può essere inclusa per fornire il framework necessario all'autenticazione degli utenti.
Per aggiungere l'applicazione al progetto si modifica l'array INSTALLED_APPS
in settings.py
:
INSTALLED_APPS = ( 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'my_app' )
Nella scelta dei nomi per il progetto e le applicazioni ricordarsi che:
- Il progetto non può avere lo stesso nome di un modulo built-in Python oppure di un modulo Django. Quindi - ad esempio - non sono validi nomi come
django
otest
. - Una applicazione non può avere lo stesso nome del progetto.
I modelli (le tabelle con i dati)
Nell'applicazione si crea un modello, cioè si descrive i dati che l'applicazione gestisce. In pratica il modello diventa una tabella nel database. Si edita il file my_app/models.py
mettendo qualcosa del genere:
from django.db import models from django.contrib.auth.models import User class Expense(models.Model): user = models.ForeignKey(User, verbose_name = 'persona') description = models.CharField('descrizione', max_length=200) date_time = models.DateField('data') amount = models.FloatField('importo') def __unicode__(self): return u'%s, %s' %(self.date_time, self.description) class Meta: ordering = ['-date_time', 'description']
Nell'esempio sopra il modello Expense sarà memorizzato nella tabella my_app_expense
, il campo user
sarà legato con un rapporto di FOREIGN KEY con la chiave primaria della tabella auth_user
. Notare che la tabella auth_user
deriva da un'altra applicazione (django.contrib.auth
), quindi è necessario fare l'import opportuno prima di poterla usare.
Il metodo __unicode__()
del modello verrà usato in tutte le occasioni in cui Django deve mostrare un record della tabella, altrimenti il record sarà presentato come un generico Expense object.
La classe Meta
consente di aggiungere informazioni non essenziali, ad esempio l'ordinamento predefinito dei dati in questa tabella.
Il codice SQL necessario a creare le tabelle per i dati dell'applicazione viene generato con il comando:
python manage.py sql my_app
Si potrebbe copiare le istruzioni SQL al prompt del database, oppure chiedere a Django di eseguire tutte le istruzioni necessarie (Django verifica quali oggetti esistono già nel database) con il comando:
python manage.py syncdb
NOTA: il comando syncdb si accorge se una tabella esiste già oppure deve essere creata, ma ad esempio non si accorge se una colonna deve essere aggiunta oppure è stata modificata.
L'applicazione admin
Vedere The Django admin site.
Le oprazioni di base sui dati (i modelli, cioè le tabelle del database), vale a dire INSERT, DELETE, UPDATE, ecc. possono essere fatti con l'applicazione fornita con django.contrib.admin
. È sufficiente attivarla nel nostro progetto.
Nel file settings.py
del progetto aggiungere all'array INSTALLED_APPS
l'applicazione django.contrib.admin
. Ovviamente si deve eseguire il python manage.py syncdb
per sincronizzare l'applicazione con il database.
Se vogliamo che i dati della nostra applicazione siano gestibili con l'interfaccia admin bisogna creare un file my_app/admin.py
con le seguenti istruzioni:
from my_app.models import Expense from django.contrib import admin admin.site.register(Expense)
Infine si deve “attivare” l'URL dell'interfaccia admin, editando il file urls.py
del progetto, in particolare si devono scommentare tre righe nell'esempio creato automaticamente:
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'^expenses/', include('expenses.foo.urls')), # Uncomment the admin/doc line below to enable admin documentation: # (r'^admin/doc/', include('django.contrib.admindocs.urls')), # Uncomment the next line to enable the admin: (r'^admin/', include(admin.site.urls)), )
L'interfaccia per l'editing della tabella è fortemente personalizzabile definendo un oggetto di tipo admin.ModelAdmin
, tale oggetto va definito nel file my_app/admin.py
ed utilizzato nella funzione admin.site.register()
:
from my_app.models import Expense from django.contrib import admin class ExpenseAdmin(admin.ModelAdmin): list_display = ('date_time', 'description', 'amount') list_filter = ['date_time'] search_fields = ['description'] ordering = ['-date_time'] admin.site.register(Expense, ExpenseAdmin)
Creare una nuova pagina
Vista
Nel file my_app/views.py
si definisce una funzione Python che restituisce una pagina web, questa sarà una vista della nostra applicazione:
from django.http import HttpResponse def my_view(request): output = u''' <html> <body> <h1>%s</h1> </body> </html> ''' % ( u'Titolo della vista' ) return HttpResponse(output)
URL
Nel file urls.py
contenuto alla radice del progetto si mappa un nuovo URL sulla vista, cioè sulla funzione Python definita in precedenza nel modulo my_project.views
. L'URL viene aggiunto nell'array urlpatterns
, oltre agli altri eventualmente esistenti:
from my_app.views import * urlpatterns = patterns('', (r'^my_url/', my_view), )
La pagina sarà visibile all'URL /my_url/
(vedere più avanti come configurare l'URL base del progetto).
Mettere i dati nella vista
Invece di includere il codice HTML direttamente nella vista (funzione Python) si utilizza il sistema dei template.
Nella directory radice del progetto si crea la sottodirectory templates
e nel file settings.py
si definisce la TEMPLATE_DIRS
(controllare se il modulo os.path è già incluso):
import os.path TEMPLATE_DIRS = ( os.path.join(os.path.dirname(__file__), 'templates), )
Quindi si crea il file templates/my_view.html
che conterrà dei segnaposto del tipo {{nome}}
che verranno sostituiti a runtime:
<html> <body> <h1>{{page_title}}</h1> <p>{{page_body}}</p> </body> </html>
Infine si modifica il codice della vista:
from django.http import HttpResponse from django.template import Context from django.template.loader import get_template def my_view(request): template = get_template('my_view.html') variables = Context({ 'page_title': u'Titolo della vista', 'page_body': u'Contenuto della pagina' }) output = template.render(variables) return HttpResponse(output)
Visualizzazione e aggregazione dei dati
Nel codice della view è possibile accedere ai dati (i model) sfruttando anche le relazioni tra essi (le foreign key delle tabelle del database). È possibile anche usare funzioni di aggregazione.
Ecco un esempio di una vista dell'applicazione spese (my_project/spese/views.py
), che accede ai dati del modello User
e del modello correlato Expense
(sono tabelle correlate). Nella vista si aggiunge un attributo al modello User
, aggregando per ogni User
il campo amount
del modello Expense
, calcolando la Sum
e il Count
:
from django.http import HttpResponse from django.template import Context from django.template.loader import get_template from django.db.models import Sum, Count from django.contrib.auth.models import User #from spese.models import Expense def balance(request): users = User.objects.all() max_expense = 0 for user in users: user.aggr = user.expense_set.aggregate(Count('amount'), Sum('amount')) if user.aggr['amount__sum'] > max_expense: max_expense = user.aggr['amount__sum'] for user in users: user.balance = user.aggr['amount__sum'] - max_expense template = get_template('balance.html') variables = Context({ 'page_title': u'Bilancio spese', 'users': users, 'max_expense': max_expense }) output = template.render(variables) return HttpResponse(output)
Ecco infine il template my_project/templates/balance.html
che provvede ad impaginare i dati:
<html> <body> <h1>{{page_title}}</h1> {% if users %} <table border="1" cellspacing="0"> <tr> <th>Persona</th> <th>Numero spese</th> <th>Somma spesa</th> <th>Bilancio</th> </tr> {% for user in users %} <tr> <td>{{user.username}}</td> <td>{{user.aggr.amount__count}}</td> <td>{{user.aggr.amount__sum}}</td> <td>{{user.balance}}</td></tr> {% endfor %} </table> {% else %} <p>No users found.</p> {% endif %} </body> </html>
Webserver di test
Per lanciare il webserver di test (da non usare in produzione):
python manage.py runserver 127.0.0.1:8000
L'opzione predefinita 127.0.0.1:8000 può essere omessa, è possibile indicare altri indirizzi IP, oppure 0.0.0.0 per fare il bind su tutti gli indirizzi.
Una cosa molto utile del webserver integrato è che si accorge se uno dei file sorgenti viene modificato, in tal caso ricarica il progetto senza bisogno di essere riavviato.
Webserver Apache
Per eseguire progetti Django con Apache si consiglia di usare il modulo wsgi, vedere il tutorial How to use Django with Apache and mod_wsgi.
La configurazione che vogliamo realizzare è la seguente:
- La configurazione riguarda ad un solo
VirtualHost
. - Uno o più progetti Django sono accessibili da una sottodirectory della
DocumentRoot
. - I file di progetto sono salvati fuori dalla
DocumentRoot
.
Fare attenzione soprattutto al punto #3: i file sorgenti del progetto Python non devono essere pubblicati nella DocumentRoot
.
All'interno della DocumentRoot
si crea una sottodirectory - ad esempio di nome webapps
- e nella configurazione del VirtualHost si dichiara quale script deve essere servito come WSGI:
<VirtualHost> ... # Django applications. WSGIScriptAlias /webapps/my_project /var/www/VirtualHost/webapps/my_project.wsgi #WSGIScriptAlias /webapps/ /var/www/VirtualHost/webapps/ </VirtualHost>
Nell'esempio si inserisce una riga WSGIScriptAlias per ogni progetto. In alternativa (riga commentata) si può indicare che tutti gli script presenti nella directory devono essere serviti come script WSGI, in questa seconda ipotesi conviene nominare gli script senza estensione .wsgi.
Abbiamo preferito la prima ipotesi perché in tal modo è possibile creare nella directory webapps
un index.html
per elencare le applicazioni esistenti. Inoltre è possibile creare nella stessa directory un link simbolico che punta ai file (CSS, JavaScript, immagini) usati dalle applicazioni. In pratica il contenuto del VirtualHost
non viene modificato, ad eccezione della nuova directory webapps
.
Nella directory webapps
si crea il file WSGI per ogni progetto Django, ad esempio my_project.wsgi
:
import os import sys path = '/usr/local/lib/django' if path not in sys.path: sys.path.append(path) path = '/usr/local/lib/django/my_project' if path not in sys.path: sys.path.append(path) os.environ['DJANGO_SETTINGS_MODULE'] = 'my_project.settings' import django.core.handlers.wsgi application = django.core.handlers.wsgi.WSGIHandler()
Il file con il progetto Django devono risiedere in una directory fuori dalla DocumentRoot
, nell'esempio /usr/local/lib/django/
.
L'unica modifica che deve essere fatta al progetto (in settings.py
) riguarda la variabile ADMIN_MEDIA_PREFIX
, si imposta:
ADMIN_MEDIA_PREFIX = '/webapps/admin_media/'
Nella directory webapps si crea il link simbolico:
admin_media -> /usr/share/pyshared/django/contrib/admin/media
Fortunatamente la variabile ADMIN_MEDIA_PREFIX
viene ignorata quando si utilizza il webserver di test.
Compilazione del codice Python
L'esecuzione del codice Python viene velocizzato (soprattutto l'avvio) compilando i file .py
nei rispettivi .pyc
. Ciò avviene automaticamente se Python ha i permessi per scrivere nella directory, ma se il codice Django viene eseguito via WSGI l'utente è www-data
che di solito non ha il permesso di scrivere nella directory del progetto.
Dare i permessi all'utente www-data
di scrivere nella directory che contiene i sorgenti del progetto non è una buona idea. C'è chi sostiene che il guadagno di performance nell'avere i file compilati è minimo, in quanto il processo WSGI rimane caricato in memoria a lungo e l'overload c'è solo nella fase iniziale di caricamento.
Ad ogni modo, lo sviluppatore che volesse compilare tutti i moduli dopo aver apportato delle modifiche, esegue nella directory radice del progetto:
python -m compileall .
Permessi
Fare attenzione al file settings.py
ed al suo compilato settings.pyc
relativi al progetto: contengono le credenziali di accesso al database. È necessario che siano leggibili al server web, ma è opportuno che non siano leggibili al mondo.
Tips and tricks
Formattazione delle date
Nelle impostazioni del progetto (settings.py
) vengono impostati diversi parametri per la localizzazione dell'applicazione:
TIME_ZONE = 'America/Chicago' LANGUAGE_CODE = 'en-us' USE_L10N = True
In particolare la variabile USE_L10N
impostata a True
significa che verranno usate automaticamente le convenzioni locali per formattare date, numeri, ecc. Per forzare ad esempio il formato data in qualcosa di diverso si può impostare:
USE_L10N = False DATE_FORMAT = '%Y/%m/%d'