===================================== 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. .. sectnum:: .. contents:: Indice degli argomenti .. |date| date:: Generato il |date| con: http://docutils.sourceforge.net/rst.html 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. Pyven -------- Con python 3.5 si usa pyvenv:: apt-get install python3-venv python3 -m venv env . env/bin/activate pip install django 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 Extensions ~~~~~~~~~~~~~~~~ :: pip install django-extensions bpython add INSTALLED_APPS = ( ... 'django_extensions', ) Shell: ./manage.py shell_plus ./manage.py shell_plus --bpython 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 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 - https://pip.pypa.io/en/latest/user_guide/#requirements-files 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/# 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 * http://initd.org/psycopg/docs/install.html 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. 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. } } 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", } } Sviluppo in locale ===================== 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 Fast DB sync ---------------- :: remoto: pg_dump -c piffanet > piffasql Locale: psql piffanet < piffasql Generate VHosts ----------------- Con un file names contenente un nome per riga:: while read name; do echo " ServerAdmin webmaster@andreamanni.com ServerName $name.piffa.net DocumentRoot /home/$name/public Options Indexes FollowSymLinks MultiViews IndexIgnore README.html HEADER.html AllowOverride None Order allow,deny allow from all 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 " > $name; done < names Postgresql ============== 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`` . 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 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 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 Utenti -------------- Da ``root`` fare un su su postgres:: su postgres createuser -d eaman createdb -O eaman piffanet 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 Django start ============= :: cd ~/sites/ . env/bin/activate django-admin startproject name cd name ./manage.py migrate ./manage.py createsuperuser ./manage.py runserver Deployment con WSGI ====================== /etc/apache2/sites-available/site.conf :: 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/ Options FollowSymLinks Require all granted Require all granted # End satic madness Require all granted 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 # 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. * Note: https://docs.djangoproject.com/en/1.10/howto/deployment/wsgi/modwsgi/ 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... 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 . 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. Redis ======= Pacchetti:: pip install django-redis 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}} 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 `````` 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 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")), 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' }), 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 in ``settings.py`` #. Aggiungere ``django.contrib.flatpages.middleware.FlatpageFallbackMiddleware`` a ``MIDDLEWARE_CLASSES`` in ``settings.py`` #. Creare un template Template ~~~~~~~~~~ Le pagine create vengono renderizzate tramite un template, che renderizza almeno il titolo e il contenuto. Il default e' ``templates/flatpages/default.html`` :: {{ flatpage.title }} {{ flatpage.content }} 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) 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::

Elenco articoli

This is the list of available articoli atm

{% if articoli %}
    {% for articolo in articoli.object_list %}
  1. {{ articolo.titolo }}
  2. {% endfor %}
urls.py:: from django.conf.urls.defaults import patterns, include, url urlpatterns = patterns('articoli.views', (r'^$', 'index'), (r'^(?P\d+)/(.*)/$', 'detail'), ) 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 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. Template ------------ Il primo cicolo IF e' tralasciabile se si e' sicuro che devono esistere elementi::

Elenco articoli

This is the list of available articoli atm

{% if articoli %}
    {% for articolo in articoli.object_list %}
  1. {{ articolo.titolo }}
  2. {% endfor %}
{% else %}

No article are available.

{% endif %} 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/ 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') Template ------------ contact.html::
{% csrf_token %} {{ form.as_p }}
invio.html::

Invio avvenuto correttamente

Urls ------- urls.py:: from django.conf.urls.defaults import patterns, include, url urlpatterns = patterns('contatti.views', (r'^$', 'contact'), (r'^invio/$', 'invio'), ) TiniMCE ============= Installare il pacchetto Per l'Admin progetto/templates/admin/flatpages/flatpage/change_form.html:: 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')), Apache ============= Installare il pacchetto ``libapache2-mod-wsgi-py3`` Python handlers =============== Questi *handler* sono da utilizzarsi con mod python, non con WSGI. Si possono comunque usare entrambi coemporaneamente. Mod python basic handler --------------------------- apache.conf:: AddHandler mod_python .py PythonHandler script PythonDebug On script.py:: from mod_python import apache def handler(req): req.content_type = "text/plain" req.write("Hello World!") return apache.OK Publisher ---------- apache.conf:: AddHandler mod_python .py PythonHandler mod_python.publisher PythonDebug On script.py:: s = """\

Hello %s!

""" def index(): return s % 'World' def everybody(): return s % 'everybody' PSP ===== apache.conf:: AddHandler mod_python .psp PythonHandler mod_python.psp PythonDebug On hello.psp:: <% import time %> Hello world, the time is: <%=time.strftime("%Y-%m-%d, %H:%M:%S")%> 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 = 'r' filetype indent plugin on set background=dark set ignorecase set smartcase " Set paste macro map :set paste! :set paste? imap :set paste map :set number! :set number? imap :set number? map :%!astyle " Save macro map :up " Save macro map :up Python-mode ------------------ K Help di una funzione [Ctr][w] cambia finestra [Ctr][w][o] Chiudi tutto a parte la corrente finestra 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. 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; } 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