Introducción a Django

Por Arrecio


Django es un framework de desarrollo de aplicaciones web utilizando el lenguaje de programación Python. En mi caso se ha convertido en la mejor opción para programar aplicaciones web porque se trata de un lenguaje que me es familiar por haberlo usado en todo tipo de situaciones aprovechando su gran versatilidad. Esta versatilidad tiene como ejemplo precisamente sus capacidades de creación de webs de cualquier tipo, y no sólo de las que se categorizan puramente como aplicaciones web aunque estas sean su fuerte.

Antes de continuar debes saber que en el momento de escribir este artículo estoy usando Python 3.13 con Django, a pesar de que existen versiones posteriores. El motivo es que a esta fecha es la última versión que soporta.

Una aplicación web utilizará el cliente básicamente para renderizar lo computado en el lado del servidor, sin perjuicio de que las webs que desarrollemos incluyan código javascript o sucedáneo que realice algunos cómputos del lado del cliente.

Alternativas a Django usando Python está por ejemplo Flask. Frameworks que usando JavaScript, TypeScript o similares son React, Vue, Angular, AngularJS, a los que siguen una buena lista de otros igual no tan populares que pueden usar otros lenguajes como PHP, o incluso el histórico Perl, como es el caso de Cataclyst.

Sobre la eterna pregunta de cual es el mejor o el peor framework para hacer aplicaciones web supongo que la respuesta será "elige aquella que utilice un lenguaje que te resulte más familiar y que tenga una buena comunidad de usuarios y desarrolladores".

Django respeta el patrón de diseño MVC (Modelo-Vista-Controlador) aunque redefinido a MVT una vez que el componente controlador se refiere a su sistema de plantillas para generar lo que vemos en el navegador, pero también lo que almacenamos en las bases de datos. Ese patrón responde un poco a lo que vemos en el siguiente dibujo:

Modelo OSI
Sirviendo Django con nginx

En el mismo vemos a la izquierda la petición al servidor, en el centro se representa lo que sucede en el lado del servidor y finalmente a la derecha la página renderizada. Lo primero que sucede en Django es el procesamiento de la URL o dirección web. Tras esto se asignará una vista que básicamente es una función python que generará lo que se verá (la vista), es decir lo que se enviará al navegador web. Para generar este contenido, que puede ser por ejemplo en formato HTML o JSON, se puede hacer uso de plantillas e igualmente puede auxiliarse de consultas a la base de datos que estemos utilizando, que puede ser local o estar en la nube.

En esta introducción tan sólo me voy a limitar a explicar como en mi caso hago para instalar este framework en un servidor Debian, cómo creo el entorno de desarrollo de una primera aplicación y cómo hago para publicarla mediante un servidor web de producción.

Primero los cimientos

Vamos a inicializar todo el entorno de Django lo que haremos dentro del directorio de trabajo que puede ser cualquiera que elijas. En mi mi caso tengo Python 3.13 instalado en C:\Python13\ por lo que lo primero que hago es crear un entorno virtual para esta versión, lo cual hago desde el directorio de trabajo, y a continuación la "activo":

C:\Python313\python.exe -m venv env
.\env\Scripts\activate.ps1

Soy consciente de que Poetry es muy usado hoy en día para hacer estas cosas pero creo que aún no ha llegado mi momento para usarlo.

A continuación instalamos el paquete de Django:

pip install django

Tras esto podremos ver como en el directorio Scripts del entorno virtual (el cual queda incorporado al PATH con su activación) ha aparecido un ejecutable llamado django-admin que será el que utilizaremos para crear nuestro proyecto.

django-admin startproject test_project .

El . final indica a django-admin que no cree un directorio para el proyecto y que use el directorio actual en su lugar. En cualquier caso creará un nuevo directorio para la aplicación principal que recibe el nombre asignado al proyecto.

Con esto nos creará la siguiente estructura de directorios.

Estructura de archivos inicial de Django

Comentemos un poco los archivos:

Construida esta estructura básica ya podemos correr nuestra aplicación (que realmente no hace nada) utilizando el servidor de prueba mediante el siguiente comando:

python.exe manage.py runserver

