1 Note su django¶
Appunti allo sviluppo per Django con python v3, versione periodicamente aggiornata dato che l’intera baracca cambia continuamente…
- Author:
Andrea Manni
- Copyright:
GFDL
- Version:
0.4
Questi appunti sono ad uso privato.
Generato il 2024-01-27 con: http://docutils.sourceforge.net/rst.html
1.1 Virtual env¶
Il modo preferito per eseguire Django generalmente e’ tramite un virtualenv, si veda la guida di flask per lagestione di questo e l’implementazione con apache.
1.1.1 Pyven¶
Con python 3.5 si usa pyvenv:
apt-get install python3-venv
python3 -m venv env
. env/bin/activate
pip install django
1.1.2 Virtual env¶
Per una installazione ‘pulita’ mainstream in un virtual enviroment:
mkdir site
cd site/
virtualenv -p /usr/bin/python3 env
. env/bin/activate
pip install django
1.1.2.1 Extensions¶
pip install django-extensions bpython
add
INSTALLED_APPS = (
...
'django_extensions',
)
Shell:
./manage.py shell_plus
./manage.py shell_plus --bpython
1.1.2.2 django_bash_completion¶
Il file si puo’ recuperare da una installazione locale di django: /usr/share/bash-completion/completions/django_bash_completion
oppure scaricare da: https://github.com/django/django/blob/master/extras/django_bash_completion
cd ~/sites/env/
wget https://raw.githubusercontent.com/django/django/master/extras/django_bash_completion
cp django_bash_completion ~/.django_bash_completion.sh
Quando si va l’activate
viene fatto il source di quel file, altrimenti si puo’ mettere in coda a bashrc
:
echo ". ~/sites/env/django_bash_completion" >> ~/.bashrc
bash
Se per attviare un env si usa:
. env/bin/activate
source env/bin/activate
Per disattivarlo:
deactivate
- Per aggiornarlo::
pip freeze –local | grep -v ‘^-e’ | cut -d = -f 1 | xargs -n1 pip install -U
1.1.2.3 Requirements list¶
A
Per generare una list they requirements del virtual env (o del sistema operativo senza il -E
o se non si e’ attviato il virtual env):
pip freeze > requirements.txt
# Esempio
asgiref==3.6.0
Django==4.1.6
sqlparse==0.4.3
Per installare i pacchetti in base alla lista generata:
pip install -r requirements.txt
1.1.2.4 Upgrades¶
- PIP ha una sua procedura per aggiornare tutti i pacchetti installati:
pip freeze –local | grep -v ‘^-e’ | cut -d = -f 1 | xargs -n1 pip install -U
- Per aggiornare un solo pacchetto:
pip install Django –upgrade
- Per aggiornare a una specifica release:
pip install django==1.8.12
Release notes: https://docs.djangoproject.com/en/4.1/releases/#
1.1.3 Postgresql support: psycopg2¶
Per usare Postgres serve il pacchetto pip psycopg2 https://pypi.org/project/psycopg2 che va generalmente compilato (quindi serve tutto il build enviroment!), su puo’ prima provare a installare il binario da pip:
pip install psycopg2-binary
Per poter usare Postgresql in un virtualenv occorre compilare il supporto psycopg2
all’interno del virtualenv. Si proceda installando nel sistema operativo i pacchetti:
apt-get install python3-dev build-essential python-dev libpq-dev
Poi all’interno del virtual-env si potra’ compilare il pacchetto psycopg2:
pip install psycopg2
Se si disinstall tutto il build env poi non si potranno installare nuovi psycopg2 in altri env, sarebbe sensato generare un solo env per tutti i siti e magari cercare di aggiornare solo django nel caso di updates, oppure re-installare tutto al momento di aggiornamenti di release.
Per un test:
python
>>> import psycopg2
1.1.3.1 Debian way¶
C’e la versione binaria pre pacchettizzata di psycopg:
apt-get install python-psycopg2
Che non funzionara’ all’interno di un virtual env.
1.1.4 Settings¶
Esempio:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
'NAME': 'mydb', # Or path to database file if using sqlite3.
# The following settings are not used with sqlite3:
'USER': 'myuser',
'PASSWORD': 'password',
'HOST': '', # Empty for localhost through domain sockets or '127.0.0.1' for localhost through TCP.
'PORT': '', # Set to empty string for default.
}
}
1.1.4.1 Local socket¶
Per trovare la soket locale sotto la quale gira Postgres:
ps -aef | grep postgres | grep main
# il PID e' quello del demone Postgres
lsof -p 9793 | grep unix
es: /var/run/postgresql/.s.PGSQL.5433
, questa andra’ in HOST
.
Per controllare la socket: netstat -nlp | grep 5432 `` , ``netstat -lp --protocol=unix | grep postgres
.
Postgresql v.9.4 gira sulla porta 5433 , non sulla 5432. Genera un soket /var/run/postgresql/.s.PGSQL.5433
e non *32. In pratica Django vuole nei settings HOST = percorso del soket (senza porta) - e poi nella porta il numero del socket:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'piffanet',
'USER': 'eaman',
'PASSWORD': '',
'HOST': '/var/run/postgresql/',
"PORT": "5433",
}
}
1.2 Sviluppo in locale¶
1.2.1 SSHFS per montare il remoto¶
sshfs -o uid=1000 -o allow_other -o ro eaman@zap:/home/eaman/ /mnt/virtual/chroots/stretch/home/eaman/zap
/etc/fstab
# SSHFS
eaman@kim:/home/eaman/store /home/eaman/store fuse.sshfs noauto,rw,nosuid,nodev,uid=1000,uid=1000,allow_other 0 0
1.2.2 Fast DB sync¶
- ::
remoto: pg_dump -c piffanet > piffasql
Locale: psql piffanet < piffasql
1.2.3 Generate VHosts¶
Con un file names contenente un nome per riga:
while read name; do
echo "<VirtualHost *:80>
ServerAdmin webmaster@andreamanni.com
ServerName $name.piffa.net
DocumentRoot /home/$name/public
<Directory /home/public/store >
Options Indexes FollowSymLinks MultiViews
IndexIgnore README.html HEADER.html
AllowOverride None
Order allow,deny
allow from all
</Directory>
ErrorLog /var/log/apache2/error.log
# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
LogLevel warn
CustomLog /var/log/apache2/access.log combined
</VirtualHost>" > $name;
done < names
1.3 Postgresql¶
1.3.1 Installazione¶
Vediamo i passi fondamentali per installare il server e utilizzarlo con PHP:
apt-get install postgresql
Ora dovremo creare utenti e database, ma per poter gestire gli utenti dobbiamo prima preoccuparci di come questi contino di accedere a PostgreSQL. Come gia’ accennato i metodi sono fondamentalmente due:
Accesso tramite socket
Accesso tramite TCP/IP via rete
Il primo tipo e’ sicuramente piu’ semplice e non viene appesantito da tutto quanto e’ richiesto per mantenere una connessione TCP/IP tra client (il nostro server web) e server (il server PostgreSQL). La socket e’ disponibile nel file system nel percorso: /var/run/postgresql
. Se server web e DBMS sono sullo stesso host questo e’ il metodo piu’ efficiente per connetterli.
Il secondo approccio e’ utile se volete o meglio dovete accedere direttamente al database da remoto, si tenga conto che un client web come phppgadmin
e’ in esecuzione sul server e vi permette di accedere al DB senza dover offrire il supporto alla rete. Necessario se DBMS e server web sono su host diversi.
Dopodiche’ dovremo considerare come autenticarci col server, tra le varie opzioni ( http://www.postgresql.org/docs/8.4/static/auth-methods.html ) considereremo:
Thrust: nessuna credenziale richiesta: accesso sempre garantito.
Password: accesso tramite un’accoppiata nome utente / password.
Ident: autenticazione tramite le credenziali dell’utente sull’host in esecuzione. Abilitato di default.
Il file di configurazione in cui andremo ad operare sara’ su un sistema Debian Squeeze: ` /etc/postgresql/8.4/main/pg_hba.conf
, vediamo come dovremo modificarlo per alcuni scenari tipici. Si ricorda che per rendere effettivi i cambiamenti apportati si dovra’ eseguire un reload del servizio: /etc/init.d/postgresql reload
.
1.3.1.1 Workstation di sviluppo¶
Semplificando potremmo dire che sulla macchina su cui sviluppate puo’ risultare conveniente la possibilita’ di accedere a qualunque database senza fornire credenziali, ovviamente tramite socket. In questo modo i vostri script potranno connettersi al DBMS a prescindere dell’esistenza di utenti / password corrispondenti al server di produzione. Avremo quindi:
# TYPE DATABASE USER CIDR-ADDRESS METHOD
local all all trust
1.3.1.2 Hosting / server pubblico tramite socket¶
Sul server pubblico si puo’ optare per una accoppiata nome utente / password, per lo meno per evitare che altri utenti del server possano intervenire sul nostro DB. Se l’host ospita sia database che server web si utilizzera’ un socket:
# TYPE DATABASE USER CIDR-ADDRESS METHOD
local all all password
Si e’ scelto di usare una password in chiaro dato che la connessione non dovrebbe essere tracciabile da malintenzionati. Vediamo come generare la password per un utente generico (che non deve essere necessariamente esistente sul sistema operativo) myuser
con password mypasswd
, al quale assegneremo una database phpdb
.
su postgres
# creiamo un utente: myuser
# con password: mypasswd
# L'utente non sara' amministratore, non potra' creare ruoli o database
Createuser -DRS -P myuser
...
# Creiamo un database 'phpdp' assegnato all'utente 'myuser'
createdb -O myuser phpdb
# Per vedere una lista dei DB disponibili:
psql -l
# termiare la sessione dell'utente postgres
exit
# Per avere una shell sul database
psql -w phpdb myuser
Per una prima introduzione all’uso della shell di PostgreSQL si veda: - http://www.faqs.org/docs/ppbook/c4890.htm#AEN4903 - http://www.postgresql.org/docs/8.4/static/tutorial.html
1.3.1.3 Hosting / server pubblico tramite rete¶
Permettere l’accesso al DBMS da rete pone ovvi problemi di sicurezza: cercare di limitare gli accessi (ad esempio su base IP quando i client hanno IP
fissi), garantire la confidenzialita’ del traffico.
Vediamo un esempio con un configurazione di rete con i seguenti parametri:
Host |
IP |
---|---|
Server |
192.168.0.1 |
Client |
192.168.0.2 |
IP statico, database e utente, password. Traffico in chiaro o in SSL se disponibile, autenticazione con hash MD5:
# TYPE DATABASE USER CIDR-ADDRESS METHOD
host phpdb myuser 192.168.0.2/32 md5
Hint
In un sistema Debian dovrebbe essere disponibile di default SSL sia per il server che per il client senza bisogno di particolari configurazioni. E’ comunque possibile generare i propri certificati.
Di default il server Postgres ascolta solo su localhost (l’interfaccia di loopback con IP 127.0.0.1
), se volete rendere il server raggiungibile da un altro IP assegnato all’host del server dovrete aggiungere al file di configurazione del servizio postgres:
/etc/postgresql/8.4/main/postgresql.conf
(riga 60):
listen_addresses = 'localhost , 192.168.0.1'
Si dovra’ riavviare (non basta un reload) Postgres per rendere effettivo il cambiamento:
/etc/init.d/postgresql restart
Possiamo testare la connessione dal client con:
psql -h 192.168.0.1 phpdb -U myuser -W
1.3.2 Utenti¶
- Da
root
fare un su su postgres:: su postgres createuser -d eaman createdb -O eaman piffanet
1.4 Chroot¶
stretcha:
#!/bin/sh
#sudo mount -o bind /proc/ /mnt/virtual/chroots/stretch/proc
sudo chroot /mnt/virtual/chroots/stretch/ /root/stretcha_ch
stretcha:
#!/bin/sh
cd /home/eaman/sites/
su eaman
Note
Sia il mount bind che la partenza dei servizi devono essere fatti una volta sola e non alla partenza di ogni shell!
services:
mount -t proc proc /proc
service rsyslog start
service postgresql start
service apache2 start
1.5 Django start¶
cd ~/sites/
. env/bin/activate
django-admin startproject name
cd name
./manage.py migrate
./manage.py createsuperuser
./manage.py runserver
1.6 Deployment con WSGI¶
/etc/apache2/sites-available/site.conf
<VirtualHost *:80>
ServerName www.example.com
ServerAlias www.example.com example.com
ServerAdmin webmaster@piffa.net
# Static files Madness
Alias /robots.txt /home/eaman/sites/mysite/static_root/robots.txt
Alias /favicon.ico /home/eaman/sites/mysite/static_root/favicon.ico
Alias /media/ /home/eaman/sites/mysite/static_root/media/
Alias /static/ /home/eaman/sites/mysite/static_root/
<Directory /home/eaman/sites/mysite/static_root>
Options FollowSymLinks
</Directory>
<Directory /home/eaman/sites/mysite/static_root>
Require all granted
</Directory>
<Directory /home/eaman/sites/mysite/media>
Require all granted
</Directory>
# End satic madness
<Directory /home/eaman/sites/mysite/mysite>
<Files wsgi.py>
Require all granted
</Files>
</Directory>
WSGIDaemonProcess dev python-home=/home/eaman/enviroments/dj1.10 python-path=/home/eaman/sites/dev
WSGIProcessGroup dev
WSGIScriptAlias / /home/eaman/sites/dev/dev/wsgi.py
LogLevel warn
CustomLog /var/log/apache2/access.log combined
</VirtualHost>
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet
Note
Questo e’ per avere istanze dei siti sotto il loro virtualenv dedicato, questo comporta caricare piu’ moduli in memoria ma permette di usare versini di Django e moduli differenti.
Wsgi file viene generato da Django.
1.6.1 Static_root¶
Con Django servito da un solo web server tutti i file statici vanno messi in una cartella locale: static_root
. da settings.py
del sito Django:
# In coda del file
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.9/howto/static-files/
STATIC_URL = '/static/'
STATIC_ROOT = '/home/eaman/sites/lab/static_root'
Circa ci va’ questa roba (e altra nel caso!):
~/sites/lab:$ tree -L 2 static_root/
static_root/
├── admin
│ ├── css
│ ├── fonts
│ ├── img
│ └── js
├── content
│ ├── css
│ │ ├── bootstrap.min.css
│ │ ├── ie10-viewport-bug-workaround.css
│ │ └── starter-template.css
│ ├── favicon.ico
│ ├── img
│ │ ├── ardu_splash.jpg
│ │ ├── desk_splash.jpg
│ │ ├── dualboards.jpg
│ │ └── splash.jpg
│ ├── js
│ │ ├── bootstrap.min.js
│ │ ├── ie10-viewport-bug-workaround.js
│ │ ├── ie-emulation-modes-warning.js
│ │ └── jquery.min.js
│ └── pippo.png
├── favicon.ico
└── robots.txt
La roba dell’admin e’ da prendere direttamente dalla cartella in cui e’ stato installato Django. Per robots e favicon ci vogliono un paio di alias in apache…
1.6.2 Sqlite permessi¶
Per poter usare un db sqlite con apache si dovra’ dare i permessi di scrittura sia al db che alla cartella genitrice all’utente o al gruppo www-data .
1.6.3 Git¶
- Settings:
Per il server remoto si puo’ inpostare il file dei setting di produzione in wsgi.conf
gitignore:
*.pyc
*~
__pycache__
**/__pycache__/
**.sw
**.swp
db.sqlite3
**/.ropeproject
Two consecutive asterisks ("**") in patterns matched against full pathname may have special meaning:
A leading "**" followed by a slash means match in all directories. For example, "**/foo" matches file or directory "foo" anywhere, the same as pattern "foo". "**/foo/bar" matches file or directory "bar" anywhere that is directly under directory "foo".
A trailing "/**" matches everything inside. For example, "abc/**" matches all files inside directory "abc", relative to the location of the .gitignore file, with infinite depth.
A slash followed by two consecutive asterisks then a slash matches zero or more directories. For example, "a/**/b" matches "a/b", "a/x/b", "a/x/y/b" and so on.
.. NOTE::
- Migrations:
Considerare che queste sono relative al tipo di DB usato quindi se si vogliono tenere in controllo di versione bisogna utilizzare lo stesso motore su tutte le macchine.
1.7 Redis¶
Pacchetti:
pip install django-redis
1.8 RST¶
Ogni 2 mesi il modo per renderizzare RST cambia. Per usare il filter di RST installare:
pip install django-markupfield # incomprensibile
pip install django-markwhat
moderatamente comprensibile...
Aggiungere a settings:
INSTALLED_APPS = [
...
'django.contrib.staticfiles',
'django_markwhat',
In ogni template:
{% extends "base.html" %}
{% load markup %}
Il filtro:
{{ classi.content|restructuredtext}}
1.9 Static Files¶
In ogni app deve esserci una cartella:
` static/nome_app`
, es`static/content/pippo.png`
Nei file HTML si chiama:
`/static/nome_app/resource_name`
es`<img src="/static/content/pippo.png" />`
1.10 Static Pages¶
Warning
Outdated
Ci sono almeno tre modi per gestire pagine dal contenuto statico:
direct_to_template : funzione
TemplateView : classe con Generic view
La app static pages
1.10.1 TemplateView¶
In url.conf avremo qualcosa come:
from django.views.generic import TemplateView
...
urlpatterns = patterns('',
url(r'info/$', TemplateView.as_view(template_name="info.html")),
1.10.2 direct_to_template¶
C’e’ caso che questa sia gia’ caricata, comunque avremo sempre in url.conf:
from django.views.generic.simple import direct_to_template
urlpatterns = patterns('',
url('^about/$', direct_to_template, {'template': 'about.html' }),
1.10.3 Flat pages¶
Se ci sono molte pagine statiche si puo’ optare per la app flatpages: https://docs.djangoproject.com/en/dev/ref/contrib/flatpages/
Aggiungere
django.contrib.flatpages
alle app registrate insettings.py
Aggiungere
django.contrib.flatpages.middleware.FlatpageFallbackMiddleware
aMIDDLEWARE_CLASSES
insettings.py
Creare un template
1.10.3.1 Template¶
Le pagine create vengono renderizzate tramite un template, che renderizza almeno il titolo e il contenuto. Il default e’ templates/flatpages/default.html
<!DOCTYPE html>
<html>
<head>
<title>{{ flatpage.title }}</title>
</head>
<body>
{{ flatpage.content }}
</body>
</html>
1.11 Auto date¶
I parametri auto_now and auto_now_add
sono sconsigliati, meglio intervenire sul metodo save di un modello.
models.py:
def save(self, *args, **kwargs):
''' On save, update timestamps '''
if not self.id:
self.creata = datetime.datetime.today()
self.modificata = datetime.datetime.today()
super(Articoli, self).save(*args, **kwargs)
A questo punto conviene mettere i campi come non editabili:
creata = models.DateTimeField('Data di creazione',editable=False)
modificata = models.DateTimeField('Ultima modifica',editable=False)
1.12 Slug¶
Per l’uso degli slug mettere in model.py:
class Articoli(models.Model):
titolo = models.CharField(max_length=200)
slug = models.SlugField(max_length=50)
testo = models.TextField()
La prassi e’ poi di far autopopolare il campo slug
da un javascript nell’admin:
class ArticoliAdmin(admin.ModelAdmin):
prepopulated_fields = {"slug": ("titolo",)}
Per la creazione delle url e’ meglio non affidarsi unicamente allo slug per recuperare un record, perquanto si potrebbe usare usato un unique=True
nella definizione del modello: meglio aggiungere l’id
direttamente (e nell’esempio tralasciare completamente lo slug!).
index.html:
<h1>Elenco articoli</h1>
<p>This is the list of available articoli atm</p>
{% if articoli %}
<ol>
{% for articolo in articoli.object_list %}
<li><a href="/articoli/{{ articolo.id }}/{{ articolo.slug }}/">{{ articolo.titolo }}</a></li>
{% endfor %}
</ol>
urls.py:
from django.conf.urls.defaults import patterns, include, url
urlpatterns = patterns('articoli.views',
(r'^$', 'index'),
(r'^(?P<articolo_id>\d+)/(.*)/$', 'detail'),
)
1.13 Paginazione¶
Info all’url: http://docs.djangoproject.com/en/dev/topics/pagination/
Tutto il codice risiede nella view, se consideriamo il modello:
class Articoli(models.Model):
titolo = models.CharField(max_length=200)
testo = models.TextField()
pub_data = models.DateTimeField()
autore = models.ForeignKey(Autori
1.13.1 View¶
Avremo una view per Dj 1.3:
from django.core.paginator import Paginator, InvalidPage, EmptyPage
def index(request):
lista_articoli = Articoli.objects.all().order_by('-pub_data')
paginator = Paginator(lista_articoli, 5,orphans=3)
# Make sure page request is an int. If not, deliver first page.
try:
page = int(request.GET.get('page', '1'))
except ValueError:
page = 1
# If page request (9999) is out of range, deliver last page of results.
try:
articoli = paginator.page(page)
except (EmptyPage, InvalidPage):
articoli = paginator.page(paginator.num_pages)
return render_to_response('articoli/index.html', {"articoli": articoli})
In Dj 1.4 sara’ diversa. Il grosso e’ nella linea:
paginator = Paginator(lista_articoli, 5,orphans=3)
In cui si sceglie 5 come numero di record per pagina, 3 come minimo numero di orfani.
1.13.2 Template¶
Il primo cicolo IF e’ tralasciabile se si e’ sicuro che devono esistere elementi:
<h1>Elenco articoli</h1>
<p>This is the list of available articoli atm</p>
{% if articoli %}
<ol>
{% for articolo in articoli.object_list %}
<li><a href="/articoli/{{ articolo.id }}/">{{ articolo.titolo }}</a></li>
{% endfor %}
</ol>
<div class="pagination">
<span class="step-links">
{% if articoli.has_previous %}
<a href="?page={{ articoli.previous_page_number }}">precedente</a>
{% endif %}
<span class="current">
Pagina {{ articoli.number }} di {{ articoli.paginator.num_pages }}.
</span>
{% if articoli.has_next %}
<a href="?page={{ articoli.next_page_number }}">prossimo</a>
{% endif %}
</span>
</div>
{% else %}
<p>No article are available.</p>
{% endif %}
1.14 Contatti - formmail¶
La Form puo’ essere creata direttamente nei modelli (o meglio in un file forms.py
):
from django import forms
class ContactForm(forms.Form):
subject = forms.CharField(max_length=100, label= "Oggeto del messaggio")
message = forms.CharField(widget=forms.Textarea,label="Testo del messaggio")
sender = forms.EmailField(required=False, label='Recapito email'
Note
Per avere come widget un textfield lo si specifica nel parametro widget, non c’e’ un campo TextField
in forms.
links: - http://docs.djangoproject.com/en/1.3/topics/forms/modelforms/ - http://docs.djangoproject.com/en/1.3//ref/forms/widgets/
1.14.1 View per ricevere e inviare¶
views.py:
from django.core.mail import send_mail, BadHeaderError
from django.template import RequestContext
from django.shortcuts import render_to_response
from django.http import HttpResponseRedirect
# last one should not be need
from forms import ContactForm
# Our form model is in form.py, not model.py
def contact(request):
if request.method == 'POST': # If the form has been submitted...
form = ContactForm(request.POST) # A form bound to the POST data
if form.is_valid(): # All validation rules pass
# Process the data in form.cleaned_data
# ...
subject = form.cleaned_data['subject']
message = form.cleaned_data['message']
sender = form.cleaned_data['sender']
cc_myself = form.cleaned_data['cc_myself']
recipients = ['fake@andreamanni.com']
from django.core.mail import send_mail
send_mail(subject, message, sender, recipients)
return HttpResponseRedirect('invio/') # Redirect after POST
else:
form = ContactForm() # An unbound form
return render_to_response('contact.html', {
'form': form,
}, context_instance=RequestContext(request))
def invio(request):
return render_to_response('invio.html')
1.14.2 Template¶
contact.html:
<form action="/contact/" method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>
invio.html:
<h1>Invio avvenuto correttamente</h1>
1.14.3 Urls¶
urls.py:
from django.conf.urls.defaults import patterns, include, url
urlpatterns = patterns('contatti.views',
(r'^$', 'contact'),
(r'^invio/$', 'invio'),
)
1.15 TiniMCE¶
Installare il pacchetto
Per l’Admin progetto/templates/admin/flatpages/flatpage/change_form.html:
<script type="text/javascript" src="/static/tiny_mce/tiny_mce.js"></script>
<script type="text/javascript">
tinyMCE.init({
mode: "textareas",
theme: "simple"
});
</script>
setting.py:
STATICFILES_DIRS = (
# Put strings here, like "/home/html/static" or "C:/www/django/static".
# Always use forward slashes, even on Windows.
# Don't forget to use absolute paths, not relative paths.
'/tmp/tinymce/jscripts/',
)
...
INSTALLED_APPS = (
'tinymce',
urls.py:
(r'^tinymce/', include('tinymce.urls')),
url(r'', include('django.contrib.flatpages.urls')),
1.16 Apache¶
Installare il pacchetto libapache2-mod-wsgi-py3
1.17 Python handlers¶
Questi handler sono da utilizzarsi con mod python, non con WSGI. Si possono comunque usare entrambi coemporaneamente.
1.17.1 Mod python basic handler¶
apache.conf:
<Directory /var/www/mod>
AddHandler mod_python .py
PythonHandler script
PythonDebug On
</Directory>
script.py:
from mod_python import apache
def handler(req):
req.content_type = "text/plain"
req.write("Hello World!")
return apache.OK
1.17.2 Publisher¶
apache.conf:
AddHandler mod_python .py
PythonHandler mod_python.publisher
PythonDebug On
script.py:
s = """\
<html><body>
<h2>Hello %s!</h2>
</body></html>
"""
def index():
return s % 'World'
def everybody():
return s % 'everybody'
1.18 PSP¶
apache.conf:
<Directory /var/www/psp>
AddHandler mod_python .psp
PythonHandler mod_python.psp
PythonDebug On
</Directory>
hello.psp:
<html>
<%
import time
%>
Hello world, the time is: <%=time.strftime("%Y-%m-%d, %H:%M:%S")%>
</html>
1.19 Vim: python mode¶
Install:
git clone https://github.com/klen/python-mode.git
cp -R python-mode/* ~/.vim
Then rebuild helptags in vim:
:helptags ~/.vim/doc/
.vimrc per python mode:
syntax enable
let g:pymode_options = 1
let g:pymode_run = 1
let g:pymode_run_bind = '<leader>r'
filetype indent plugin on
set background=dark
set ignorecase
set smartcase
" Set paste macro
map <F2> :set paste! <CR>:set paste?<CR>
imap <F2> <C-O> :set paste<CR>
map <F3> :set number! <CR>:set number?<CR>
imap <F3> <C-O> <CR> <C-O>:set number?<CR>
map <F4> :%!astyle<CR>
" Save macro
map <Esc><Esc> :up<CR>
" Save macro
map <Esc><Esc> :up<CR>
1.19.1 Python-mode¶
- K
Help di una funzione
- [Ctr][w]
cambia finestra
- [Ctr][w][o]
Chiudi tutto a parte la corrente finestra
1.20 Gunicorn¶
apt-get install gunicorn3
gunicorn3 --workers=2 --env DJANGO_SETTINGS_MODULE=first.settings first.wsgi -b 0.0.0.0
gunicorn3 --workers=2 first.wsgi:application -b 0.0.0.0
Questo andra’ poi attivato via chroot con un path adeguato.
1.20.1 Nginx + gunicorn¶
Se eseguito in chroot / container conviente far girare gunicorn su una socket:
gunicorn3 --workers=2 first.wsgi:application -b unix:first.sock
Usare poi nginx come proxy per tirare a questa in base a un URL:
server_name _;
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root /var/www/html/first/static;
}
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
include proxy_params;
proxy_pass http://unix:/var/www/html/first/first.sock;
}
1.20.1.1 Hints¶
Fare un link simbolico ai files dell’admin ( /usr/lib/python3/dist-packages/django/contrib/admin/static/admin ) nella static
- se non si monta il proc non si puo’ spegnere / restartare nginx, lo si deve fare con un
killall nginx
dal’host principale. /mnt/virtual/stretch/run/nginx.pid
pkill -F /mnt/virtual/stretch/run/nginx.pid
- se non si monta il proc non si puo’ spegnere / restartare nginx, lo si deve fare con un