User Tools

Site Tools


apuntes:aplicaciones_web

Creación de aplicaciones web

Django

Django 1) es un framework open source para el desarrollo web escrito en lenguaje Python que sigue la arquitectura Model-View-Controller (MVC), que es un patrón de diseño de arquitectura para la construcción de aplicaciones aplicable con independencia de la plataforma para la que se desarrolla.

La principal característica de este framework es la rapidez y facilidad con la que se pueden crear las aplicaciones web, incluso si éstas trabajan con Bases de Datos. Está totalmente orientado al desarrollo fácil y rápido de forma que el programador no tenga que repetir las tareas una y otra vez, lo que se conoce como DRY (Don't Repeat Yourself).

mvc.jpg
Figure 1: Arquitectura MVC (Model-View-Controller)

Instalación del framework Django

Antes de comenzar con la instalación del propio framework, necesitaremos instalar Python en el equipo puesto que Django está escrito en este lenguaje.

Instalación de Python

La instalación de Python es muy sencilla. Simplemente accedemos a la sección Descargas de su web y descargamos el paquete que corresponda con nuestro Sistema Operativo y la versión que queramos (en nuestro caso la 3.6.x).

Una vez instalado podremos disponer del intérprete del lenguaje directamente desde nuestra consola de comandos. Si ejecutamos el comando python (o python3) se cargará y esperará que introduzcamos alguna orden.

santi@zenbook:$ python
Python 3.5.3 (default, Jan 19 2017, 14:11:04) 
[GCC 6.3.0 20170118] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> |

Para salir del intérprete bastará con ejecuta la orden quit()

Hay una guía de Python en la sección Anexos a la que habrá que echarle un vistazo antes de seguir, puesto que a partir de ahora tendremos que programar en este lenguaje.

Instalación de Django

Para la instalación de Django necesitamos la utilidad pip (gestor de de instalación de paquetes para Python) pero viene de serie en las versiones de Python >= 3.4 por lo que no será necesario hacer nada. Directamente podremos usarla para que se encargue de instalarnos el nuevo framework con el siguiente comando:

santi@zenbook:$ pip install django==1.11.8

Para comprobar que todo está funcionando correctamente podemos ejecutar la consola de Python y comprobar que versión del framework se ha instalado

santi@zenbook:$ python
Python 3.5.3 (default, Jan 19 2017, 14:11:04) 
[GCC 6.3.0 20170118] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import django
>>> print(django.get_version())
1.11.4

Introducción

Como se ha dicho anteriormente, el desarrollo en Django se realiza siguiendo una arquitectura MVC, aunque más concretamente en este caso se trabaja bajo lo que se conoce como arquitectura MVT (Model-View-Template) que sigue, en el caso de Django, al siguiente esquema:

Figure 2: Arquitectura MVT (Model-View-Template)

En el esquema se pueden distinguir los diferentes componentes que formarán cualquier aplicación web desarrollada con este framework y que son las siguientes:

  • Routing (urls): Es un fichero que contendrá las
  • View:
  • Template:
  • Model: Representa qué forma tienen la información que gestiona nuestra aplicación. Serán las clases Python que definen la estructura de la Base de Datos donde se almacena dicha información

Crear un proyecto y la primera aplicación

Crear un proyecto

Django no sólo es una conjunto de librerías de código escrito en Python sino que además dispone de una serie de comandos que pueden hacer trabajo por nosotros.

En el caso de que tengamos que iniciar un nuevo proyecto desde cero, disponemos de un comando que nos creará todas la estructura y ficheros necesarios (con el código correspondiente) para poder empezar a trabajar sin que tengamos que realizar esa tarea manualmente y de forma repetitiva cada vez que queramos empezar cada proyecto.

Asi, con el siguiente comando del framework, lanzándolo directamente desde la consola de comandos, se creará una carpeta 'peliculas' con la estructura inicial de un proyecto de Django

santi@zenbook:$ django-admin startproject peliculas
Figure 3: Estructura inicial de un proyecto Django

En la imagen anterior se puede observar como queda la estructura del proyecto inicial. A partir de ahora podemos decidir trabajar con el editor de código que más nos guste. En este caso la captura corresponde al editor que usaremos nosotros que es PyCharm. Entre los ficheros que ya aparecen creados podemos destacar los siguientes:

  • __init__.py: Este fichero le indica a Python que considere a este directorio como un paquete
  • settings.py: Contiene todos los parámetros de configuración del proyecto
  • urls.py: En este fichero se declaran todas las urls. Más adelante veremos como trabajar con él
  • wsgi.py: Aqui configuraremos nuestro proyecto para que pueda desplegarse en servidores compatibles con WSGI, como puede ser Apache

Además, se debe respetar la jerarquía de directorios que se observa en la imagen, donde se puede ver un directorio peliculas que contiene a otro con el mismo nombre, siendo posible renombrar sin ningún problema el directorio más externo ya que sólo se usa como contenedor del proyecto. El más interno es el que hace de paquete Python y el que usaremos más adelante durante el desarrollo para referirnos al mismo.

Una vez creado entonces el proyecto inicial, podemos comprobar que todo funciona correctamente lanzando el servidor de pruebas que Django incorpora para la fase de desarrollo, utilizando el siguiente comando

santi@zenbook:$ python3 manage.py runserver
Performing system checks...
 
System check identified no issues (0 silenced).
August 31, 2017 - 13:42:42
Django version 1.11.4, using settings 'peliculas.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

Y si todo funciona correctamente veremos un mensaje parecido al que se muestra justo encima.

Hay que tener en cuenta que se trata de un servidor de pruebas, para que podamos ir probando el proyecto durante el desarrollo del mismo. El despliegue de una aplicación es a veces un proceso largo y realizar dicho despliegue cada vez que se quiere probar una nueva funcionalidad puede resultar una pérdida de tiempo. De esta manera podemos probar rapidamente nuestra aplicación pero deberemos desplegar la misma de la forma correcta utilizando algún servidor más robusto si la queremos poner en marcha para producción.

Añadir aplicaciones a un proyecto

santi@zenbook:$ python3 manage.py startapp mispeliculas

Por último, antes de comenzar el desarrollo de nuestra aplicación web, echaremos un vistazo al fichero settings.py donde encontraremos la opción INSTALLED_APPS con la siguiente información ya configurada:

. . .
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]
. . .

Cada línea indica una aplicación que Django tiene ya preinstalada y que podremos usar más adelante. Siempre podremos eliminar aquellas que sepamos con certeza que no vamos a usar.

Lo que sí tendremos que hacer antes de continuar es indicarle a Django que queremos que inicialize dichas aplicaciones y lo haremos con el siguiente comando, que basicamente lo que hace es crear las Bases de Datos que requieran las aplicaciones instaladas en el proyecto.

