Tutorial de Python Django

Foto por nyuhuhuu. Algunos derechos reservados.

En este tutorial de Python Django, vamos a explicar cómo instalar Django y PostgreSQL. También explicaremos cómo crear un proyecto y una aplicación web de lista de tareas utilizando las utilidades de línea de comandos que Django nos ofrece. Para finalizar, vamos a explicar paso a paso cómo programar la aplicación web de lista de tareas.

Este tutorial aplica para la versión 1.8 de Django y 9.4 de PostgreSQL.

Instalando Django y PostgreSQL

Para comenzar, necesitaremos instalar la librería Django. Podemos instalarla directamente usando pip:

$ pip install Django

O, lo que es más recomendable, a través del archivo requirements.txt.

También es recomendable usar virtualenvs cuando instalemos librerías para nuestros proyectos. De esta forma, seremos más organizados y nos evitaremos problemas innecesarios.

Para aprovechar todo el potencial que Django nos ofrece, se hace necesaria una base de datos. Django actualmente ofrece soporte para los motores PostgreSQL, MySQL, Oracle y SQLite. En este tutorial vamos a usar PostgreSQL. Para instalarlo ejecutamos los siguientes comandos:

# Archlinux
$ sudo pacman -S postgresql
$ sudo -i -u postgres
$ initdb --locale en_US.UTF-8 -E UTF8 -D '/var/lib/postgres/data'

# Fedora
$ sudo dnf install postgresql-server postgresql-contrib
$ sudo postgresql-setup initdb

# Debian, Ubuntu
$ sudo apt-get install postgresql postgresql-contrib postgresql-client

# OS X (con Homebrew)
$ brew install postgresql
$ initdb /usr/local/var/postgres

Ahora instalamos algunos paquetes necesarios para compilar el driver psycopg2 que Django necesita para comunicarse con una base de datos PostgreSQL:

# Archlinux
$ sudo pacman -S postgresql-libs make automake gcc

# Fedora
$ sudo dnf install postgresql-devel make automake gcc gcc-c++

# Debian, Ubuntu
$ sudo apt-get install build-essential postgresql-server-dev-all

# OS X
$ xcode-select --install

Para finalizar instalamos el driver psycopg2:

$ pip install psycopg2

Creando un nuevo proyecto

El código fuente explicado en este artículo puede ser descargado desde Github o Bitbucket.

Ahora que tenemos Django instalado, podemos crear un nuevo proyecto:

$ django-admin startproject todo_django

De esta forma, Django creará automáticamente un directorio con la siguiente estructura:

todo_django/
  manage.py
  todo_django/
    __init__.py
    settings.py
    urls.py
    wsgi.py

El archivo manage.py sirve para interactuar con los comandos administrativos de nuestro proyecto, como por ejemplo python manage.py migrate, el cual sirve para generar y modificar las tablas de la base de datos.

En el directorio interno todo_django/ encontramos el archivo settings.py que contiene algunas variables de configuración del framework, como por ejemplo, información sobre las bases de datos o ubicación de los archivos estáticos de nuestro sitio o aplicación web.

En el archivo urls.py se definen las rutas o URLs de nuestro sitio web. En la segunda parte de este tutorial, explicaremos con mayor detalle el contenido de este archivo. Por último, el archivo wsgi.py actúa como punto de entrada para servir el proyecto cuando sea desplegado al servidor de producción.

Configurando el proyecto

Luego de haber instalado las librerías necesarias y después de haber creado el nuevo proyecto, debemos configurarlo de acuerdo con nuestras necesidades. Por ahora vamos a configurar la base de datos. Para esto, abrimos el archivo settings.py y modificamos la variable DATABASES:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'todo_django_db',
        'USER': 'todo_django_user',
        'PASSWORD': 'password'
    }
}

La configuración anterior le indica a Django que nuestra base de datos es de tipo PostgreSQL, con el nombre todo_django_db y el usuario todo_django_user.

Django es un framework altamente configurable. Desafortunadamente, no es posible explicar todas y cada una de las configuraciones en este tutorial. Pero para el lector interesado, existe una página de referencia en inglés que explica todas las configuraciones disponibles.

Ahora debemos crear la base de datos y asignarle el usuario y la contraseña definidos en el archivo de configuración:

Para algunos sistemas Linux es necesario cambiar al usuario postgres antes de ejecutar los comandos que se describen a continuación.

$ createuser -P -d todo_django_user
# Un prompt aparecerá para digitar la contraseña

$ createdb todo_django_db -U todo_django_user