El comando admite como argumento un número de puerto TCP o una cadena <address>:<port>. Por defecto se servirá en la dirección del loopback y puerto 8000, por lo que con el comando anterior tendríamos disponible la aplicación en la url ```http://127.0.0.1:8000``.

La funcionalidad de la aplicación se dará a través de las URL de las peticiones que llegan al servidor y que se procesan en urls.py. En este archivo se vinculan estas URL con las funciones que las despacharán que serán las encargas de crear las "Vistas" que no son otra cosa que las respuestas del servidor.

Aunque inicialmente no se crea un archivo donde definir tales funciones lo normal es que por cuestiones auto-ilustrativas las agrupemos en en archivo llamado views.py.

En cuanto al archivo settings.py, a no ser que sepas exactamente que estás haciendo no se recomienda tocarlo cuando inicias en Django.

Ejemplo extremadamente sencillo

Este es un artículo de iniciación a Django cuyo objeto no es más que exponer de manera sesgada como funciona este framework y llegados a este punto lo mejor es poner un simple ejemplo. Este ejemplo no se va a reducir a dibujar el clásico Hello World ya que presuntamente estamos diseñando una aplicación por lo que normal será que se realice alguna operación en el servidor que formule una respuesta (response) en base a lo solicitado (request).

En primer lugar, la carpeta test_project, donde se encuentra urls.py, vamos a crear un archivo views.py con el siguiente contenido:

from django.http import HttpResponse

def suma(request, a, b):
    resultado = a + b
    return HttpResponse("Resultado: " + str(resultado))

Y tras ello modificamos urls.py para que quede tal que así:

from django.contrib import admin
from django.urls import path
from test_project import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('suma/<int:a>/<int:b>/', view=views.suma)
]

Hecho esto, y arrancado nuestro servidor de prueba, podemos introducir como url http://127.0.0.1:8000\suma\10\2\ obteniendo como salida el mensaje Resultado = 12. Detrás de esto hay primero un "match" de la url suma\10\2\ con el patrón suma/<int:a>/<int:b>/, asignado 10 al primer argumento llamado a y asignado 2 al primer argumento llamado b, lo cual queda incorporado a un objeto URLPattern que Django utiliza para llamar a la función despachadora (Vista), incorporando además el HttpRequest como primer parámetro de la función que despachará la URL. En ese objeto se pueden incorporar además más parámetros GET o POST, por poner ejemplos de su utilidad.

La ejecución de la función despachadora retornará un objeto response que en este caso es un HttpResponse pero que bien podría haber sido por ejemplo un JSONResponse, o bien haber redirigido su resultado a alguna de las funciones renderizadoras de templates.

Obviamente se trata de un ejemplo que ni de lejos ilustra las capacidades de Django pero sí lo poco que hemos visto hasta ahora en este artículo. A partir de esto animo al lector que si le parece interesante este framework y se ajusta a sus necesidades que empiece a usarlo apoyándose en los muchos tutoriales que tantos y tantas altruistas han colgado en Internet.

Entendiendo Middleware

Cuando empiezas con Django puedes sentirte un poco abrumado por su complejidad interna. Lo normal es que cuando empieces con una tecnología te centres en familiarizarte con ella, siguiendo el primer tutorial Hola Mundo de turno, sin preocuparte de la mayor parte de las cosas.

Una de estas cosas que la primera vez que examiné el settings.py es MIDDLEWARE, y la verdad, los tutoriales de introducción a Django lo ignoran totalmente. Después de conocer su función creo que es algo que todo aquel que se inicia con Django debería conocer. El siguiente boceto deja bastante claro que son los MIDDLEWARE.

Modelo OSI
Diagrama de ejecución de la pila de Middleware(s)

Los MIDDLEWARE son una especie de módulos que se cargan mediante capas. El array con el mismo nombre en el archivo settings.py establece tanto los MIDDLEWARE cargados como el orden de los mismos. Creado mi proyecto de ejemplo, la configuración por defecto es la que sigue, que difiere en algo de la del boceto:

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

Hemos visto que cuando Django recibe una petición (request) de una URL la procesa despachándola hacia una vista. Antes de llamar al código de la vista pasa la request a los MIDDLEWARE en el orden dado aplicándole por tanto una especie de pre-procesado a la misma (la salida de un MIDDLEWARE se convierte en entrada del siguiente). Cuando la vista lanza una respuesta (response) esta se pasa igualmente a los MIDDLEWARE, en el orden inverso al anterior, que realiza una especie de post-procesado de tal respuesta en términos similares.

Me dejo como tarea para un futuro artículo el analizar como actúan internamente los MIDDLEWARE. De momento dejo el enlace a Middleware de la documentación oficial.

Primeros pasos con los templates

No suelo repetir enlaces externos en los artículos pero en el caso de los templates haré una excepción por comodidad, a la que añado otra relativa a su apartado de configuración. Su lugar en mi settings.py tiene esta forma:

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

En cuanto a la ubicación de los templates dentro del árbol de directorios del proyecto, y que será lo que habrá que añadir a DIR, resulta casi una convención que los mismos cuelguen de un directorio que para sorpresa de nadie tendría el nombre de templates. El nombre del directorio tiene que ser una localización absoluta dentro del árbol de directorios del sistema por lo que lo mejor es utilizar un nombre relativo a BASE_DIR. En nuestro caso:

        ...
        "DIRS": [BASE_DIR / "templates"],
        ...

Esa forma de construir el directorio a partir de BASE_DIR con el operador / no es más que producto de las mismas instrucciones que se proporcionan en el fichero de configuración en el que también encontramos:

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent

La lista de directorios proporcionada será donde Django busque las plantillas cuando alguna de sus funciones internas haga referencia a alguna de ellas como por ejemplo la función django.shortcuts.render.

Otra cuestión a reseñar es que en lo que respecta al BACKEND tenemos dos opciones de base la por defecto django.template.backends.django.DjangoTemplates que es la que se explica en templates (y ya van 3 veces), y la otra opción es django.template.backends.jinja2.Jinja2 cuya documentación está disponible aquí y que requiere tener instalado el paquete Python Jinja2.

Respecto al resto de opciones creo que escapan de lo que sería una mera introducción a Django. Y dicho esto lo mejor es proceder de nuevo con un ejemplo, de nuevo muy simple, para lo que he pensado que lo mejor era mantener la funcionalidad de la url http://127.0.0.1:8000\suma\10\2\ pero haciendo uso de templates. Cierto es que no resulta muy operativo pero no es este el objetivo de esta explicación.

Teniendo por modificado el TEMPLATES de settings.py en la forma en la que hemos dicho antes, comenzamos creando la carpeta templates en el directorio raíz del proyecto. Dentro de esta carpeta pasamos a crear un archivo resultado.html con el siguiente contenido:

Resultado: {{resultado}}

En este caso la plantilla es simplemente texto plano, sin ningún tipo de etiquetas html, pero es que el objetivo del ejemplo es mostrar exactamente el mismo resultado que el anterior.

Establecido el template toca modificar el views.py para que la función suma, que es la que genera la vista, diu¡buje el resultado usando el template en lugar de remitir directamente el objeto HttpResponse:

from django.shortcuts import render

def suma(request, a, b):
    resultado = a + b
    return render(request, "resultado.html", {"resultado": resultado})

Si navegas hasta el código de la función render verás que igualmente devuelve un objeto HttpResponse pero lo hace a través del motor de plantillas de Django. Dicha función recibe el objeto ```request, el nombre del fichero con la plantilla (se buscará en la lista que ya vimos TEMPLATES.DIRdesettings.py) y en este caso se envía también un contexto`` extra como tercer parámetro posicional.