Así, ejecutaremos el siguiente comando antes de continuar, obteniendo la correspondiente salida por pantalla:

santi@zenbook:$ python3 manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying sessions.0001_initial... OK

Ahora ya estamos listos para empezar a trabajar en la aplicación web cuya estructura se ha generado anteriormente.

Definir el modelo y configurar la Base de Datos

Antes de definir el modelo mediante clases Python, tenemos que configurar el proyecto para que pueda hacer uso de algún motor de Bases de Datos. Para este ejemplo dejaremos la configuración como está, de forma que se utilizará el motor de SQLite tal y como muesra el parámetro ENGINE en la opción DATABASES del fragmento del fichero de configuración settings.py que se muestra a continuación. Allí también se indica el nombre del fichero que tendrá en este caso la Bases de Datos.

Si empleamos algún otro motor como MySQL o PostgreSQL tendremos que indicar los parámetros que corresponda en estos casos (como host, usuario, contraseña, . . .).

Además, tenemos que añadir nuestra aplicación mispeliculas a la lista de aplicaciones disponibles en este proyecto, tal y como aparece en la opción INSTALLED_APPS.

settings.py
. . .
 
INSTALLED_APPS = [
    'mispeliculas.apps.MispeliculasConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'mispeliculas',
]
 
. . .
 
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}
 
. . .

Y siguiendo la filosofía, como ya decíamos anteriormente, DRY (Don't Repear Yourself), Django nos facilita de nuevo el trabajo a la hora de trabajar con Bases de Datos.

Así, simplemente tenemos que definir nuestro modelo de datos implementando las clases que consideremos oportunas y Django hará el resto (creará la Bases de Datos, el SQL para crear las tablas, . . .) con un par de simples comandos como veremos más adelante.

Lo primero que debemos hacer es definir el Modelo de Datos que necesitamos para nuestra aplicación. En este caso hemos decidido crear una aplicación web para la gestión de nuestras peliculas y series por lo que definimos dos clases Pelicula y Serie con los atributos que consideramos. Como se puede observar en el siguiente fragmento de código podemos indicar para cada uno de ellos el tipo y las restricciones necesarias directamente en la definición de la clase.

from django.db import models
 
class Pelicula(models.Model):
    titulo = models.CharField(max_length=200)
    genero = models.CharField(max_length=200)
    director = models.CharField(max_length=200, blank=True)
    fecha = models.DateField
    descargada = models.BooleanField(default=False, blank=True)
 
    def __str__(self):
        return self.titulo
 
class Serie(models.Model):
    titulo = models.CharField(max_length=200)
    genero = models.CharField(max_length=200)
    fecha = models.DateField
    temporadas = models.IntegerField(default=1)
    completada = models.BooleanField(default=False)
    descargada = models.BooleanField(default=False)
 
    def __str__(self):
        return self.titulo
 
class Capitulo(models.Model):
    numero = models.IntegerField(default=0)
    temporada = models.IntegerField(default=0)
    serie = models.ForeignKey(Serie, on_delete=models.CASCADE)
    fecha_emision = models.DateField
 
    def __str__(self):
        return self.serie.titulo + " " + self.temporada + "x" + self.numero

Una vez escritas las clases ejecutaremos el siguiente comando para indicarle que se han producido cambios en nuestro modelo (en este caso se ha creado por primera vez) y que prepare los cambios necesarios sobre la Base de Datos (todavía no los ejecuta).

santi@zenbook:$ python3 manage.py makemigrations mispeliculas
Migrations for 'mispeliculas':
  mispeliculas/migrations/0001_initial.py:
    - Create model Pelicula
    - Create model Serie
    - Create model Capitulo
    - Add field serie to capitulo

Y para lanzar esos cambios sobre la Base de Datos tenemos que lanzar el comando migrate que veremos más adelante. Antes, conviene saber que siempre que queramos podremos ejecutar un comando si queremos revisar el código SQL que Django ha preparado. Este comando no tiene efecto ninguno ni realiza cambios en la Base de Datos, simplemente imprime por pantalla el código SQL del cambio que hemos dejado listo con el comando anterior.

santi@zenbook:$ python3 manage.py sqlmigrate mispeliculas 0001
BEGIN;
--
-- Create model Serie
--
CREATE TABLE "mispeliculas_serie" (
    "id" serial NOT NULL PRIMARY KEY,
    "titulo" varchar(200) NOT NULL,
    "genero" varchar(200) NOT NULL,
    . . .
    . . .
);
--
-- Create model Pelicula
--
CREATE TABLE "mispeliculas_pelicula" (
    "id" serial NOT NULL PRIMARY KEY,
    "titulo" varchar(200) NOT NULL,
    . . .
    . . .
);
 
. . .
. . .

Hay que tener en cuenta también que este paso no es obligatorio llevarlo a cabo. Siempre podremos ejecutar el comando makemigrations y migrate seguidos confiando en que Django haga correctamente su trabajo.

santi@zenbook:$ python3 manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, mispeliculas, sessions
Running migrations:
  Rendering model states... DONE
  Applying mispeliculas.0001_initial... OK

Además, si queremos trabajar con otro SGBD, como por ejemplo MySQL, simplemente tendremos que cambiar la configuración de conexión con la Base de Datos y crear (makemigrations) y lanzar (migrate) los cambios para que éstos sean efectivos.

. . .
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'nombre',
        'USER': 'usuario',
        'PASSWORD': 'contraseña',
        'HOST': 'localhost',
        'PORT': '3306',
    }
}
. . .

Realizar cambios en el modelo durante el desarrollo

Y siguiendo con lo que contabamos en el apartado anterior a la hora de crear nuestro modelo por primera vez, cada vez que realicemos algún cambio bastará con ejecutar los comandos makemigrations y migrate y los cambios se prepararán y ejecutarán sobre la Base de Datos, respectivamente.

santi@zenbook:$ python3 manage.py makemigrations mispeliculas
. . .
. . .
santi@zenbook:$ python3 manage.py migrate
. . .
. . .

Probar nuestro modelo

En cualquier momento podemos lanzar, desde la termina, una consola interactiva de Python sobre nuestra aplicación web.

santi@zenbook:$ python3 manage.py shell

Una vez iniciada la consola, disponemos de los modelos definidos en nuestra aplicación web Django para interactuar con ellos y comprobar rapidamente si todo funciona como esperamos que lo haga. Podemos registrar objetos en la Base de Datos, consultarlos, eliminarnos, modificarlos, . . .

