Neuro17
6/25/2015 - 6:39 PM

Django

Django

[TOC]

MVC in Django

Ci sono un elevato numero di funzioni che possono essere usate per facilitare e modularizzare compiti “standard”.

  • interazione con db
  • autenticazione
  • etc

Gle elementi MVVC sono gestiti in 3 file separati:

  • models.py -> i modelli
  • views.py -> le viste
  • urls.py -> i controller

Model

Il componente Model dell'applicazione è implementato direttamente come collezione di classi Python, che poi andranno a rappresentare le tabelle del database. Il codice SQL per creare lo schema viene generato automaticamente da Django quando si esegue il deployment del modello.

Si crea un n 'virtual object database' accessibile e usabile direttamente attraverso linguaggio di programmazione ad oggetti.

View

Si usano template per generare diversi tipi di output, e accesso ai dati del model attraverso API Python.

Controller

il file urls.py serve per mappare gli URL richiesti sulle view che restituiscono le pagine richieste. Si possono usare funzioni built-in e espressioni regolari per mappare gli URL richiesti.

Filosofia di progetto

Favorire lo sviluppo rapido dell'applicazione, scrivendo meno codice senza ripeterlo, e interagendo con il DB principalmente con codice SQL autogenerato.

API per il DB

Limitare al massimo le interazioni con il DB. I dati sono accessibii da ogni modulo e i join vengono creati automaticamente dallo strato di interfacciamento software. Rimane possibile scrivere direttamente codice SQL, ma solo quando è veramente necessario.

URL nel controller

URL puliti e riutilizzabili: evitare le estensioni negli URL.

Utilizzo di template per le view

Lo scopo è separare la logica dalla presentazione. Si evita la ridondanza, si è più protetti contro codice malevolo e si facilita l'estendibilità per il futuro.

View

Nessun bisogno di creare nuove classi, sono essenzialmente realizzate attraverso delle funzioni Python. Si usano oggetti che incapsulano le richieste HTTP.

Struttura di un progetto Django

 mysite (dir top-level)
 |_ manage.py
 |_ mysite (package Python)
 |   |_ __init__.py 
 |   |_ settings.py
 |   |_ urls.py
 |_ app1 (package Python)
     |_ __init__.py
     |_ models.py
     |_ vies.py
     |_ ...

Un singolo progetto Django può essere spezzato in diverse sotto-applicazioni (ognuna sarà un package separato, come app1), che avrà tutti i suoi componenti MVC (model, views, urls). Applicazioni diverse possono interagire importado i package.

  • manage.py script per automatizzare le operazioni di Django.

  • mysite/mysite cartella della applicazione principale (omonima )del progetto

  • settings.py impostazioni per il funzionamento dell'applicazione, tra cui: definizione dei path dell'applicazione e configurazione di accesso a DB

  • urls.py configurazione degli URL per il progetto (livello root).

Tutorial base

Creo il progetto direttamente da pycharm, oppure con il comando:

django-admin.py startproject mysite

Per usare MySQL devo prima creare il DB:

$ mysql --user=root -p
mysql> CREATE DATABASE djangodb;
mysql> CREATE USER 'djangouser'@'localhost' IDENTIFIED BY 'rue1iep5';
mysql> GRANT ALL PRIVILEGES ON djangodb.* TO 'djangouser'@'localhost' WITH GRANT OPTION; 
mysql> show databases;

Devo aggiungere la connessione al DB creato nel file settings.py

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql', 
        'NAME': 'djangodb',
        'USER': 'djangouser',
        'PASSWORD': 'rue1iep5',
        'HOST': '',
        'PORT': '',
    }
}

Per PostgreSQL:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'djangodb',
        'USER': 'djangouser',
        'PASSWORD': 'rue1iep5',
        'HOST': '',
        'PORT': '',
    }
}

All'interno del progetto creo una nuova app

$ python manage.py startapp polls

Creo i modelli all'interno di polls/models.py

from django.db import models

class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')
    
    def __unicode__(self):
        return self.question_text

      def was_published_recently(self):
        return self.pub_date >= timezone.now() - datetime.timedelta(days=1)


class Choice(models.Model):
    question = models.ForeignKey(Question)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)
    
    def __unicode__(self):
        return self.choice_text

Aggiungo l'app alla lista delle app disponibili in mysite/settings.py

INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'polls',
)