Por último, ejecutamos el siguiente comando para crear las tablas, que Django define por defecto, en la base de datos:

$ python manage.py migrate

Probando la instalación de Django y PostgreSQL

Para probar que Django ha sido instalado correctamente, ejecutamos el siguiente comando en el directorio del proyecto:

$ python manage.py runserver

Este comando ejecuta un servidor HTTP de prueba que viene por defecto con Django. No es recomendable utilizarlo para servir una aplicación web en un servidor de producción debido a que no está optimizado para grandes cantidades de tráfico ni posee el nivel de seguridad apropiado.

Abrimos nuestro navegador web e ingresamos la dirección http://localhost:8000. Si todo va bien, veremos algo como esto:

Python Django funciona!

¡Felicitaciones! Nuestra instalación de Django y PostgreSQL funciona a la perfección. Ahora estamos listos para implementar nuestra pequeña aplicación de lista de tareas.

Creación de la aplicación

Hasta el momento hemos instalado algunas librerías y herramientas y configurado la base de datos. Como se mencionó al principio del tutorial, vamos a desarrollar una aplicación de lista de tareas que le permita al usuario agregar y marcar como completada una o más tareas.

Para crear la aplicación, ejecutamos el siguiente comando dentro del directorio del proyecto:

$ python manage.py startapp todo

El comando automáticamente crea un directorio todo, dentro del directorio del proyecto, con la siguiente estructura:

todo_django/
  todo/
    __init__.py
    admin.py
    models.py
    tests.py
    views.py
    migrations/
      __init__.py

Definición del modelo de datos

En el modelo de datos se define la información que es indispensable para el funcionamiento del sitio o aplicación web. En Django, el modelo de datos se compone de un conjunto de clases y cada una representa una tabla en la base de datos. Para nuestro proyecto de lista de tareas, vamos a almacenar la descripción de la tarea y un valor para saber si ya fue completada o no.

Para definir el modelo de datos, implementamos la clase Todo en el archivo models.py:

from django.db import models

class Todo(models.Model):
    description = models.CharField(max_length=128)
    is_done = models.BooleanField(default=False)

La clase Todo se compone de una descripción y un valor que nos indica si la tarea fue marcada como completada o no. El atributo description es una cadena de caracteres y tiene una longitud máxima de 128 caracteres. El atributo is_done es un valor booleano, es decir que solamente puede ser verdadero o falso y su valor por defecto es falso.

Ahora debemos agregar nuestra aplicación todo al archivo de configuración settings.py, dentro de la sección INSTALLED_APPS:

INSTALLED_APPS = (
    'todo'
)

Para crear la tabla en la base de datos, ejecutamos los siguientes comandos:

$ python manage.py makemigrations todo
$ python manage.py migrate

El comando makemigrations crea un archivo .py dentro del directorio migrations de la aplicación todo. Este archivo contiene código Python generado automáticamente que define las operaciones que Django debe hacer en la base de datos. El comando migrate le indica a Django que debe aplicar en la base de datos, las operaciones del archivo que se acabó de generar.

Definición de las vistas

Dentro de la terminología que usa Django, las vistas son un conjunto de funcionalidades que permiten el procesamiento de la información contenida en el modelo de datos para presentarle al usuario un resultado específico. Por ejemplo, una vista puede consultar todas las tareas en la base de datos para presentarle al usuario un listado al acceder a una URL específica.

Las vistas pueden ser definidas como funciones o como clases. En este tutorial vamos a definir las vistas usando clases. La primera vista que vamos a definir es el listado de tareas, implementando el siguiente fragmento de código en el archivo views.py:

from django.views.generic import View
from django.template.response import TemplateResponse

from todo.models import Todo

class TodoListView(View):
    def get(self, request,_args, _*kwargs):
        context = {
            'todo_list': Todo.objects.all()
        }
        return TemplateResponse(request, 'todo_list.html', context)

El método get() de la vista corresponde a la petición GET de HTTP. Este método devuelve una respuesta al navegador con los datos de la petición HTTP almacenados en la variable request y con el contenido de la plantilla todo_list.html. En el diccionario context incluimos todos los datos que queremos mostrar en la plantilla. En este caso incluimos el listado de todas las tareas almacenadas en la base de datos, obtenido mediante la expresión Todo.objects.all().

Ahora vamos a implementar la vista que permite crear una nueva tarea:

from django.http import HttpResponseRedirect

class TodoAddView(View):
    def get(self, request,_args, _*kwargs):
        return TemplateResponse(request, 'todo_add.html', {})

    def post(self, request,_args, _*kwargs):
        description = request.POST['description']
        Todo.objects.create(description=description)
        return HttpResponseRedirect('/')