>>> from peliculas.models import Pelicula
. . .
>>> # Registra una pelicula en la Base de Datos
>>> pelicula = Pelicula(titulo='Rocky', director='Silvester Stallone')
>>> pelicula.save()
>>> # Lista todas las peliculas
>>> lista_peliculas = Pelicula.objects.all()
>>> print(lista_peliculas)
. . .
>>> # Obtiene una pelicula concreta por titulo
>>> pelicula = Pelicula.objects.get(titulo='Rocky')
>>> print(pelicula.titulo)
. . .
>>> # Elimina una pelicula de la Base de Datos
>>> pelicula = Pelicula.objects.get(titulo='Rocky')
>>> pelicula.delete()
. . .
>>> # Modifica datos de una pelicula
>>> pelicula = Pelicula.objects.get(titulo='Rocky')
>>> pelicula.director = 'Otro'
>>> pelicula.save()
. . .
>>> # Obtiene las peliculas ordenadas por un campo
>>> peliculas = Pelicula.objects.order_by('titulo')
>>> print(peliculas)
. . .

Django dispone de una API muy completa en relación al API de consultas donde se pueden ver numerosos ejemplos de las diferentes vías de las disponemos para consultar y trabajar con la Base de Datos en nuestras aplicaciones web.

El panel de administración de Django

Por defecto Django proporciona un panel de administración para nuestra aplicación web y éste viene preparado para ser instalado en el fichero setttings.py. Simplemente tenemos que crear el primer usuario administrador con el siguiente comando:

santi@zenbook:$ python3 manage.py createsuperuser

Lanzar el servidor de pruebas:

santi@zenbook:$ python3 manage.py runserver

Y podemos acceder al mismo a través de la URL http://localhost:8000/admin.

Por defecto tendremos acceso a la gestión de usuarios y grupos de dicho panel. Si queremos además poder gestionar los diferentes objetos que hemos definidos como modelos de nuestra Base de Datos, tendremos que indicarlo como tal en el fichero admin.py que podemos encontrar en la carpeta de nuestra aplicación.

admin.py
from django.contrib import admin
 
from .models import Pelicula, Serie, Capitulo
 
admin.site.register(Pelicula)
admin.site.register(Serie)
admin.site.register(Capitulo)

Y de esa manera podremos realizar las operaciones CRUD sobre los tres objetos definidos (en este caso) desde un panel ya creado por el propio framework.

Creación de vistas y templates

Para la creación de las vistas de nuestra aplicación utilizaremos directamente el sistema de plantillas (templates) que incluye el framework.

Lo primero que haremos será definir una plantilla que será donde escribiremos el código HTML que defina la estructura de la vista. Además, podremos acceder a la información que pasemos desde el código Python a dicha vista.

En el siguiente ejemplo hemos preparado una plantilla donde se listan todas las películas de la Base de Datos. Se espera una variable lista_peliculas con toda la información, que habremos pasado al cargar la plantilla.

En este caso además se trata de la página que hará de portada de nuestra aplicación (index.html) por lo que la asociaremos con la vista index que luego más adelante tendremos que registrar en la aplicación para definir a través de que URL podremos acceder.

Antes de seguir tendremos que crear la carpeta templates dentro de la carpeta de nuestra aplicación y a continuación otra carpeta dentro de éste con el nombre de la misma (mispeliculas), quedando la ruta templates/mispeliculas (en este caso). Será allí donde almacenaremos todas las plantillas de la aplicación.

Ahora definimos la plantilla en la que aparecerá el listado de peliculas mostrando el título para cada una de ellas y creándose un hipervínculo a través del id de la misma a otra view donde podríamos hacer que se mostraran los detalles de cada una:

index.html
<h1>Mis películas</h1> <a href="#">+</a>
{% if lista_peliculas %}
   <ul>
       {% for pelicula in lista_peliculas %}
           <li>{{ pelicula.titulo }}</li>
       {% endfor %}
    </ul>
{% else %}
   <p>No hay películas disponibles</p>
{% endif %}

Ahora, en el fichero views.py de nuestra aplicación tendremos que definir la vista index que cargará la plantilla que acabamos de crear y le pasará los parámetros necesarios, en este caso la lista de películas.

from django.shortcuts import render
 
. . .
 
def index(request):
    lista_peliculas = Pelicula.objects.all()
    contexto = {'lista_peliculas': lista_peliculas}
    return render(request, 'mispeliculas/index.html', contexto)

Por último, tenemos que registrar la URL que le queramos definir a esta vista. Para eso, creamos el fichero urls.py en nuestra aplicación (por ser la primera vista que hemos creado, a partir de aquí será añadir sobre este mismo fichero) y definimos la URL para acceder a la portada:

mispeliculas/urls.py
from django.conf.urls import url
 
from . import views
 
urlpatterns = [
    url(r'^$', views.index, name='index')
]

Y a continuación añadimos el contenido de este fichero al fichero urls.py del proyecto. A estas alturas, y puesto que ya deberíamos tener el panel de administración activado, este fichero debería quedar de la siguiente manera:

peliculas/urls.py
from django.conf.urls import url,include
from django.contrib import admin
 
urlpatterns = [
    url(r'^peliculas/', include('mispeliculas.urls')),
    url(r'^admin/', admin.site.urls),
]

Así, si accedemos a la URL http://localhost:8000/peliculas se cargará la vista definida como index utilizando la plantilla que hemos escrito.

Y a continuación, podemos preparar la view y plantilla para ver el detalle de cada una de las películas. La view quedaría de la siguiente manera, definida en el fichero views.py

. . .
def get_pelicula(request, pelicula_id):
    try:
        pelicula = Pelicula.objects.get(pk=pelicula_id)
    except Pelicula.DoesNotExist:
        raise Http404("La pelicula no existe")
 
    contexto = {'pelicula': pelicula}
    return render(request, 'mispeliculas/pelicula.html', contexto)
 
. . .

Y la plantilla quedaría asi:

<h1>{{ pelicula.titulo }}</h1>
<h2>{{ pelicula.director }}</h2>
<h2>{{ pelicula.genero }}</h2>

Y por último, añadir la url en el fichero urls.py de la aplicación mispeliculas:

. . .
    url(r'^peliculas/(?P<pelicula_id>[0-9]+)/$', views.get_pelicula, name='peliculas'),
. . .

Y ahora, podríamos modificar la plantilla index.html para que el título de cada película sea un enlace a esta nueva view donde podamos ver los detalles de la misma:

index.html
<h1>Mis películas</h1> <a href="#">+</a>
{% if lista_peliculas %}
   <ul>
       {% for pelicula in lista_peliculas %}
           <li><a href="{% url 'peliculas' pelicula.id %}">{{ pelicula.titulo }}</a></li>
       {% endfor %}
    </ul>
{% else %}
   <p>No hay películas disponibles</p>
{% endif %}

Trabajar con contenido estático