Dentro de la plantilla podemos acceder a las variables de contexto mediante la expresión {{variable}} (nombre encerrado entre doble llave), algo que ya se puede inferir del contenido de resultado.html. Existen otras expresiones que no hemos usado aquí que se denominan tags que se encierran de otra manera: {% tag %}. Con estos tags podemos por ejemplo realizar control del flujo en la plantilla para decidir que dibujar con los clásicos if ... then ... else o usar bucles for.

En cuanto al contexto, en un caso real será bastante común que esté formado por datos que hayamos obtenido de las bases de datos, además de la entrada del usuario, o de procesar mediante código esta que es lo que hemos hecho en nuestro simplificado ejemplo.

Cualquiera que quiera profundizar en Django lo primero que debería hacer es profundizar en su lenguaje de plantillas, o si lo prefiere en el de Jinja2, si bien son extremadamente similares. Estos lenguajes de plantillas son muy versátiles y sin apenas dificultad podrás que crear extensiones al mismo. Quizás alguno de mis artículos termine hablando de esto.

El proceso en renderizado básicamente consiste en utilizar las plantillas base en html y combinarlas con el contexto. Lo usual es diseñar una estructura modular de plantillas utilizando un tag llamado extends que permite incorporar dentro de la plantilla el contenido de otras dando lugar a plantillas extendidas sobre las que se incorpora el contexto utilizando tags de control de flujo si es necesario. Un resumen gráfico de esto lo tenemos en el siguiente dibujo:

Proceso de renderizado de plantillas de Django

Y hasta aquí todo lo que creo que hay que decir para introducir Django. Esta información debería ser suficiente para que decidas si Django es el framework de desarrollo de aplicaciones web que se adapta a ti y a tus necesidades. Si así y eres nuevo en Django todavía te queda un largo trecho de aprendizaje por delante.

¿Y sobre las bases de datos qué?

En efecto, no hablado nada en este artículo en relación al uso de las bases de datos, algo que he hecho a conciencia una vez que no la considero un componente que deba estar en toda aplicación web. En cualquier caso su uso general para incorporar los datos que podamos tener almacenados a las páginas web finales será incorporar los datos al context para que sean procesados por el motor de plantillas.