User Tools

Site Tools


Sidebar

No ai soldati italiani all'estero

Indice

Eventi

Energia

Rigacci.Org usa energia elettrica da fonti rinnovabili, grazie al gruppo di acquisto Merci Dolci.

Merci Dolci - Energia Rinnovabile

Software libero!

Petizione contro i brevetti software

Faunalia: Soluzioni GIS professionali

Debian

www.gnu.org www.kernel.org

doc:appunti:prog:django

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 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 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:

  1. La configurazione riguarda ad un solo VirtualHost.
  2. Uno o più progetti Django sono accessibili da una sottodirectory della DocumentRoot.
  3. 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'
doc/appunti/prog/django.txt · Last modified: 2011/06/28 09:15 by niccolo