Se considera contenido estático en una aplicación web Django a todos los recursos que utilicemos para su construcción a excepción de las plantillas HTML y el código Python: Hojas de estilo, ficheros de Javascript, imágenes, . . .

De forma similar a como hicimos con las plantillas, crearemos primero una carpeta llamada static directamente dentro de nuestra aplicación (en este caso se llama peliculas) y a continuación crearemos otra carpeta peliculas dentro de static. A partir de allí podremos organizar el contenido estático de nuestra aplicación web (css, js, img, . . ., por ejemplo).

Para poder hacer referencia a cualquier contenido estático, tendremos que hacer uso de la etiqueta static que previamente tendremos que cargar al inicio de la plantilla desde donde queramos acceder con la orden load. A continuación se muestran algunos ejemplos de uso de contenido estático desde una plantilla de nuestra aplicación web.

{% load static %}
 
. . .
<link href="{% static 'peliculas/css/bootstrap.min.css' %}" rel="stylesheet">
. . .
<img src="{% static 'peliculas/img/imagen.jpg' %}"/>
. . .

Incluir código HTML en una plantilla

También es posible formar una plantilla a partir del código HTML incluido en otras, como en el siguiente ejemplo que se adjunta donde se suponen una serie de documentos HTML que forman cabecera y pie de las plantillas de la aplicación web.

Además, si el caso lo requiere es posible parametrizarlas pasando parámetros a las plantillas con la orden with de forma que éstos podrán ser utilizados en la plantilla destino (con la notación {{ parametro }}).

{% include 'peliculas/cabecera.html' %}
{% include 'peliculas/navbar.html' with titulo='Portada' %}
. . .
<h1>Esta es la página principal de mi aplicación web</h1>
. . .
{% include 'peliculas/pie.html' %}

Creación de formularios

Primero definimos el formulario en Django (crearemos el fichero forms.py por ser el primero formulario que realizamos. A partir de ahora añadiremos clases aqui para ir registrando más formularios):

forms.py
from django import forms
from .models import Pelicula
. . .
class PeliculaForm(forms.ModelForm):
    class Meta:
        fields = ('titulo', 'director', 'genero')
        model = Pelicula
. . .

Y ahora tendremos que crear la correspondiente view y url para la plantilla que acabamos de crear:

views.py
. . .
def nueva_pelicula(request):
    form = PeliculaForm()
    context = {'form': form}
    return render(request, 'mispeliculas/nueva_pelicula.html', context)
. . .
urls.py
. . .
    url(r'^nueva_pelicula/$', views.nueva_pelicula, name='nueva_pelicula'),
. . .

A continuación, definimos la plantilla con el código HTML del mismo, incluyendo el token csrf_token para evitar el ataque Cross-Site Request Forgery.

Para cada caja de texto (por cada etiqueta input) que queramos representar usaremos el campo definido en el formulario (form.titulo, por ejemplo) y podremos añadir (incluyendo el paquete django-widgets-tweaks y cargándolo siempre que sea necesario) un estilo si queremos modificar su apariencia con la orden add_class e incluso añadir algún atributo más con attr, pudiendo indicarse cualquier atributo que aceptara una etiqueta input de HTML.

Para poder utilizar el módulo django-widgets-tweaks habrá que incluir como una aplicación de nuestro proyecto en el fichero settings.py:

settings.py
. . .
INSTALLED_APPS = [
    . . . ,
    . . . ,
    'widget_tweaks'
]
. . .

Además, podemos hacer que muestre justo debajo los errores (cuando los haya) asociados con cada uno de los campos. Esto tendrá efecto cuando, al rellenar y validar el formulario, éste nos venga de vuelta por algún fallo de validación.

nueva_pelicula.html
. . .
<form class="empty-form" action="{% url 'anadir_pelicula' %}" method="post">
{% csrf_token %}
. . .
{{ form.titulo | add_class:'form-control' | attr:'size:20'}}
{{ form.titulo.errors }}
. . .
</form>
. . .

También es posible, utilizando el tag render_field, renderizar los elementos del formulario de otra manera más cercana al estilo HTML

. . .
{% load widget_tweaks %}
. . .
{% render_field form.titulo class="form-control" placeholder=form.titulo.label size="40" %}
{{ form.titulo.errors }}
. . .

A continuación creamos la view y url que permitan recoger los datos del formulario y registrar la pelicula en la Base de Datos:

views.py
. . .
from django.shortcuts import render, redirect
from django.contrib import messages
from .forms import PeliculaForm
. . .
def anadir_pelicula(request):
    if request.method == 'POST':
        form = PeliculaForm(request.POST, request.FILES)
        if form.is_valid():
            pelicula = Pelicula()
            pelicula.titulo = form.cleaned_data['titulo']
            pelicula.director = form.cleaned_data['director']
            pelicula.genero = form.cleaned_data['genero']
            pelicula.save()
        else:
            return render(request, 'mispeliculas/nueva_pelicula.html', {'form':form})
 
        return redirect('nueva_pelicula')
 
. . .
urls.py
. . .
    url(r'^anadir_pelicula/$', views.anadir_pelicula, name='anadir_pelicula'),
. . .

Envio de mensajes

Django proporciona un framework de paso de mensajes por el que podemos enviar mensajes de texto después de procesar un formulario, por ejemplo. Resultaría útil, en este caso, para el ejemplo anterior donde volvemos a la plantilla de nueva_pelicula cuando los datos se han introducido en la Base de Datos correctamente. Quizás podría interesarnos colocar allí algún mensaje de confirmación o uno de error en alguna otra situación.

Veremos ahora un ejemplo sobre cómo dejar mensajes en una view y que puedan ser procesador luego por una plantilla.

Para este caso, vamos a dejar un mensaje justo antes de hacer la llamada a la función redirect del ejemplo anterior.

. . .
from django.contrib import messages
. . .
        messages.success(request, 'Pelicula registrada correctamente')
        return redirect('nueva_pelicula')
. . .

Y ahora, en la plantilla que carga la view nueva_pelicula (nueva.pelicula.html) podremos leer ese mensaje para utilizarlo siempre y cuando no haya ningún problema al procesar el formulario:

. . .
<form . . . .>
</form>
<br/>
{% if messages %}
    {% for message in messages %}
        <div class="alert alert-success" role="alert">
            {{ message }}
        </div>
    {% endfor %}
{% endif %}
. . .
Figure 4: Mensaje confirmación

Subir imágenes en un formulario

En el caso de que queramos subir imágenes en un formulario, tendremos que añadir algo de código adicional a lo que ya hemos comentado. A parte de definir el formulario para que soporte la subida de ficheros binarios, a la hora de definir el modelo, los campos que contenga imágenes tendrán que definirse de la siguiente forma:

. . .
<form . . . enctype="multipart/form-data">
. . .
</form>
. . .

El campo imagen en el modelo quedaría como sigue (hay que tener que en cuenta que tendremos que instalar el paquete Pillow si no lo tenemos ya instalado. Lo podemos hacer desde la ventana donde instalamos el paquete para Django en su día)

models.py
. . .
    imagen = models.ImageField(upload_to='img', default='')
. . .

Hará falta también modificar la clase del formulario para añadir el nuevo campo imagen, de forma que quedaría de esta forma:

forms.py
from django import forms
from .models import Pelicula
. . .
class PeliculaForm(forms.ModelForm):
    class Meta:
        fields = ('titulo', 'director', 'genero', 'imagen')
        model = Pelicula
. . .

Y, por supuesto, hay que asignar el campo imagen del formulario al atributo imagen del objeto a la hora de crearlo y almacenarlo en la Base de Datos. Eso lo haremos, como hacemos con cualquier formulario, en el fichero views.py en la view que corresponda:

views.py
. . .
def anadir_pelicula(request):
    . . .
    pelicula.imagen = form.cleaned_data['imagen']
    . . .
. . .

La carpeta img tendrá que existir dentro de la carpeta destinada al contenido estático del proyecto y será donde las imágenes se suban cuando se procese el formulario. Y en el campo imagen (en este caso) quedará almacenada la ruta relativa. Asi, si queremos cargar esa imagen más adelante podemos hacerlo de la siguiente manera:

. . .
    <img src="{% static 'peliculas/' %}{{ pelicula.imagen.url }}"/>
. . .

Por último, tendremos que definir el valor de la variable MEDIA_ROOT en el fichero settings.py de nuestro proyecto indicando la ruta (desde la aplicación web) donde se encuentra la carpeta img donde se guardan las imágenes.

settings.py
. . .
MEDIA_ROOT = 'peliculas/static/peliculas'
. . .

Llamar a views de Django utilizando jQuery

Supongamos que tenemos un listado de peliculas como el que hemos implementando un poco más arriba y queremos añadir un enlace con forma de botón que permite eliminar dichas peliculas de la Base de Datos. Y además queremos realizar dicha acción de forma asíncrona utilizando Ajax, puesto que asi eliminamos la necesidad de recargar la página al volver de ejecutar la vista Django en el servidor.

Añadiremos un enlace con forma de botón (con el símbolo X) para eliminar cada película, indicando el id de dicha pelicula como atributo de la etiqueta del enlace (para luego recogerlo desde Javascript):

peliculas.html
<h1>Mis películas</h1> <a href="#">+</a>
{% if lista_peliculas %}
   <ul>
       {% for pelicula in lista_peliculas %}
           <li>{{ pelicula.titulo }}<a id="{{ pelicula.id }}" href="#" class="btn btn-outline-danger btn-sm">X</a></li>
       {% endfor %}
    </ul>
{% else %}
   <p>No hay películas disponibles</p>
{% endif %}

Y ahora, en el mismo fichero, añadimos el código jQuery que permitirán capturar la acción click sobre el enlace con forma de botón (identificándolos a través de su etiqueta y estilo a.btn):

peliculas.html
. . .
<script type="text/javascript">
    $(document).ready(function() {
        $("a.btn").click(function() {
            var element = $(this);
            $.ajax({
               url: "/mispeliculas/eliminar_pelicula/" + element.attr("id"),
               dataType: 'json',
               success: function() {
                   element.parent().remove();
               }
           });
        });
    });
</script>
. . .

Faltaría crear la vista Django que se encargue de realizar la operación de eliminar la pelicula, que en este caso variará ligeramente de las que hemos ido haciendo hasta ahora. En este caso tiene que devolver un resultado en forma de JSON (en este caso vacío) tras realizar la operación de borrado:

views.py
. . .
import json
. . .
def eliminar_pelicula(request, pelicula_id):
    pelicula = Pelicula.objects.get(pk=pelicula_id)
    pelicula.delete()
    return HttpResponse(json.dumps({}), content_type='application/json')

Y su correspondiente URL:

urls.py
. . .
    url(r'^eliminar_pelicula/(?P<pelicula_id>[0-9]+)/$', views.eliminar_pelicula, name="eliminar_pelicula"),
. . .

Implementar una caja de texto con autocompletado con Django y jQuery

Definimos una caja de texto a la que queremos añadir soporte para autocompletado a medida que el usuario escriba:

unformulario.html
. . .
<input type="text" name="serie" id="serie" class="form-control" placeholder="Serie" size="20">
. . .

Con jQuery (y la librería jQquery UI) añadimos soporte para el autocompletado a dicha caja de texto indicando la URL que se debe de ejecutar y, en este caso (no es obligatorio), incluso indicamos como renderizar la información recibida desde el servidor:

unformulario.html
. . .
$('#director').autocomplete({
    source: "{% url 'autocompleta_director' %}",
    minLength: 2,
}).autocomplete("instance")._renderItem = function( ul, item ) {
    return $("<li>")
           .append("<div>" + item.nombre + "<br>" + item.apellidos + "</div>")
           .appendTo(ul);
};
. . .
Figure 5: Caja de texto con autocompletado

En la vista Django, recibimos el texto del elemento input por el protocolo GET y realizamos una búsqueda en la Base de Datos. Con los resultados formamos un documento JSON y lo mandamos de vuelta para que sea renderizado como una lista de autocompletado en el formulario inicial. Hay que tener en cuenta que si no se implementa con jQuery la opción de personalizar el HTML de renderizado de la lista de autocompletado, aparecerá directamente aquel dato que quede almacenado con la clave label en el documento JSON que Django devuelve.

views.py
. . .
def autocompleta_director(request):
    datos = request.GET
    contenido = datos.get('term')
    if contenido:
        directores = Director.objects.filter(nombre__contains=contenido)
    else:
        directores = Director.objects.all()
 
    resultados = []
    for director in directores:
        item_json = {}
        # Valor por defecto si no se implementa la opción de renderizado en jQuery
        item_json['label'] = director.nombre
        item_json['nombre'] = director.nombre
        resultados.append(item_json)
 
    datos = json.dumps(resultados)
    mimetype = 'application/json'
    return HttpResponse(datos, mimetype)
. . .

Y su correspondiente url para poder invocarlo:

urls.py
. . .
    url(r'^autocompleta_director/$', views.autocompleta_director, name='autocompleta_director'),
. . .

Paginación de resultados

Para la paginación de resultados en Django existe un objeto llamado Paginator que permite repartir los resultados obtenidos directamente de la Base de Datos indicando la página que se quiere mostrar. Será este objeto quién se encargue de calcular qué filas toca representar en cada una de las páginas.