La implementación de esta vista permite que pueda recibir tanto peticiones GET como peticiones POST. Cuando la petición es GET, la vista le presenta al usuario el formulario para crear una nueva tarea. Cuando la petición es POST, la vista almacena los valores de los controles del formulario HTML en el diccionario de datos request.POST para que puedan ser usados más adelante.

Al invocar la expresión Todo.objects.create() estamos creando una nueva tarea en la base de datos con la descripción obtenida del formulario mediante la expresión request.POST['description']. Los parámetros que recibe la función create() son los mismos que definimos en el modelo de datos para la clase Todo.

Por último, implementamos la vista que le permitirá al usuario marcar una tarea como completada:

class TodoDoneView(View):
    def get(self, request,_args, _*kwargs):
        todo = Todo.objects.get(id=kwargs['todo_id'])
        todo.is_done = True
        todo.save()
        return HttpResponseRedirect('/')

La expresión Todo.objects.get() consulta una tarea de la base de datos teniendo en cuenta su id o clave primaria. Enseguida actualizamos el valor del atributo is_done para así marcar la tarea como completada. El parámetro kwargs contiene todos los valores que puedan ser obtenidos de la URL. ¿Que valores pueden ser obtenidos? Todos los que se hayan definidos en archivo urls.py. De ello hablaremos en seguida.

Relacionando urls con vistas

Para que las vistas que hemos creado puedan ser de alguna utilidad, debemos relacionar una URL con cada vista, de tal manera que cuando un usuario ingrese una URL específica en su navegador web, pueda ver el listado de tareas o agregar una nueva tarea. Para nuestro proyecto, vamos a modificar el archivo urls.py para agregar las siguientes relaciones:

  • La URL / con la vista que enlista todas las tareas
  • La URL /add/ con la vista que crea una nueva tarea
  • La URL /done/<todo_id>/ con la vista que marca una tarea como completada
from django.conf.urls import url
from todo import views

urlpatterns = [
    url(r'^$', views.TodoListView.as_view(), name='todo-list'),
    url(r'^add/$', views.TodoAddView.as_view(), name='todo-add'),
    url(
        r'^done/(?P<task_id>\d+)/$',
        views.TodoDoneView.as_view(),
        name='todo-done'
    )
]

Para definir una URL utilizamos el método url() que acepta como parámetros:

  • La URL mediante una expresión regular
  • Un método que permite ejecutar la vista
  • Un nombre para poder hacer referencia a esta URL en otros archivos o plantillas

Cada clase de vista expone un método as_view() el cual sirve como punto de entrada para la ejecución de la vista.

Note como la URL definida para la vista TodoDoneView contiene la expresión regular (?P<todo_id>\d+). Estas expresiones permiten definir variables dinámicas en la URL. En este caso estamos indicándole a Django que la URL contiene un número que corresponde con el id de una tarea y que podemos obtener este valor a través de la variable kwargs['todo_id'] en el método get() de la vista.

Implementando las plantillas

Las plantillas de Django son archivos HTML que contienen algunas etiquetas especiales usadas generalmente para inyectar los datos provenientes de la ejecución de las vistas. Para nuestra aplicación de lista de tareas necesitamos implementar tres plantillas: base.html, todo_list.html y todo_add.html.

De acuerdo con la configuración por defecto, Django va a buscar las plantillas en el directorio templates/ dentro de cada aplicación. Es decir que nuestras plantillas deben ubicarse en el directorio:

todo_django/
  todo/
    templates/
      base.html
      todo_list.html
      todo_add.html

La plantilla base.html va a servir como modelo para implementar las demás plantillas. En esta plantilla incluiremos el código HTML que es común para todas las páginas de nuestra aplicación como son los meta-datos, el encabezado y el pie de página.

<!DOCTYPE html>
<html>
  <head>
    <title>{% block title %}Todo list{% endblock %}</title>
  </head>
  <body>
    <h1>{% block heading %}Todo list{% endblock %}</h1>
    {% block body %}{% endblock %}
    <p>Powered by Django</p>
  </body>
</html>

Las etiquetas {% block ... %}{% endblock %} sirven para indicarle a Django que el contenido encerrado en ellas, puede ser modificado por otra plantilla. El sistema de plantillas de Django utiliza un paradigma de herencia, haciendo posible la reutilización de código HTML que, de otra forma, tendría que ser copiado en cada página de la aplicación.

Para demostrar este concepto, implementemos la plantilla todo_list.html:

{% extends "base.html" %}

