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''** o **''test''**.
* 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 **[[http://docs.djangoproject.com/en/1.2/ref/contrib/admin/|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'''
%s
''' % (
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:
{{page_title}}
{{page_body}}
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:
{{page_title}}
{% if users %}
Persona
Numero spese
Somma spesa
Bilancio
{% for user in users %}
{{user.username}}
{{user.aggr.amount__count}}
{{user.aggr.amount__sum}}
{{user.balance}}
{% endfor %}
{% else %}
No users found.
{% endif %}
===== 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 **[[http://docs.djangoproject.com/en/1.2/howto/deployment/modwsgi/|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:
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'