Lo primero que tendremos que hacer, en la configuración de nuestro proyecto, es fijar el número de resultados por página estableciendo una constante con el valor que nos interese (en este caso se muestra 5 resultados por página):

settings.py
. . .
PELICULAS_POR_PAGINA = 5
. . .

Ahora, siempre que queramos paginar, en la vista que extraiga los resultados de la Base de Datos, tendremos que hacer uso del objeto Paginator para que los resultados se repartan en páginas y sólo obtengamos y carguemos en la plantilla los que correspondan en función de la página donde estemos:

views.py
. . .
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from Peliculas.settings import PELICULAS_POR_PAGINA
. . .
def peliculas(request):
    todas_las_peliculas = Pelicula.objects.order_by('titulo')
    pagina = request.GET.get('pagina', 1)
 
    paginator = Paginator(todas_las_peliculas, PELICULAS_POR_PAGINA)
    try:
        peliculas = paginator.page(pagina)
    except PageNotAnInteger:
        peliculas = paginator.page(1)
    except EmptyPage:
        peliculas = paginator.page(paginator.num_pages)
 
    context = {'peliculas': peliculas}
    return render(request, 'peliculas/peliculas.html', context)
. . .

A continuación, se muestra como quedaría la template para la view anterior, donde muestran botones para acceder a la página anterior y la siguiente (en caso de que haya) y la página actual sobre el total.

peliculas.html
. . .
<ul>
{% for pelicula in peliculas %}
    <li>{{ pelicula.titulo }}</li>
{% endfor %}
</ul>
 
<nav aria-label="Page navigation example">
  <ul class="pagination">
    {% if peliculas.has_previous %}
    <li class="page-item"><a class="page-link" href="?pagina={{ peliculas.previous_page_number }}">Anterior</a></li>
    {% endif %}
    {% if peliculas.has_next %}
    <li class="page-item"><a class="page-link" href="?pagina={{ peliculas.next_page_number }}">Siguiente</a></li>
    {% endif %}
  </ul>
</nav>
<p>Mostrando {{ peliculas.number }} de {{ peliculas.paginator.num_pages }}</p>
. . .

Sólo faltaría añadir la url en urls.py para esta view y su template.

Se puede encontrar más información sobre cómo paginar resultados con Django en la página que dedican en el manual de este framework sobre Paginación en Django

Ejercicios

  1. Pagina los resultados de una lista con botones para ir las páginas anterior y siguiente, y para ir a cada una de ellas (1, 2, 3, . . .). Puedes utilizar alguno de los diseños que Bootstrap ofrece, como el que se acompaña a este ejercicio:


Autenticación de usuarios

Supongamos ahora que queremos añadir un sistema de gestión de usuarios a nuestra aplicación web. Necesitamos una tabla donde almacenar los usuarios y sus datos y por otro lado, de alguna manera, tendremos que permitir que éstos se registren, inicien sesión y otra serie de operaciones que podamos necesitar.

Puesto que Django ya incorpora un sistema de gestión de usuarios con nuestra aplicación web, podemos aprovecharnos del mismo y podemos registrar los usuarios directamente haciendo uso del mismo. Podemos crear un formulario para permitir el registro de usuarios y también podemos registrarlos directamente desde la consola interactiva directamente. Lo haremos ahora de esta última forma para centrarnos en como gestionar el inicio de sesión. Más adelante podríamos crear otro formulario que permitiera el registro de nuevos usuarios ya que el código Python sería el mismo pero asociado a una view que recibiera el formulario de registro con los datos del usuario. Además, en la documentación de la API para la clase User podemos consultar todos los datos con los que podemos contar a la hora de utilizar el sistema de usuarios incorporado por Django.

Para este primer caso, creamos desde la consola interactiva un usuario e indicamos su nombre de usuario, e-mail y contraseña.

santi@zenbook:$ python3 manager.py shell
>>> from django.contrib.auth.models import User
>>> user = User.objects.create_user('santi', 'santi@codeandcoke.com', 'mipassword')
>>> user.save()

Inicio de sesión

A continuación, crearíamos un formulario para realizar el inicio de sesión y añadiríamos la correspondiente view y url como siempre para hacerlo funcionar. En este caso, además habría que preparar la view login_view que es donde se procesará dicho formulario para llevar a cabo la autenticación del usuario, que es en lo que nos centraremos en este apartado.

login.html
. . .
<form class="form-signin" action="{% url 'login_view' %}" method="post">
{% csrf_token %}
  <h2 class="form-signin-heading">Iniciar Sesión</h2>
  <label for="username" class="sr-only">Usuario</label>
  <input type="text" id="username" name="username" class="form-control" placeholder="Usuario" required autofocus>
  <label for="password" class="sr-only">Contraseña</label>
  <input type="password" id="password" name="password" class="form-control" placeholder="Contraseña" required>
  <div class="checkbox">
    <label>
      <input type="checkbox" value="remember-me"> Recordar
    </label>
  </div>
  <button class="btn btn-lg btn-primary btn-block" type="submit">Entrar</button>
</form>
{% if message %}
<div class="alert alert-danger" role="alert">
  {{ message }}
</div>
{% endif %}
Figure 6: Formulario de inicio de sesión

Así, suponiendo que se han creado las views y urls para el formulario, pasamos a ver como quedaría la view para la autenticación del usuario login_view:

views.py
. . .
from django.contrib.auth import authenticate, login, logout
. . .
def login_view(request):
username = request.POST['username']
    password = request.POST['password']
    user = authenticate(request, username=username, password=password)
    if user is not None:
        login(request, user)
        return redirect('index')
 
    context = {'message': 'Invalid Username/Password'}
    return render(request, 'peliculas/signin.html', context)
. . .

En la vista anterior, se recogen los datos del formulario y se comprueba si el usuario existe (función authenticate). Si el usuario es válido (is not None) se procede a iniciar la sesión (función login). En caso contrario volvemos a mandar al usuario al formulario de inicio de sesión con un mensaje de error.

Mantener la sesión del usuario

Una vez iniciada la sesión ya hemos visto que enviamos al usuario a la portada de la web. También podríamos haberlo enviado a la portada del panel de administración o cualquier otra acción que nos interese. Lo importante es saber cómo podemos saber que un usuario ha iniciado sesión y que ésta sigue activa (no ha salido de la misma). En este caso, suponemos que sólo queremos permitir el acceso a la portada de nuestra web a usuarios autenticados:

views.py
. . .
def index(request):
    if request.user.is_authenticated:
        return render(request, 'peliculas/index.html')
    else:
        return redirect('signin')
. . .

Hemos añadido en la view de la página de inicio un control para comprobar si el usuario está autenticado y asi permitirle visitar la página. En caso contrario lo enviamos directamente al formulario de inicio de sesión.