{% block body %}
  <a href="{% url 'todo-add' %}">Add todo</a>
  <ul>
    {% for todo in todo_list %}
      <li>
        {{ todo.description }}
        -
        {% if not todo.is_done %}
          <a href="{% url 'todo-done' todo.id %}">Mark as done</a>
        {% else %}
          <span>Done!</span>
        {% endif %}
      </li>
    {% endfor %}
  </ul>
{% endblock %}

La etiqueta {% extends ... %} se usa para indicarle a Django que la plantilla va a heredar o utilizar el código de una plantilla base. Como se puede apreciar, la plantilla todo_list.html redefine el contenido de las etiquetas {% body %} presentes inicialmente en la platilla base.html.

La etiqueta {% for ... %} es utilizada para iterar sobre una colección de objetos. Funciona de forma muy similar al for en Python. Aquí estamos iterando sobre cada uno de los elementos de la variable todo_list que, como recordarán, se incluyó en la variable context del método get() de la vista TodoListView.

Si la tarea aún no se ha marcado como completada, un enlace se encontrará disponible para marcarla. En caso contrario, el enlace es reemplazado por texto que indica que la tarea ya fue completada. Este tipo de escenarios condicionales se implementan usando la etiqueta {% if ... %}{% endif %} y funciona de la misma forma que una estructura if en Python.

La etiqueta {% url ... %} permite resolver automáticamente la URL de una vista teniendo en cuenta el nombre que le fue asignado en el archivo urls.py. En este caso se está refiriendo a la URL con el nombre todo-done que corresponde a la vista que marca una tarea como completada.

Observe como además del nombre, la etiqueta {% url %} también recibe el id o clave primaria de una tarea. Esto se debe a que la definición de la URL en el archivo urls.py exige que se proporcione un id.

Entonces, cuando el usuario cargue la URL /, el código HTML completo de la página que lista las tareas se verá algo como esto:

<!DOCTYPE html>
<html>
  <head>
    <title>Todo list</title>
  </head>
  <body>
    <h1>Todo list</h1>
    <ul>
      <li>
        Todo A
        <a href="/done/1/">Mark as complete</a>
      </li>
      <li>
        Todo B
        <a href="/done/2/">Mark as complete</a>
      </li>
      ...
    </ul>
    <p>Powered by Django</p>
  </body>
</html>

Lo que en definitiva es el resultado de unir el código presente en la plantilla base.html con el código presente en la plantilla todo_list.html.

Igualmente, implementamos el formulario para crear tareas en la plantilla todo_add.html:

{% extends "base.html" %}

{% block title %}
    Add todo
{% endblock %}

{% block heading %}
    Add todo
{% endblock %}

{% block body %}
  <form action="{% url 'todo-add' %}" method="post">
    {% csrf_token %}
    <label for="description">Description</label>
    <input type="text" id="description" name="description">
    <input type="submit" name="submit" value="Add todo">
  </form>
{% endblock %}

La etiqueta de Django {% csrf_token %} es una medida de seguridad para proteger a la aplicación web contra ataques de tipo Cross Site Request Forgery. La etiqueta agrega un input oculto cuyo contenido es un token o valor aleatorio generado por el servidor antes de mostrar el formulario al usuario. Si el formulario no envía de vuelta éste token al servidor, no es posible procesar el formulario.

Al crear el formulario debemos tener cuidado a la hora de configurar los nombres para cada uno de los controles input. El atributo name debe coincidir con los nombres que estamos usando para obtener los datos del diccionario request.POST en la vista TodoAddView.

Probando la aplicación

Para probar nuestra aplicación, ejecutamos el servidor de prueba usando una terminal:

$ python manage.py runserver

Abrimos el navegador web de nuestra preferencia e ingresamos la URL http://localhost:8000. Debemos ver algo como esto:

Python Django app

Si hacemos clic en el enlace Add todo, veremos el formulario para agregar una nueva tarea:

Python Django app

Para agregar una nueva tarea, llenamos el campo description y hacemos clic en el botón Add todo. Veremos que el listado ahora muestra la tarea que acabamos de agregar:

Python Django app

Para marcar la tarea como completada, hacemos clic en el enlace Mark as done. Como podemos ver, el enlace ha desaparecido:

Python Django app

Y es así como hemos desarrollado una aplicación web de lista de tareas bastante simple usando Python Django. Es una aplicación muy simple pero que demuestra con claridad los conceptos básicos de Django.

Recuerden que el código fuente explicado en este articulo se puede descargar desde Github o Bitbucket. Hasta la próxima!