Ora tutto è pronto per sfruttare le comodità di django:

  • sincronizziamo il D

      $ python manage.py makemigrations polls
    
  • creiamo le tabelle

      $ python manage.py migrate
    
  • SQL generato (mostra il codice SQL generato)

      $ python manage.py sqlmigrate polls 0001
    
  • Controllo

      $ python manage.py check
    
  • Python shell

      $ python manage.py shell
    

Le migrazioni sono uno strumento estremamente potente che permette modifiche in corso d'opera senza perdere i dati già inseriti. I passaggi per applicare le modifiche consistono in:

  • modificare il modello
  • lanciare python manage.py makemigrations
  • lanciare python manage.py migrate

A questo punto il model mette a disposizione una serie di metodi e utility per interrogare il DB, ad esempio

Statementdescrizione
q = Question(question_text="What's new?", pub_date=timezone.now())crea un nuovo oggetto Question
q.save()salva sul DB (sincronizzazione esplicita)
Question.objects.all()select di tutti gli elementi della tabella Question (se non è definito __unicode__ (__str__ in python 3) da una rappresentazione inutile,come quando non si fa l'override di toString() in JAVA)
Question.objects.get(id=1)con get si possono recuperare singoli oggetti come una WHERE sugli attributi della classe
Question.objects.filter(<predicato>)ritorna la lista degli oggetti filtrati sulla base del predicato
Question.objects.exclude(<predicato>)ritorna la lista degli oggetti filtrati che non soddisfano il predicato di filtraggio
q.choice_set.all()fa il join tra la tabella Question e la tabella Choice (relazione one-to-many)
c = q.choice_set.create(choice_text='Just hacking again', votes=0)crea l'oggetto choice, lo aggiunge al set della question q (fa la insert) e lo ritorna
q.choice_set.count()fa il count delle Choice associate a una Question
Choice.objects.filter(question__pub_date__year = current_year)

I predicati di filtraggio sono nella forma <nomeCampo>__criterio=<valore>. Alcuni possibili predicati di filtraggio sono:

CriterioEsempio
Sottocampi__year, __month
Substring__startswith, __contains
Operatori di confronto__gte, __gt
Uguaglianza__exact

Interfaccia di amministrazione

Viene generata automaticamente da Django ed è pensata per gli amministratori dei siti e i content publisher. Permette i gestire tutti i modelli e i dati in essi contenuti. L'interfaccia è raggiungibile a

127.0.0.1:8000/admin 

La prima cosa da fare è creare un superuser Django:

$ python manage.py createsuperuser

Inserire le info e rilanciare il server.

Per aggiungere un' app alla pagine admin modifico il file <nome_app>/admin.py

from .models import Question
admin.site.register(Question)

Ora basta ricaricare la pagina admin

Customizzare l'interfaccia admin

Si personalizza modificando il file admin.py dell'applicazione, creadno una classe che descriva come vogliamo personalizzare l'interfaccia del modello.

#non funzia, capire perchè
class QuestionAdmin(admin.ModelAdmin):
    fields = ['pub_date', 'question']
admin.site.register(Question, QuestionAdmin)

Si possono raggruppare in sezioni i campi (comodo quando se ne hanno tanti)

class QuestionAdmin(admin.ModelAdmin):
    fieldsets = [
        (None, {'fields': ['question_text']}),
        ('Date information', {'fields': ['pub_date'], 'classes': ['collapse']}),
    ]
admin.site.register(Question, QuestionAdmin)

E' possibile aggiungere anche le info prese dalla tabella che andrebbe in join con Question, ovvero Choice, in modo da avere tutto inline, anche in inserimento.

class ChoiceInline(admin.StackedInline):
    model = Choice

    # number of choices for each question
    extra = 3

Ulteriori customizzazioni possono essere fatte ad esempio per mostrare info aggiuntive e non solo quelle del metodo __unicode__ che è ciò che viene fatto di default.

 class QuestionAdmin(admin.ModelAdmin):
     # …
     inlines = [ChoiceInline]
     list_display = ('question_text', 'pub_date', 'was_published_recently')

All'interno di list_display si possono mettere sia attributi del modello che risultati di metodi. Per quanto riguarda i risultati di metodi, non è consentito l'ordinamento, ma si può aggiungere nel seguento modo:

class Question(models.Model):
    #...
    # enable ordering
    was_published_recently.admin_order_field = 'pub_date'
    # show a symbol instead of True
    was_published_recently.boolean = True
    # deine the column name
    was_published_recently.short_description = 'Published recently?'

Per specificare un opzione di filtraggio, aggiuntere al file admin.py

list_filter = ['pub_date'] #in class QuestionAdmin

Per le funzioni di ricerca invece, sempre in class QuestionAdmin:

search_fields = ['question_text']
# non funzia
date_hierarchy = 'pub_date'

Template tutorial

(modifichiamo la pagina admin) La prima cosa da fare è aggiungere il path delle cartella template, in modo che django sappia dove prendere i template. Modificare il file settings.py

TEMPLATES = [
 {
 'BACKEND': \
 'django.template.backends.django.DjangoTemplates',
 'DIRS': [os.path.join(BASE_DIR, 'templates')], #nuova riga 
 'APP_DIRS': True,
 ….
 }

creare una cartella in template di nome admin e copiarci il template base_site.html che si trova in :

/usr/local/lib/python2.7/dist-packages/django/contrib/admin/templates/admin/base_site.html

dopodichè si modifica il file. Ogni template Django può essere sovrascritto ini questo modo: copia dalla directory di default a quella del progetto e modifica del file.

I template-loader cercano i template all'interno della cartella template al cui interno sarà presente una cartella per ogni progetto/package Python. Se 'APP_DIRSèTrueall'interno disettings.py`, si assume che ci sia una sotto-directory templates per ogni applicazione/package del progetto.

Sostanzialmente i template sono un insieme di blocchi del tipo:

{% block NOMEBLOCCO %}{% endblock %}

Quando un template eredita da un altro, i blocchi ridefiniti vanno a sostituire la definizione originale dei blocchi.

Template - filtri

E' possibile manipolare l'output delle variabili mediante filtri. I filtri sono definiti attraverso l'uso di pipe:

{{variabile | filtro}}

Alcuni filtri sono:

Filtro | Descrizione | esempio --|--| |default|se la variabile è falsa o vuota viene mostrato il parametro del filtro | {{ value | default:"nothing" }} | length | mostra la lunghezza del dato | | join | separa con , e spazio | {{ list | join:", " }} | lower | | upper | |capfirst | |add:X | |date:"M d, Y" | | {{my_date|date:"Y-m-d"}} |first, |last | |truncatechars:X, |truncatewords:X | mostra solo i primi X caratteri/parole |

Template: controllo di flusso

Ereditarietà:

{% extends “base.html” %} 

Tag con costrutti condizionali if:

{% if athlete_list %}
    Number of athletes: {{ athlete_list|length }}
{% else %}
     No athletes.
{% endif %}

{% if user.is_authenticated %}
     Hello, {{ user.username }}.
{% endif %}

{% for story in story_list %}
    <a href="{{ story.get_absolute_url }}">
         {{ story.headline|upper }} 
    </a>
{% endfor %}

View

In django pagine Web e contenuti sono distribuiti attraverso il meccanismo delle view, implementate tramite una funzione python. Per associare un URL a una view, si esegue il mapping nel file ulrs.py. Gli URL sono nella forma :

 /newsarchive/<year>/<month>/

dove year e month sono parametri. Ogni view è definita come funzione nel file view.py, e deve ritornare un oggetto di tipo HttpResponse

from django.http import HttpResponse
def index(request):
    return HttpResponse("You're at the poll index.")

request consente di accedere ai dettagli della richiesta.

una volta mappati gli URL ad esempio:

from django.conf.urls import url
from . import views
urlpatterns = [
    url(r'^$', views.index, name='index'),
]

dobbiamo integrare lo spazion di URL dell' applicazione con quelli del progetto.

from django.conf.urls import patterns, include, url
from django.contrib import admin
urlpatterns = [
    url(r'^polls/', include('polls.urls')),
    url(r'^admin/', include(admin.site.urls)),
]

Ora andando alla pagina localhost:8000/polls otteniamo una risposta perchè:

Quando arriva la richiesta Django va a cercare il mapping di questo indirizzo nel file principale urls.py e capisce grazie a r'^polls/' che a tutte le pagine che iniziano con polls fanno match con con polls.urls quindi andrà a cercare il mapping del resto dell' URL in questo file. la parte polls viene tolta perchè ha già fatto match in mysite/urls.py quindi ciò che rimane è una stringa vuota, che viene gestita nel file polls/urls.py grazie all' espressione regolare r'^$' che fa match, appunto, con tutte le stringhe vuote.

###** Regular expression in Python/Django**

Written with StackEdit.