Además, tenemos que tener en cuenta que podemos acceder a los datos del usuario desde las plantillas de nuestra aplicación web accediendo directamente al objeto request.

. . .
<p>Bienvenido {{ request.user.username }}</p>
. . .

Finalizar la sesión del usuario

Cuando queramos finalizar la sesión del usuario, simplemente tendremos que crear una view con el siguiente código:

views.py
. . .
from django.contrib.auth import authenticate, login, logout
. . .
def logout_view(request):
    logout(request)
    return HttpResponseRedirect(reverse('index'))
. . .
Se puede encontrar más información sobre la gestión de la autenticación con Django en la página que dedican en el manual de este framework sobre El sistema de autenticación en Django

Ejercicios

  1. Añade a la aplicación web un formulario para registrar nuevos usuarios
  2. Añade a la aplicación web una zona donde cada usuario pueda modificar los datos de su perfil

Informes

Para la creación de informes con Django utilizaremos la librería reportlab que permite generar ficheros PDF con lenguaje Python.

El primer paso será instalar el paquete reportlab con la herramienta de instalación de paquetes de Python o bien desde el IDE PyCharm directamente.

santi@zenbook:$ pip3 install reportlab

A continuación se muestran algunos ejemplos de uso de esta librería para la generación de informes. Conviene tener en cuenta que en éstos, se muestra solamente el código corresponde a la función que permite crear la view que lanzará el informe al usuario. Faltaría registrar la correspondiente url en el fichero urls.py de nuestra aplicación web y también añadir un enlace o botón en la plantilla donde queramos que el usuario tenga que pinchar para visualizar este informe.

En este primer ejemplo se creará un informe (en PDF) donde se mostrará un título y a continuación un listado con los títulos de todas las películas de la Base de Datos. En este caso utilizaremos el objeto Canvas que nos permite ubicar los elementos del informe utilizando las coordenadas x e y para situarlos. Hay que tener en cuenta que la esquina inferior izquierda se corresponde con los valores x=0 e y=0.

views.py
. . .
def informe_peliculas(request):
    response = HttpResponse(content_type='application/pdf')
    response['Content-Disposition'] = 'attachment; filename="peliculas.pdf"'
 
    buffer = BytesIO()
 
    can = canvas.Canvas(buffer)
    can.drawString(200, 800, "Peliculas")
 
    peliculas = Pelicula.objects.all()
    y = 700
    for pelicula in peliculas:
        can.drawString(50, y, pelicula.titulo)
        y -= 20
 
    can.showPage()
    can.save()
 
    pdf = buffer.getvalue()
    buffer.close()
    response.write(pdf)
 
    return response
. . .

En este segundo ejemplo, utilizando en este caso el objeto SimpleDocTemplate, que permite añadir contenido al documento del informe si necesidad de indicar las coordenadas explicitamente. En este caso se genera una tabla (con borde) con el listado de las películas y cierta información sobre las mismas.

views.py
. . .
def informe_peliculas(request):
    response = HttpResponse(content_type='application/pdf')
    response['Content-Disposition'] = 'attachment; filename="peliculas.pdf"'
 
    buffer = BytesIO()
 
    documento = SimpleDocTemplate(buffer, pagesize=A4, rigthMargin=40, leftMargin=40, topMargin=60, bootomMargin=18)
    contenido = []
    estilos = getSampleStyleSheet()
    cabecera = Paragraph("Listado de Películas", estilos['Heading1'])
    contenido.append(cabecera)
 
    cabecera_tabla = ('Título', 'Género', 'Director')
    datos = [(pelicula.titulo, pelicula.genero, pelicula.director) for pelicula in Pelicula.objects.all()]
    tabla = Table([cabecera_tabla] + datos)
    tabla.setStyle(TableStyle([('GRID', (0, 0), (-1, -1), 1, colors.black),]))
 
    contenido.append(tabla)
    documento.build(contenido)
    response.write(buffer.getvalue())
    buffer.close()
    return response
. . .

Podéis encontrar más información en esta Guía de reportlab

Internacionalización

Desde el directorio raíz de la aplicación (no del proyecto), primero creamos la carpeta donde se almacenarán las traducciones para cada uno de los idiomas para los que queramos dar soporte

santi@zenbook:Projects/peliculas/mispeliculas$ mkdir locale

Si el texto va en una template HTML:

{% load i18n %}
. . .
<p>{% trans "Añadir pelicula" %}</p>
. . .

Si estamos utilizando Python como lenguaje (en una vista, por ejemplo):

from django.utils.translation import ugettext as _
. . .
mensaje = _("Añadir pelicula")
. . .

Cuando queramos crear la estructura para el soporte de un idioma determinado (dentro de la carpeta de la aplicación):

santi@zenbook:$ django-admin makemessages -l es
processing locale es
santi@zenbook:$ django-admin makemessages -l en
processing locale en

. . .
msgid "Añadir pelicula"
msgstr "Add movie"
. . .

Y si queremos reexaminar todos los ficheros de idiomas que se hayan creado porque se han realizado cambios o se han añadido nuevos mensajes (dentro de la carpeta de la aplicación):

santi@zenbook:$ django-admin makemessages -a

Además, es posible convertir estos ficheros de texto .po en ficheros binarios .mo más optimizados para usar con gettext:

santi@zenbook:$ django-admin compilemessages

Despliegue de aplicaciones

Utilizando WSGI con Apache

En este apartado veremos como desplegar una aplicación web Django con Apache usando el módulo WSGI. Para ello tendremos que realizar una serie de ajustes, instalación y configuraciones:

  1. Antes de comenzar, tendremos que ajustar algunos parámetros de configuración de nuestro proyecto
  2. Más adelante instalaremos las aplicaciones y módulos necesarios para su despliegue con Apache y crearemos el host virtual
  3. Para terminar tendremos que ajustar algunos permisos para que Apache pueda acceder al contenido de la aplicación web sin ningún problema.

Ajustar parámetros en el proyecto Django

Antes de comenzar, tenemos que comprobar que tenemos instalado el intérprete de Python y todos los paquetes necesarios en la máquina servidor. En nuestro caso al menos los paquetes django (en nuestro caso la versión 1.11.8), django-widget-tweaks y Pillow a través del comando pip3 install

santi@zenbook:$ sudo apt-get install python3
. . .
santi@zenbook:$ sudo pip3 install django==1.11.8
. . .
santi@zenbook:$ sudo pip3 install django-widget-tweaks
. . .
santi@zenbook:$ sudo pip3 install Pillow
. . .

Lo primero que haremos será reunir todos los ficheros estáticos de la aplicación con ayuda del siguiente comando, lo que generará una carpeta static en el directorio raíz de nuestro proyecto con todo el contenido estático del mismo.

santi@zenbook:$ python3 manage.py collectstatic
. . .
. . .
543 static files copied to '/home/santi/mispeliculas.com/static'

Ahora, en el fichero settings.py de nuestro proyecto, fijaremos la nueva ruta donde se ha almacenado el contenido estático y también incluiremos la ruta raíz para el contenido que se suba con los modelos (imágenes asociadas, por ejemplo, a través de un formulario).

Si además queremos que la aplicación web pueda ser accedida remotamente tendremos que definir cuáles son los hosts permitidos.

Y por último, si suponemos que la aplicación web se encuentra en un servidor de producción, podríamos desactivar el modo DEBUG

settings.py
. . .
# Directorio con el contenido estático
STATIC_ROOT = os.path.join(BASE_DIR, 'static/')
# Directorio a partir del cual se almacenarán los ficheros que se suban con algún modelo (en ''items'' en este caso)
MEDIA_ROOT = '/home/santi/mispeliculas.com/static/mispeliculas'
# Define los hosts que tienen permitido acceder a la aplicación web
ALLOWED_HOSTS = ['mispeliculas.com']
# Desactiva el modo DEBUG
DEBUG = False
. . .

En el fichero /home/santi/mispeliculas.com/peliculas/urls.py podemos modificar la url raíz para acceder directamente sobre el dominio de la aplicación web, dejando el fichero asi:

peliculas/urls.py
. . .
    url(r'^', include('mispeliculas.urls')
. . .

De esta manera, una vez desplegada correctamente la aplicación, podremos acceder directamente a través de la url http://mispeliculas.com en vez de http://mispeliculas.com/peliculas o algo similar como teníamos desde un principio.

Instalación y configuración de Apache

Suponiendo que ya tenemos instalado Apache, tendremos que instalar el módulo WSGI para Python 3 (en nuestro caso)

santi@zenbook:$ sudo apt-get install libapache2-mod-wsgi-py3

A continuación, activamos el módulo:

santi@zenbook:$ sudo a2enmod wsgi
Enabling module wsgi.
To activate the new configuration, you need to run:
  systemctl restart apache2

Finalmente tendremos que crear un host virtual con las directivas habituales para un sitio alojado por Apache, al que añadiremos algunas específicas por tratarse de una aplicación web hecha con Python

/etc/apache2/sites-available/mispeliculas.com.conf
<VirtualHost *:80>
    ServerName mispeliculas.com
    ServerAdmin santi@codeandcoke.com
 
    Alias /static /home/santi/mispeliculas.com/static
    <Directory /home/santi/mispeliculas.com/static>
        Require all granted
    </Directory>
 
    <Directory /home/santi/mispeliculas.com/peliculas>
        <Files wsgi.py>
            Require all granted
        </Files>
    </Directory>
 
    WSGIDaemonProcess peliculas python-path=/home/santi/mispeliculas.com
    WSGIProcessGroup peliculas
    WSGIScriptAlias / /home/santi/mispeliculas.com/peliculas/wsgi.py
 
    ErrorLog ${APACHE_LOG_DIR}/mispeliculas-error.log
    CustomLog ${APACHE_LOG_DIR}/mispeliculas-access.log combined
</VirtualHost>

Ahora ya podemos reiniciar Apache y comprobar que todo está funcionando correctamente

santi@zenbook:$ sudo service apache2 restart

Ajustes de permisos

Siempre dependerá de cómo se tenga configurado en cada caso, pero conviene echar un vistazo a los permisos asignados al fichero y carpeta donde se encuentra la Base de Datos (en este caso estamos utilizando SQLite), y también a la carpeta donde se encuentra el proyecto.

santi@zenbook:$ chown :www-data /home/santi/mispeliculas.com/db.sqlite3
santi@zenbook:$ chmod 664 /home/santi/mispeliculas.com/db.sqlite3
santi@zenbook:$ chown :www-data /home/santi/mispeliculas.com
santi@zenbook:$ chmod g+w /home/santi/mispeliculas.com

También es posible que necesitemos asignar permisos de escritura en la carpeta donde se sube el contenido a través de los formularios (donde indica la variable MEDIA_ROOT)

santi@zenbook:$ chown -R :www-data /home/santi/mispeliculas.com/static
santi@zenbook:$ chmod -R g+w /home/santi/mispeliculas.com/static

Y ahora podrás visitar tu aplicación web Django accediendo directamente a http://mispeliculas.com

Desplegar la aplicación utilizando entornos virtuales

santi@zenbook:$ pip3 install virtualenv
santi@zenbook:$ virtualenv env
santi@zenbook:$ source env/bin/activate
. . .
santi@zenbook:$ pip install django
santi@zenbook:$ pip install Pillow
. . .
santi@zenbook:$ deactivate
/etc/apache2/sites-available/mispeliculas.com.conf
. . .
    WSGIDaemonProcess peliculas python-path=/home/santi/mispeliculas.com:/home/santi/mispeliculas.com/env/lib/python3.5/site-packages
. . .

Ejercicios

  1. Tomando como referencia el ejemplo del Blog de Bootstrap, preparar una aplicación web con Django que tenga las siguientes funcionalidades:
    • En la portada se mostraran los posts que haya escritos y de cada uno de ellos se mostrará la siguiente información:
      • Titulo del post
      • Nombre del autor
      • Fecha
      • Texto
    • En otra zona de la página (por ahora pública) hay que crear un formulario que permita escribir y registrar nuevos posts con toda esa información



  2. Sobre el ejercicio anterior del blog, añadir las siguientes secciones o funcionalidades:
    • Crear una zona privada protegida por usuario/contraseña desde donde se creen nuevas entradas en el blog
    • Listar (en forma de tabla) en esa zona privada las entradas de forma que puedan ser eliminadas. Como diseño se puede tomar cualquier de los que aparecen en la documentación de Bootstrap en Content-Tables, añadiéndole algún botón para que los posts puedan ser directamente eliminados desde la tabla
    • Paginar los resultados de la portada fijando un número máximo de post visualizados por página

  3. Realizar una página web que liste en forma de tabla los equipos de fútbol de primera división, mostrando para cada uno de ellos el nombre, ciudad de origen, nombre y apellidos del entrenador y el número de jugadores
    • El sitio web contará con una portada en la que habrá un botón para acceder a la página donde se mostrará el listado de equipos
    • Paginar los resultados en bloques de 5 equipos

Proyectos de Ejemplo

  • Peliculas Proyecto de gestión de peliculas con Django

Prácticas


© 2018 Santiago Faci

apuntes/aplicaciones_web.txt · Last modified: 2019/06/12 16:08 by Santiago Faci