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

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:

  1. Accesso tramite socket

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

  1. Thrust: nessuna credenziale richiesta: accesso sempre garantito.

  2. Password: accesso tramite un’accoppiata nome utente / password.

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

  1. direct_to_template : funzione

  2. TemplateView : classe con Generic view

  3. 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/

  1. Aggiungere django.contrib.flatpages alle app registrate in settings.py

  2. Aggiungere django.contrib.flatpages.middleware.FlatpageFallbackMiddleware a MIDDLEWARE_CLASSES in settings.py

  3. 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