**Django** django python framework ===== Instalacion ===== 1. Instalar pip sudo aptitude update; sudo aptitude install python-pip 2. Instalar ultima version de django: sudo pip install Django==1.8 ===== Primeros pasos ===== /srv/www/docroot 1. Crear proyecto: django-admin.py startproject project1 └── project1 ├── manage.py └── project1 ├── __init__.py ├── settings.py ├── urls.py └── wsgi.py 2. Motor SGBD vim settings.py 3. Crear aplicacion: cd project python manage.py startapp app1 ├── app1 │   ├── admin.py │   ├── __init__.py │   ├── models.py │   ├── tests.py │   └── views.py ├── manage.py └── project1 ├── __init__.py ├── __init__.pyc ├── settings.py ├── settings.pyc ├── urls.py └── wsgi.py 4. Crear modelos: A mano: vim app1/models.py O 'extraerlo' de una bd ya existente. Luego hay que pulir un poco: python manage.py inspectdb >> app1/models.py 5. Activar modelo (seccion 'INSTALLED_APPS') vim settings.py INSTALLED_APPS = ( 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', 'django.contrib.messages', 'django.contrib.staticfiles', # Uncomment the next line to enable the admin: 'django.contrib.admin', # Uncomment the next line to enable admin documentation: # 'django.contrib.admindocs', 'app1', ) 6. Crear bd y tablas: python manage.py syncdb 7. Ingesta: python manage.py loaddata app1/sql/regional.csv.json 8. Arrancar el servido: python manage.py runserver 0.0.0.0:8080 ===== Interfaz administrativa ===== Ahora la interfaz administrativa se activa por defecto ==== Crear superusuario ==== https://docs.djangoproject.com/en/2.2/ref/django-admin/#django-admin-createsuperuser ==== (Deprecated) Activar la interfaz administrativa ===== 1 vim settings.py Descomentar 'django.contrib.admin' en 'INSTALLED_APPS' 2 python manage.py syncdb 3 vim urls.py Descomentar las siguientes lineas: from django.contrib import admin admin.autodiscover() (r'^admin/', include(admin.site.urls)), ==== (Deprecated) Activar la interfaz administrativa en app1 y tabla 'Tabla1' ==== 2.1 Crear: vim app1/admin.py Con el siguiente contenido: from app1.models import Tabla1 from django.contrib import admin admin.site.register(Tabla1) ===== Django + nginx ===== 1. Instalar paquetes: sudo aptitude install python-flup nginx 2. Configurar nginx: sudo mv /etc/nginx/nginx.conf /etc/nginx/nginx.conf.old sudo vim /etc/nginx/nginx.conf user javi javi; worker_processes 2; error_log /var/log/nginx/error_log info; events { worker_connections 1024; use epoll; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] ' '"$request" $status $bytes_sent ' '"$http_referer" "$http_user_agent" ' '"$gzip_ratio"'; client_header_timeout 10m; client_body_timeout 10m; send_timeout 10m; connection_pool_size 256; client_header_buffer_size 1k; large_client_header_buffers 4 2k; request_pool_size 4k; gzip on; gzip_min_length 1100; gzip_buffers 4 8k; gzip_types text/plain; output_buffers 1 32k; postpone_output 1460; sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 75 20; ignore_invalid_headers on; index index.html; server { listen 80; server_name localhost; location /media { root /srv/test.facsimile/branches/1.1/proyecto2/media; } location ~* ^.+\.(jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|doc|xls|exe|pdf|ppt|txt|tar|mid|midi|wav|bmp|rtf|js|mov) { access_log off; expires 30d; } location / { # host and port to fastcgi server fastcgi_pass 0.0.0.0:8080; fastcgi_param PATH_INFO $fastcgi_script_name; fastcgi_param REQUEST_METHOD $request_method; fastcgi_param QUERY_STRING $query_string; fastcgi_param CONTENT_TYPE $content_type; fastcgi_param CONTENT_LENGTH $content_length; fastcgi_pass_header Authorization; fastcgi_intercept_errors off; } access_log /var/log/nginx/localhost.access_log main; error_log /var/log/nginx/localhost.error_log; } } Parametros a cambiar: -Usuario que ejecuta nginx: user javi javi; -(Opcional) ruta a directorios multimedia (se puede eliminar) server_name localhost; location /media { root /srv/test.facsimile/branches/1.1/proyecto2/media; } - Direccion ip y puerto en el que escucha django fastcgi_pass 0.0.0.0:8080; 3. Arrancar django Suponiendo que el directorio donde este el proyecto sea '/srv/test.facsimile/branches/1.1/proyecto2/': cd /srv/test.facsimile/branches/1.1/proyecto2/; python manage.py runfcgi method=threaded host=0.0.0.0 port=8080 4. Arrancar nginx: sudo /etc/init.d/nginx start 5. Probar: http://localhost/accion_1 Nginx redirige la peticion a: http://localhost:8080/accion_1 ===== Archivos estaticos ===== **OJO** cuando ejecutamos el servidor de desarrollo, todo funciona magicamente. Pero cuando estamos en produccion probablemente serviremos los archivos desde Apache, lo que requiere configuracion adicional para los archivos estaticos https://docs.djangoproject.com/en/1.6/howto/static-files/deployment/#serving-the-site-and-your-static-files-from-the-same-server https://docs.djangoproject.com/en/1.6/howto/deployment/wsgi/modwsgi/#serving-files 1. Creamos el directorio desde donse se van a servir los archivos estaticos. Puede estar fuera del DocumentRoot de Apache mkdir /tmp/static 2. Definir la variable "STATIC_ROOT" en el archivo "settings.py" del proyecto de django: vim /srv/www/docroot/project1/project1/settings.py ... STATIC_ROOT = '/tmp/static/' ... 3. Entramos en el site de django y ejecutamos un comando para copiar todos los archivos estaticos a ese directorio: cd /srv/www/docroot/project1 python manage.py collectstatic You have requested to collect static files at the destination location as specified in your settings. This will overwrite existing files! Are you sure you want to do this? Type 'yes' to continue, or 'no' to cancel: yes Copying '/usr/local/lib/python2.7/dist-packages/django/contrib/admin/static/admin/css/login.css' ... Al final tenemos en "/tmp/static" todo un arbol con archivos estaticos (CSS, js, etc...) 4. Anyadir la entrada al VirtualHost de Apache (version < 2.4): ... Alias /static/ /tmp/static/ # Apache < 2.4 Order deny,allow Allow from all ... Para Apache >= 2.4: ... Alias /static/ /tmp/static/ # Apache >= 2.4 Require all granted ... 5. Recargar la configuracion de Apache para que los cambios tomen efecto: sudo service apache2 reload ===== Consultas ===== QuerySet Tabla1.objects.filter(pub_date__year=2006) ===== Ejemplo aplicacion sin BBDD ===== 1. Nos metemos dentro de un directorio **fuera del docroot de apache**: cd /srv 2. Creamos el proyecto (utilizo sudo porque no tengo permisos en este directorio): sudo django-admin.py startproject sms_project sudo chown -R usuario:usuario sms_project 3. Creamos la app: cd /srv/sms_project python manage.py startapp sms_app 4. Creamos una vista por defecto (hello world). Editamos: vim /srv/sms_project/sms_app Y la dejamos tal que asi: from django.http import HttpResponse def index(request): return HttpResponse("Hello, world. You're at the sms index.") 5. Creamos una regla para que por defecto todas las peticiones que apunten a 'sms_app' muestren la vista 'index'. Editamos: vim /srv/sms_project/sms_app/urls.py Y la dejamos tal que asi: from django.conf.urls import patterns, url from sms_app import views urlpatterns = patterns('', url(r'^$', views.index, name='index') ) 6. Creamos una regla para que cuando el servidor web reciba peticiones para "sms_app/" cargue el modulo "/srv/sms_project/sms_app/urls.py" Editamos: vim /srv/sms_project/sms_project/urls.py Y la dejamos tal que asi: from django.conf.urls import patterns, include, url urlpatterns = patterns('', url(r'^sms_app/', include('sms_app.urls')), ) 7. Arrancamos el servidor de prueba: cd /srv/sms_project; python manage.py runserver 8. Pedimos una pagina web: elinks http://localhost:8000/sms/sms_app/ Deberia responder: Hello, world. You're at the sms index. ===== Apache mod_wsgi ===== Configuracion general de apache. Escenario: | /srv/www/docroot | Document root | | /srv/www/docroot/project1 | Proyecto django | | /srv/www/docroot/project1/project1/wsgi.py | Creado al ejecutar "django-admin.py startproject project_tracker" | 1. Instalar libapache2-mod-wsgi: sudo aptitude update; sudo aptitude install libapache2-mod-wsgi 2. Habilitar modulo: sudo a2enmod wsgi 3. Deshabilitar sitio por defecto: sudo a2dissite 000-default 4. Crear un nuevo sitio: sudo vim /etc/apache2/sites-available/django Y dejarlo tal que asi: ServerName django.example.com ServerAdmin webmaster@localhost DocumentRoot /srv/www/docroot SSLEngine on SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key WSGIScriptAlias / /srv/www/docroot/project1/project1/wsgi.py WSGIDaemonProcess django.example.com python-path=/srv/www/docroot/project1:/usr/local/lib/python2.7/site-packages WSGIProcessGroup django.example.com Order deny,allow #Require all granted Allow from all ErrorLog ${APACHE_LOG_DIR}/django.example.com.error.log # Possible values include: debug, info, notice, warn, error, crit, # alert, emerg. LogLevel warn CustomLog ${APACHE_LOG_DIR}/django.example.com.access.log combined 5. Habilitar el sitio: sudo a2ensite django 6. Recargar apache: sudo /etc/init.d/apache2 reload ===== Base de datos ===== Lo primero configurar la BD: vim /srv/www/docroot/project1/project1/settings.py Y editar las siguientes lineas: ... DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'. 'NAME': 'django', # Or path to database file if using sqlite3. 'USER': 'asterisk', # Not used with sqlite3. 'PASSWORD': 'xxxxxx', # Not used with sqlite3. 'HOST': 'mysql-1.dev.jj.com', # Set to empty string for localhost. Not used with sqlite3. 'PORT': '', # Set to empty string for default. Not used with sqlite3. } } ... Flujo habitual para mantener el modelo: 1. Change your models (in **models.py**). 2. Run **python manage.py makemigrations** to create migrations for those changes 3. Run **python manage.py migrate** to apply those changes to the database. ==== django -> SGBD ==== Crea BBDD y las tablas a partir del modelo. Si 'model.py' no existe y se habilita el sistema de autenticacion de Django creara unas tablas: cd /srv/sms_project; python manage.py syncdb Creating tables ... Creating table auth_permission Creating table auth_group_permissions Creating table auth_group Creating table auth_user_user_permissions Creating table auth_user_groups Creating table auth_user Creating table django_content_type Creating table django_session Creating table django_site Crear cuenta superusuario: You just installed Django's auth system, which means you don't have any superusers defined. Would you like to create one now? (yes/no): yes Username (leave blank to use 'usuario'): E-mail address: javi@legido.com Password: Password (again): Superuser created successfully. ==== SGBD -> django ==== AVISO: sobreescribiremos el archivo ya existente. Si queremos crear el modelo a partir de una BBDD que ya exista: cd /srv/www/docroot/project1; python manage.py inspectdb >> app1/models.py NOTA: si la BBDD es muy compleja probablemente habra que retocar 'model.py' (tipicamente cambiar el orden de las clases) ===== Django API ===== cd /srv/sms_project; python manage.py shell ===== Autenticacion ===== https://docs.djangoproject.com/en/dev/topics/auth/?from=olddocs#built-in-forms https://docs.djangoproject.com/en/dev/topics/auth/customizing/ https://docs.djangoproject.com/en/1.4/howto/auth-remote-user/ ===== Templates ===== 1. Incluir la app en settings: vim /srv/www/docroot/project1/project1/settings.py Y anyadir la siguiente linea en "INSTALLED_APPS": INSTALLED_APPS = ( ... 'app1', ) 2. Ejemplo de uso: /srv/www/docroot/project1/app1/views.py def invoice_query(request): ... return render_to_response('template1.html',d,\ context_instance=RequestContext(request)) Que tomara la plantilla de la siguiente ruta: /srv/www/docroot/project1/app1/templates/template1.html ===== Internacionalizacion ===== ==== Flujo de trabajo tipico ==== https://docs.djangoproject.com/en/1.6/topics/i18n/translation/#url-internationalization * Estructura de archivos implicados en la internacionalizacion: . +-- project1 |   +-- __init__.py |   +-- settings.py |   +-- urls.py |   +-- wsgi.py +-- app1 |   +-- admin.py |   +-- forms.py |   +-- __init__.py |   +-- locale |   |   +-- es |   |   +-- LC_MESSAGES |   |   +-- django.mo |   |   +-- django.po |   +-- models.py |   +-- tests.py |   +-- urls.py |   +-- views.py +-- manage.py 1) Requisitos para habilitar internacionalizacion: project1/settings.py ... INSTALLED_APPS = ( ... 'app1', ) MIDDLEWARE_CLASSES = ( ... 'django.middleware.locale.LocaleMiddleware', ) USE_I18N = True USE_L10N = True 2. Crear //translation strings// con el identificador que se va a usar luego en los //message file// (uno por cada idioma) para traducir la cadena. * Ejemplo de uso en vista (translation string "hello_world"). Hemos usado el alias _, muy usado: app1/views.py from django.template import RequestContext from django.shortcuts import render_to_response from django.http import HttpResponseRedirect, HttpResponse from django.utils.translation import ugettext as _ def test(request, ): output = _("hello_world"),' - ', request.LANGUAGE_CODE return HttpResponse(output) * Ejemplo de uso en template (translation string "hello_world"): {% load i18n %} {% trans "hello_world" as hello_world %}

{{ hello_world }}

3. Crear la estructura de directorios (el archivo .po de momento no hace falta) adecuados (ver arbol al inicio de esta seccion). Por ejemplo: mkdir -p locale/es/LC_MESSAGES 4. Crear de forma automatica los //message files// para todas las lenguas que tienen directorio: django-admin.py makemessages -a processing locale en processing locale es processing locale ca 5. Ahora tenemos los .po creados de forma automatica para todos los idiomas, cada uno de los cuales con sus //trasnlation string//. Ahora tenemos que completar el campo "msgstr" para cada una de las //translation string// con la traduccion. Ejemplo de archivo .po: app1/locale/es/LC_MESSAGES/django.po # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2014-02-18 08:58+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: views.py:43 msgid "hello_world" msgstr "Hola mundo" **TODO**: eliminar las lineas que contengan la palabra fuzzy 6. Una vez editados los .po para generar los binarios, los .mo: django-admin.py compilemessagesprocessing file django.po in /srv/www/incasol/incasolproj/invoices/locale/en/LC_MESSAGES processing file django.po in /srv/www/project/project/app/locale/es/LC_MESSAGES processing file django.po in /srv/www/project/project/app/locale/ca/LC_MESSAGES **IMPORTANTE**: tras compilar mensajes hay que reiniciar el servidor de paginas web para que los cambios tomen efecto 7. Reiniciar el servidor de paginas web para que tome los cambios efectuados en los archivos .po ==== Vista ya creada para cambiar el idioma ==== En este ejemplo cambiamos el lenguaje en una vista en funcion de un parametro que llega via GET: http://example.com?newLang=ca app1/views.py from django.template import RequestContext from django.shortcuts import render_to_response from django.http import HttpResponseRedirect, HttpResponse from invoices.models import Expedient, RawSqlQueries from forms import InvoiceForm from django.utils.translation import ugettext as _, activate def test(request): lang = request.GET.get('newLang') activate(lang) output = _("Hello"),' - ', request.LANGUAGE_CODE, ' - ',\ request.session['django_language'] return HttpResponse(output) ===== Sesiones ===== https://docs.djangoproject.com/en/dev/topics/http/sessions/ * En archivos docroot/project1/project1/settings.py SESSION_ENGINE = "django.contrib.sessions.backends.file" Para saber el directorio por defecto donde almacena los archivos temporales, siempre que 'SESSION_FILE_PATH' **NO** se haya especificado: python -c "import tempfile; print tempfile.gettempdir()" ===== LDAP ===== pythonhosted.org/django-auth-ldap 1. Instalar modulo: sudo aptitude install python-ldap sudo pip install django-auth-ldap 2. Editar settings: docroot/project1/project1/settings.py # LDAP import ldap from django_auth_ldap.config import LDAPSearch AUTHENTICATION_BACKENDS = ( 'django_auth_ldap.backend.LDAPBackend', ) AUTH_LDAP_SERVER_URI = "ldaps://ldap.example.com" AUTH_LDAP_BIND_DN = "cn=readonly,dc=example,dc=com" AUTH_LDAP_BIND_PASSWORD = "secret" #AUTH_LDAP_START_TLS = True AUTH_LDAP_USER_SEARCH = LDAPSearch("dc=example,dc=com", ldap.SCOPE_SUBTREE, "(uid=%(user)s)") # LDAP groups from django_auth_ldap.config import LDAPSearch, GroupOfNamesType AUTH_LDAP_GROUP_SEARCH = LDAPSearch("ou=groups,dc=example,dc=com", ldap.SCOPE_SUBTREE, "(objectClass=groupOfNames)" ) AUTH_LDAP_GROUP_TYPE = GroupOfNamesType() AUTH_LDAP_REQUIRE_GROUP = "cn=ldapgroup1,ou=groups,dc=example,dc=com" En este ejemplo nos conectamos via TLS al servidor LDAP "ldap.example.com" y requerimos que el usuario pertenezca al grupo "ldapgroup1", que es un objeto LDAP de tipo "groupOfNames" 3. Crear el modelo de base de datos, si es que no lo estaba ya: cd docroot/project1; python manage.py syncdb Contestar a las preguntas. **TODO**: ver si hay alguna forma de evitar este paso, y que se almacenen todos los valores en sesiones. **SOLUCION 1**: sobreescribir _LDAPUser._get_or_create_user() de "/usr/local/lib/python2.7/dist-packages/django_auth_ldap/backend.py" **SOLUCION 2**: escribir nuestro propio backend tomando django_auth_ldap como ejemplo 4. Ejemplo de formulario con validacion de usuario: from django.contrib.auth import authenticate def login(request): ''' Displays/process login form''' d = {} if request.method == 'POST': # If the form has been submitted... d['form'] = LoginForm(request.POST) # A form bound to the POST data if d['form'].is_valid(): # All validation rules pass # Process the data in form.cleaned_data username = d['form'].cleaned_data['username'] password = d['form'].cleaned_data['password'] user = authenticate(username=username, password=password) if user is not None: logger.info(user) return HttpResponse('Success') if user.is_active: login(request, user) return HttpResponse('Success') else: return HttpResponse('Disabled account') else: return HttpResponse('Invalid login') else: d['result'] = "There was an error processing the form" else: d['form'] = LoginForm() # An unbound form return render_to_response('login.html',d,\ context_instance=RequestContext(request)) Falta el template y el resto de la vista. Es solo un ejemplo ===== Errores ===== ==== The password is too similar to the username. ==== En realidad no es un error, es solo para documentar un atajo para evitar esta restricción a la hora de especificar una contraseña para un nuevo usuario desde la interfaz gráfica del módulo admin. https://stackoverflow.com/a/35330167 1. Crear el usuario desde el admin: http://localhost:8000/admin/auth/user/add/ 2. Iniciar shell cd /path/django && python manage.py shell 3. Cambiar la contraseña de ese usuario, en este ejemplo "your_user": from django.contrib.auth.models import User user = User.objects.get(username='your_user') user.set_password('simple') user.save() ==== Error al crear app ==== Comando: python manage.py startapp app1 Error: File "manage.py", line 16 ) from exc ^ SyntaxError: invalid syntax Solución: python3 manage.py startapp app1