Skip to content

Instantly share code, notes, and snippets.

@bchirico
Last active August 29, 2015 14:23
Show Gist options
  • Save bchirico/ec02f196513f8b7325f9 to your computer and use it in GitHub Desktop.
Save bchirico/ec02f196513f8b7325f9 to your computer and use it in GitHub Desktop.
Django
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Django</title>
<link rel="stylesheet" href="https://stackedit.io/res-min/themes/base.css" />
<script type="text/javascript" src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML"></script>
</head>
<body><div class="container"><p><div class="toc">
<ul>
<li><ul>
<li><a href="#mvc-in-django">MVC in Django</a><ul>
<li><a href="#model">Model</a></li>
<li><a href="#view">View</a></li>
<li><a href="#controller">Controller</a></li>
<li><a href="#filosofia-di-progetto">Filosofia di progetto</a><ul>
<li><a href="#api-per-il-db">API per il DB</a></li>
<li><a href="#url-nel-controller">URL nel controller</a></li>
<li><a href="#utilizzo-di-template-per-le-view">Utilizzo di template per le view</a></li>
<li><a href="#view-1">View</a></li>
</ul>
</li>
<li><a href="#struttura-di-un-progetto-django">Struttura di un progetto Django</a></li>
</ul>
</li>
<li><a href="#tutorial-base">Tutorial base</a></li>
<li><a href="#interfaccia-di-amministrazione">Interfaccia di amministrazione</a><ul>
<li><a href="#customizzare-linterfaccia-admin">Customizzare l’interfaccia admin</a></li>
</ul>
</li>
<li><a href="#template-tutorial">Template tutorial</a><ul>
<li><a href="#template-filtri">Template - filtri</a></li>
<li><a href="#template-controllo-di-flusso">Template: controllo di flusso</a></li>
</ul>
</li>
<li><a href="#view-2">View</a><ul>
<li><a href="#regular-expression-in-pythondjango">* Regular expression in Python/Django*</a></li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
</p>
<h2 id="mvc-in-django"><strong>MVC in Django</strong></h2>
<p>Ci sono un elevato numero di funzioni che possono essere usate per facilitare e modularizzare compiti “standard”.</p>
<ul>
<li>interazione con db</li>
<li>autenticazione</li>
<li>etc</li>
</ul>
<p>Gle elementi MVVC sono gestiti in 3 file separati:</p>
<ul>
<li><code>models.py</code> -&gt; i modelli</li>
<li><code>views.py</code> -&gt; le viste</li>
<li><code>urls.py</code> -&gt; i controller</li>
</ul>
<h3 id="model"><strong>Model</strong></h3>
<p>Il componente Model dell’applicazione è implementato direttamente come collezione di classi Python, che poi andranno <br>
a rappresentare le tabelle del database. Il codice SQL per creare lo schema viene generato automaticamente da Django <br>
quando si esegue il deployment del modello.</p>
<p>Si crea un n ‘virtual object database’ accessibile e usabile direttamente attraverso linguaggio di programmazione ad oggetti.</p>
<h3 id="view"><strong>View</strong></h3>
<p>Si usano template per generare diversi tipi di output, e accesso ai dati del model attraverso API Python.</p>
<h3 id="controller"><strong>Controller</strong></h3>
<p>il file <code>urls.py</code> serve per mappare gli URL richiesti sulle view che restituiscono le pagine richieste. Si possono usare <br>
funzioni built-in e espressioni regolari per mappare gli URL richiesti.</p>
<h3 id="filosofia-di-progetto"><strong>Filosofia di progetto</strong></h3>
<p>Favorire lo sviluppo rapido dell’applicazione, scrivendo meno codice senza ripeterlo, e interagendo con il DB principalmente <br>
con codice SQL autogenerato.</p>
<h4 id="api-per-il-db"><strong>API per il DB</strong></h4>
<p>Limitare al massimo le interazioni con il DB. I dati sono accessibii da ogni modulo e i join vengono creati automaticamente <br>
dallo strato di interfacciamento software. Rimane possibile scrivere direttamente codice SQL, ma solo quando è veramente <br>
necessario.</p>
<h4 id="url-nel-controller"><strong>URL nel controller</strong></h4>
<p>URL puliti e riutilizzabili: evitare le estensioni negli URL.</p>
<h4 id="utilizzo-di-template-per-le-view"><strong>Utilizzo di template per le view</strong></h4>
<p>Lo scopo è separare la logica dalla presentazione. Si evita la ridondanza, si è più protetti contro codice malevolo e si <br>
facilita l’estendibilità per il futuro.</p>
<h4 id="view-1"><strong>View</strong></h4>
<p>Nessun bisogno di creare nuove classi, sono essenzialmente realizzate attraverso delle funzioni Python. Si usano oggetti <br>
che incapsulano le richieste HTTP.</p>
<h3 id="struttura-di-un-progetto-django"><strong>Struttura di un progetto Django</strong></h3>
<pre data-initialized="true" data-gclp-id="1"><code> mysite (dir top-level)
|_ manage.py
|_ mysite (package Python)
| |_ __init__.py
| |_ settings.py
| |_ urls.py
|_ app1 (package Python)
|_ __init__.py
|_ models.py
|_ vies.py
|_ ...
</code></pre>
<p>Un singolo progetto Django può essere spezzato in diverse sotto-applicazioni (ognuna sarà un package separato, come <code>app1</code>), <br>
che avrà tutti i suoi componenti MVC (model, views, urls). Applicazioni diverse possono interagire importado i package.</p>
<ul>
<li><p><code>manage.py</code> <br>
script per automatizzare le operazioni di Django.</p></li>
<li><p><code>mysite/mysite</code> <br>
cartella della applicazione principale (omonima )del progetto</p></li>
<li><p><code>settings.py</code> <br>
impostazioni per il funzionamento dell’applicazione, tra cui: definizione dei path dell’applicazione e configurazione di <br>
accesso a DB</p></li>
<li><p><code>urls.py</code> <br>
configurazione degli URL per il progetto (livello root). </p></li>
</ul>
<h2 id="tutorial-base"><strong>Tutorial base</strong></h2>
<p>Creo il progetto direttamente da pycharm, oppure con il comando:</p>
<pre data-initialized="true" data-gclp-id="2"><code>django-admin.py startproject mysite
</code></pre>
<p>Per usare MySQL devo prima creare il DB:</p>
<pre data-initialized="true" data-gclp-id="3"><code>$ mysql --user=root -p
mysql&gt; CREATE DATABASE djangodb;
mysql&gt; CREATE USER 'djangouser'@'localhost' IDENTIFIED BY 'rue1iep5';
mysql&gt; GRANT ALL PRIVILEGES ON djangodb.* TO 'djangouser'@'localhost' WITH GRANT OPTION;
mysql&gt; show databases;
</code></pre>
<p>Devo aggiungere la connessione al DB creato nel file <code>settings.py</code></p>
<pre data-initialized="true" data-gclp-id="4"><code>DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'djangodb',
'USER': 'djangouser',
'PASSWORD': 'rue1iep5',
'HOST': '',
'PORT': '',
}
}
</code></pre>
<p>Per PostgreSQL:</p>
<pre data-initialized="true" data-gclp-id="5"><code>DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'djangodb',
'USER': 'djangouser',
'PASSWORD': 'rue1iep5',
'HOST': '',
'PORT': '',
}
}
</code></pre>
<p>All’interno del progetto creo una nuova app</p>
<pre data-initialized="true" data-gclp-id="6"><code>$ python manage.py startapp polls
</code></pre>
<p>Creo i modelli all’interno di <code>polls/models.py</code></p>
<pre data-initialized="true" data-gclp-id="7"><code>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 &gt;= 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
</code></pre>
<p>Aggiungo l’app alla lista delle app disponibili in <code>mysite/settings.py</code></p>
<pre data-initialized="true" data-gclp-id="8"><code>INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'polls',
)
</code></pre>
<p>Ora tutto è pronto per sfruttare le comodità di django:</p>
<ul>
<li><p>sincronizziamo il D</p>
<pre data-initialized="true" data-gclp-id="9"><code>$ python manage.py makemigrations polls
</code></pre></li>
<li><p>creiamo le tabelle</p>
<pre data-initialized="true" data-gclp-id="10"><code>$ python manage.py migrate
</code></pre></li>
<li><p>SQL generato (mostra il codice SQL generato)</p>
<pre data-initialized="true" data-gclp-id="11"><code>$ python manage.py sqlmigrate polls 0001
</code></pre></li>
<li><p>Controllo</p>
<pre data-initialized="true" data-gclp-id="12"><code>$ python manage.py check
</code></pre></li>
<li><p>Python shell </p>
<pre data-initialized="true" data-gclp-id="13"><code>$ python manage.py shell
</code></pre></li>
</ul>
<p>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:</p>
<ul>
<li>modificare il modello</li>
<li>lanciare <code>python manage.py makemigrations</code></li>
<li>lanciare <code>python manage.py migrate</code></li>
</ul>
<p>A questo punto il model mette a disposizione una serie di metodi e utility per interrogare il DB, ad esempio</p>
<table>
<thead>
<tr>
<th>Statement</th>
<th>descrizione</th>
</tr>
</thead>
<tbody><tr>
<td>q = Question(question_text=”What’s new?”, pub_date=timezone.now())</td>
<td>crea un nuovo oggetto Question</td>
</tr>
<tr>
<td>q.save()</td>
<td>salva sul DB (sincronizzazione esplicita)</td>
</tr>
<tr>
<td>Question.objects.all()</td>
<td>select di tutti gli elementi della tabella Question (se non è definito <code>__unicode__</code> (<code>__str__</code> in python 3) da una rappresentazione inutile,come quando non si fa l’override di toString() in JAVA)</td>
</tr>
<tr>
<td>Question.objects.get(id=1)</td>
<td>con <code>get</code> si possono recuperare <strong>singoli</strong> oggetti come una <code>WHERE</code> sugli attributi della classe</td>
</tr>
<tr>
<td>Question.objects.filter(<code>&lt;predicato&gt;</code>)</td>
<td>ritorna la lista degli oggetti filtrati sulla base del predicato</td>
</tr>
<tr>
<td>Question.objects.exclude(<code>&lt;predicato&gt;</code>)</td>
<td>ritorna la lista degli oggetti filtrati che non soddisfano il predicato di filtraggio</td>
</tr>
<tr>
<td>q.choice_set.all()</td>
<td>fa il join tra la tabella Question e la tabella Choice (relazione one-to-many)</td>
</tr>
<tr>
<td>c = q.choice_set.create(choice_text=’Just hacking again’, votes=0)</td>
<td>crea l’oggetto choice, lo aggiunge al set della question <code>q</code> (fa la insert) e lo ritorna</td>
</tr>
<tr>
<td>q.choice_set.count()</td>
<td>fa il count delle Choice associate a una Question</td>
</tr>
<tr>
<td>Choice.objects.filter(question__pub_date__year = current_year)</td>
<td></td>
</tr>
</tbody></table>
<p>I predicati di filtraggio sono nella forma <code>&lt;nomeCampo&gt;__criterio=&lt;valore&gt;</code>. Alcuni possibili predicati di filtraggio sono:</p>
<table>
<thead>
<tr>
<th>Criterio</th>
<th>Esempio</th>
</tr>
</thead>
<tbody><tr>
<td>Sottocampi</td>
<td>__year, __month</td>
</tr>
<tr>
<td>Substring</td>
<td>__startswith, __contains</td>
</tr>
<tr>
<td>Operatori di confronto</td>
<td>__gte, __gt</td>
</tr>
<tr>
<td>Uguaglianza</td>
<td>__exact</td>
</tr>
</tbody></table>
<h2 id="interfaccia-di-amministrazione"><strong>Interfaccia di amministrazione</strong></h2>
<p>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 </p>
<pre data-initialized="true" data-gclp-id="14"><code>127.0.0.1:8000/admin
</code></pre>
<p>La prima cosa da fare è creare un superuser Django:</p>
<pre data-initialized="true" data-gclp-id="15"><code>$ python manage.py createsuperuser
</code></pre>
<p>Inserire le info e rilanciare il server.</p>
<p>Per aggiungere un’ app alla pagine admin modifico il file <code>&lt;nome_app&gt;/admin.py</code></p>
<pre data-initialized="true" data-gclp-id="16"><code>from .models import Question
admin.site.register(Question)
</code></pre>
<p>Ora basta ricaricare la pagina admin</p>
<h3 id="customizzare-linterfaccia-admin"><strong>Customizzare l’interfaccia admin</strong></h3>
<p>Si personalizza modificando il file <code>admin.py</code> dell’applicazione, creadno una classe che descriva come vogliamo personalizzare l’interfaccia del modello.</p>
<pre data-initialized="true" data-gclp-id="17"><code>#non funzia, capire perchè
class QuestionAdmin(admin.ModelAdmin):
fields = ['pub_date', 'question']
admin.site.register(Question, QuestionAdmin)
</code></pre>
<p>Si possono raggruppare in sezioni i campi (comodo quando se ne hanno tanti)</p>
<pre data-initialized="true" data-gclp-id="18"><code>class QuestionAdmin(admin.ModelAdmin):
fieldsets = [
(None, {'fields': ['question_text']}),
('Date information', {'fields': ['pub_date'], 'classes': ['collapse']}),
]
admin.site.register(Question, QuestionAdmin)
</code></pre>
<p>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.</p>
<pre data-initialized="true" data-gclp-id="19"><code>class ChoiceInline(admin.StackedInline):
model = Choice
# number of choices for each question
extra = 3
</code></pre>
<p>Ulteriori customizzazioni possono essere fatte ad esempio per mostrare info aggiuntive e non solo quelle del metodo <code>__unicode__</code> che è ciò che viene fatto di default.</p>
<pre data-initialized="true" data-gclp-id="20"><code> class QuestionAdmin(admin.ModelAdmin):
# …
inlines = [ChoiceInline]
list_display = ('question_text', 'pub_date', 'was_published_recently')
</code></pre>
<p>All’interno di <code>list_display</code> 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:</p>
<pre data-initialized="true" data-gclp-id="21"><code>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?'
</code></pre>
<p>Per specificare un opzione di filtraggio, aggiuntere al file <code>admin.py</code></p>
<pre data-initialized="true" data-gclp-id="22"><code>list_filter = ['pub_date'] #in class QuestionAdmin
</code></pre>
<p>Per le funzioni di ricerca invece, sempre in <code>class QuestionAdmin</code>:</p>
<pre data-initialized="true" data-gclp-id="23"><code>search_fields = ['question_text']
# non funzia
date_hierarchy = 'pub_date'
</code></pre>
<h2 id="template-tutorial"><strong>Template tutorial</strong></h2>
<p>(modifichiamo la pagina admin) <br>
La prima cosa da fare è aggiungere il path delle cartella template, in modo che django sappia dove prendere i template. Modificare il file <code>settings.py</code></p>
<pre data-initialized="true" data-gclp-id="24"><code>TEMPLATES = [
{
'BACKEND': \
'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')], #nuova riga
'APP_DIRS': True,
….
}
</code></pre>
<p>creare una cartella in template di nome admin e copiarci il template <code>base_site.html</code> che si trova in :</p>
<pre data-initialized="true" data-gclp-id="25"><code>/usr/local/lib/python2.7/dist-packages/django/contrib/admin/templates/admin/base_site.html
</code></pre>
<p>dopodichè si modifica il file. Ogni template Django può essere sovrascritto ini <br>
questo modo: copia dalla directory di default a quella del progetto e modifica del file.</p>
<p>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<code>è</code>True<code>all'interno di</code>settings.py`, si assume che ci sia una sotto-directory templates per ogni applicazione/package del progetto.</p>
<p>Sostanzialmente i template sono un insieme di blocchi del tipo: </p>
<pre data-initialized="true" data-gclp-id="26"><code>{% block NOMEBLOCCO %}{% endblock %}
</code></pre>
<p>Quando un template eredita da un altro, i blocchi ridefiniti vanno a sostituire la definizione originale dei blocchi.</p>
<h3 id="template-filtri"><strong>Template - filtri</strong></h3>
<p>E’ possibile manipolare l’output delle variabili mediante filtri. <br>
I filtri sono definiti attraverso l’uso di pipe:</p>
<pre data-initialized="true" data-gclp-id="27"><code>{{variabile | filtro}}
</code></pre>
<p>Alcuni filtri sono:</p>
<table>
<thead>
<tr>
<th>Filtro</th>
<th>Descrizione</th>
<th>esempio</th>
</tr>
</thead>
<tbody><tr>
<td>|default</td>
<td>se la variabile è falsa o vuota viene mostrato il parametro del filtro</td>
<td>{{ value | default:”nothing” }}</td>
</tr>
<tr>
<td>| length</td>
<td>mostra la lunghezza del dato</td>
<td></td>
</tr>
<tr>
<td>| join</td>
<td>separa con , e spazio</td>
<td>{{ list | join:”, ” }}</td>
</tr>
<tr>
<td>| lower</td>
<td></td>
<td></td>
</tr>
<tr>
<td>| upper</td>
<td></td>
<td></td>
</tr>
<tr>
<td>|capfirst</td>
<td></td>
<td></td>
</tr>
<tr>
<td>|add:X</td>
<td></td>
<td></td>
</tr>
<tr>
<td>|date:”M d, Y”</td>
<td></td>
<td>{{my_date|date:”Y-m-d”}}</td>
</tr>
<tr>
<td>|first, |last</td>
<td></td>
<td></td>
</tr>
<tr>
<td>|truncatechars:X, |truncatewords:X</td>
<td>mostra solo i primi X caratteri/parole</td>
<td></td>
</tr>
</tbody></table>
<h3 id="template-controllo-di-flusso"><strong>Template: controllo di flusso</strong></h3>
<p>Ereditarietà:</p>
<pre data-initialized="true" data-gclp-id="28"><code>{% extends “base.html” %}
</code></pre>
<p>Tag con costrutti condizionali if:</p>
<pre data-initialized="true" data-gclp-id="29"><code>{% 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 %}
&lt;a href="{{ story.get_absolute_url }}"&gt;
{{ story.headline|upper }}
&lt;/a&gt;
{% endfor %}
</code></pre>
<h2 id="view-2"><strong>View</strong></h2>
<p>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 <code>ulrs.py</code>. <br>
Gli URL sono nella forma :</p>
<pre data-initialized="true" data-gclp-id="30"><code> /newsarchive/&lt;year&gt;/&lt;month&gt;/
</code></pre>
<p>dove year e month sono parametri. <br>
Ogni view è definita come funzione nel file <code>view.py</code>, e deve ritornare un oggetto di tipo HttpResponse</p>
<pre data-initialized="true" data-gclp-id="31"><code>from django.http import HttpResponse
def index(request):
return HttpResponse("You're at the poll index.")
</code></pre>
<p><code>request</code> consente di accedere ai dettagli della richiesta.</p>
<p>una volta mappati gli URL ad esempio:</p>
<pre data-initialized="true" data-gclp-id="32"><code>from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.index, name='index'),
]
</code></pre>
<p>dobbiamo integrare lo spazion di URL dell’ applicazione con quelli del progetto.</p>
<pre data-initialized="true" data-gclp-id="33"><code>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)),
]
</code></pre>
<p>Ora andando alla pagina <code>localhost:8000/polls</code> otteniamo una risposta perchè:</p>
<p>Quando arriva la richiesta Django va a cercare il mapping di questo indirizzo nel file principale <code>urls.py</code> e capisce grazie a <code>r'^polls/'</code> che a tutte le pagine che iniziano con <code>polls</code> fanno match con con <code>polls.urls</code> quindi andrà a cercare il mapping del resto dell’ URL in questo file. la parte <code>polls</code> viene tolta perchè ha già fatto match in <code>mysite/urls.py</code> quindi ciò che rimane è una stringa vuota, che viene gestita nel file <code>polls/urls.py</code> grazie all’ espressione regolare <code>r'^$'</code> che fa match, appunto, con tutte le stringhe vuote.</p>
<h3 id="regular-expression-in-pythondjango"><em>* Regular expression in Python/Django*</em></h3>
<blockquote>
<p>Written with <a href="https://stackedit.io/">StackEdit</a>.</p>
</blockquote></div></body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment