Tutorial de iniciación a Django: Crear un blog

Hace un par de años impartí varias veces un tutorial de iniciación a Django. Se trata de la creación de un blog muy simple pero que nos ayudará a tocar casi todas las partes importantes del framework.

Antes de empezar...

Antes de empezar necesitáis tener instalado Python2.x y Django (el tutorial lo voy a seguir con Django 1.4.3). Aquí os dejo el enlace de descarga de Python y el de descarga e instalación de Django Hasta ahora fácil, ¿no?

Introducción

Un poco de teoría. Django es un framework que sigue un patrón MVC, es decir, Model-View-Controller. En este patrón cada parte del programa se encarga de una cosa diferente.

Model es la parte del programa que se encarga de modelar, recuperar y salvar datos. Controller se ocupa del procesamiento de datos, de transformarlos, de la lógica de la aplicación. View es la capa de presentación. Se ocupa de cómo se mostrarán los datos.

En Django es un poco confuso. Controller corresponde con la parte de Views de Django. Y View corresponde con las Templates. Es por ello que en este caso se hace llamar MVT (Model-View-Template) en lugar de MVC. Si queréis un poco más de información podéis ver la FAQ de la página oficial de Django.

Para trabajar con Django vamos a usar una consola y un editor de texto plano. Con eso es suficiente.

Paso 1: Creamos el proyecto

Para crear un proyecto sólo tenemos que ejecutar el siguiente comando:

%> django-admin.py startproject blog

Este comando creará un proyecto con un aplicación de igual nombre. Esta aplicación será la principal. Mi recomendación es que no metamos vistas ni modelos de datos en ella, pero es sólo una cuestión personal.

Además, aquí tenemos un fichero de urls, que es dónde definiremos las rutas de acceso a cada vista y el ficheron settings, dónde se encuentra toda la configuración del proyecto.

Paso 2: Configuramos el proyecto

Abrimos el fichero settings con nuestro editor de texto plano favorito. Aquí se pueden configurar muchas cosas pero yo os voy a comentar sólo las básicas para el ejemplo.

Tendremos que modificar los siguientes campos:

DATABASES: para el ejemplo nos bastará con un Sqlite3 pero si vamos a ponerlo online tendréis que instalar un PostgreSQL o un MySQL. A Engine le daremos el valor de django.db.backends.sqlite3 y pondremos un NAME a la base de datos, por ejemplo, blog.db.

Además pondremos los siguientes valores:

TIME_ZONE = 'Europe/Madrid'
LANGUAGE_CODE = 'es-es'

Por ahora es sufuciente. Luego continuaremos configurando más cosas.

Paso 3: Creamos una aplicación

Para crear una aplicación tenemos que estar en la raíz del proyecto. Ahora ejecutamos lo siguiente:

%> python manage.py startapp poster

Con ese comando creamos una aplicación llamada poster. Ahora tendremos disponible un nuevo directorio con ese nombre que contendrá un archivo models.py y uno views.py que contendrán el modelo de datos y las vistas respectivamente.

Paso 4: Creamos el modelo de datos de poster

Vamos a trabajar en el archivo models.py del directorio poster. Una de las grandes virtudes de Django es su ORM. Es decir, definimos los modelos de datos como clases y accedemos a la base de datos, tanto para leer como para escribir sin tener que escribir una sola línea de SQL.

Para el caso del blog vamos a crear una clase Post y otra case Category que estarán conectadas entre sí a través de una clave externa. Tal que así:

class Category(models.Model):
    name = models.CharField(max_length=200)

class Post(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    category = models.ForeignKey(Category)
    creation_date = models.DateTimeField(auto_now_add=True)

Es un ejemplo muy simple. Simplemente hemos usado cuatro tipos de campo: CharField, TextField, ForeignKey y DateTimeField.

CharField es un campo de texto corto. TextField es un campo de texto largo. ForeignKey es una clave externa a otro modelo que tiene que estar previamente definido. También podemos hacer referencia a modelos de otras aplicaciones si las importamos en el archivo. DateTimeField es un campo de tipo Fecha/Hora. Con el atributo auto_now_add a True indicamos que queremos guardar la fecha y hora en el momento de crearse. Además, cada modelo tienen un identificador numérico único id.

Es el momento de probar si nuestro modelo es correcto. Para ello volvemos al archivo de settings.py e incluimos la aplicación poster entre las INSTALLED_APPS. Una vez hecho esto ejecutamos el siguiente comando:

%> python manage.py sqlall poster

Si todo está correcto, nos mostrará el código SQL que se ejecutará a continuación. Ahora es hora de crear las tablas en la base de datos usando este comando:

%> python manage.py syncdb

Este comando es el encargado de crear nuestros modelos en la base de datos. Además, podréis observar que se crean otras tablas que Django usa para la gestión de usarios, permisos y demás. Nos preguntará si queremos crear un superusuario para la aplicación. Respondemos que sí y lo creamos.

Con este paso hemos acabado de crear nuestro modelo de datos.

Paso 5: Creamos las vistas

Esta vez vamos a trabajar en el archivo views.py del directorio poster. Aquí se ponen las funciones que gestionarán las peticiones al servidor. Vamos a crear 3 vistas diferentes: una vista para un único post, una vista de un listado de post para la página principal y otra vista para un listado de post filtrado por category.

Para empezar tenemos que importar los modelos que vayamos a utilizar y las funciones de Python o Django que vayamos a necesitar. Así:

from django.shortcuts import render_to_response

from poster.models import Category
from poster.models import Post

Ahora creamos la función. Todas las vistas reciben siempre el parámetro request que contiene información sobre la petición que se ha realizado al servidor. Además, podemos añadir más parámetros que luego veremos de dónde los sacamos. Una vez acabado el procesamiento necesario mandamos los datos a renderizar en plantillas HTML que explicaremos en el paso siguiente.

Vista para un post: Esta será la vista que muestre datos de un solo post. La función quedará así:

def one_post(request, idpost):
    post = Post.objects.get(id=idpost)

    return render_to_response(
        "post.html",
        {
            "post":post,
        },
    ) 

¿Veis cómo se realiza el acceso a los datos? Simplemente accedemos al Modelo y hacemos un get añadiendo el parámetro que queramos. Yo he usado el id pero podríamos usar el título o un slug. Luego pasamos los datos al renderizador. Básicamente le decimos renderizame este post con esta plantilla, post.html (aún tenemos que construirla).

Vista de un listado de posts: Esta será la vista que muestre los últimos posts en la portada del blog. La función quedará así:

def home(request):
    posts = Post.objects.order_by("-creation_date")

    return render_to_response(
        "home.html",
        {
            "posts":posts,
        },
    )

Esta vez no filtramos los posts. Simplemente los ordenamos por el campo que creamos conveniente, creatioin_date y si lo creemos necesario invertimos el orden añadiendo "-" delante.

Vista de un listado de posts filtrado por category: Esta será la vista que muestre los últimos posts de una categoría. La función quedará así:

def posts_by_category(request, idcategory):
    category = Category.objects.get(id=idcategory)
    posts = category.post_set.order_by("-creation_date")

    return render_to_response(
        "home.html",
        {
            "posts":posts,
        },
    )

Esta vez vamos a hacer el acceso a base de datos en 2 pasos. Primero cogemos la category igual que hicimos en la primera vista. Ahora vamos a pedir a esa category que nos de todos los posts que están relacionados a ella ordenados por creation_date.

####Paso 6: creamos las plantillas

Primero vamos a crear un directorio templates. Ahí guardaremos nuestras plantillas.

Las plantillas tienen un sistema de herencia muy interesante. Es decir, no tenemos que construir cada plantilla completamente. Es por ello que empezaremos construyendo una plantilla llamada base.html. Su contenido será el siguiente:

<!DOCTYPE html>
<html>
    <head>
    <title>{% block title %}{% endblock %}</title>
    </head>
    
    <body>
    {% block content %}{% endblock %}
    </body>
</html>

Aquí creamos nuestra estructura básica. Utilizaremos la etiqueta block para crear bloques de contenido que luego serán utilizados en las plantillas que hereden de estas. Es más fácil si lo vemos.

Vamos a crear la plantilla de post.html. Como sigue:

{% extends "base.html" %}

{% block title %}
    {{ post.title }}
{% endblock %}

{% block content %}
    <h1>{{ post.title }}</h1>
    <p>{{ post.content }}</p>
{% endblock %}

Podemos observar varios tipos de etiquetas utilizados en el lenguaje de plantillas de Django. Las etiquetas con doble llave ( {{ ... }} ) sólo imprime el dato que estamos pidiendo. Las etiquetas con llave - tanto por ciento ( {% ... %} ) son otras acciones. Por ejemplo, apertura y cierre de bloques, bloques de control if y for, etcétera.

Con la primera etiqueta ( {% extends "base.html" %} ) indicamos a la plantilla que tiene que heredar de otra plantilla. A continuación, no tenemos que crear toda la estructura HTML, sino que podemos hacer uso de los bloques de contenido creados en la plantilla base.html.

Ahora sólo nos queda una plantilla dónde rendericemos varios posts de manera consecutiva. El contenido de home.html será el siguiente:

{% extends "base.html" %}

{% block title %}Mi blog{% endblock %}

{% block content %}
    
    {% for post in posts %}
        <h1>{{ post.title }}</h1>
        <p>{{ post.content }}</p>
        <hr>
    {% endfor %}

{% endblock %}

Aquí podemos ver la forma de hacer un bucle for iterando sobre el listado de posts que le pasamos al renderizador. Creo que no necesita mayor explicación, ¿no?

Por último, pero muy importante, tenemos que indicar en el archivo de settings.py el lugar dónde tiene que buscar las plantillas. Para ello buscamos TEMPLATE_DIRS y añadimos la ruta completa de la carpeta de templates.

Paso 7: Creamos las URLs

Vamos a empezar modificando el archivo urls.py que se encuentra en la carpeta blog. Es al primer archivo de URLs que llega una petición. Lo que vamos a hacer es indicarle que queremos que use las URLs de nuestra aplicación poster.

Para ello vamos a añadir esta línea en las urlpatterns del archivo:

url(r'^', include('poster.urls')),

La primera parte es una expresión regular que indica que todas las URLs que tengan un comienzo (todas) pasen al fichero urls.py de la aplicación poster.

Ahora crearemos un fichero urls.py en la carpeta de poster. Debe quedar así:

from django.conf.urls import patterns, include, url

urlpatterns = patterns('',
    url(
         r'^post/(?P<idpost>[0-9]+)/$',
         'poster.views.one_post', 
         name="one_post"),
    url(
         r'^category/(?P<idcategory>[0-9]+)/$',
         'poster.views.posts_by_category',
         name="posts_by_category"),
    url(
         r'^$',
         'poster.views.home',
         name='home'),
)

Como veis cada URL consta de 3 partes. La primera es la URL en sí. Puede se sencilla, como la última, o más compleja, como las 2 primeras. ¿Recordáis los parámetros extras en las vistas? Pues aquí los tenemos. idpost e idcategory son parte de la URL y luego serán utilizados en la construcción de la página.

La segunda parte indica la vista que se ejecutará cuando llegue una petición a esa URL. Así, si llega una petición a http://midominio.com/category/3/ se ejecutará la función posts_by_category del archivo views.py del directorio poster añadiendo el parámetro idcategory.

El tercer parámetro no es obligatorio pero es bueno acostumbrarse a hacerlo así. Si continuais aprendiendo Django os resultará muy útil. En lugar de tener que indicar la URL completa podremos hacer cosas como {% url one_post post.id %} para imprimir una URL. Si más tarde cambia, este comando lo tendrá en cuenta y no tendremos que cambiarla en cada uno de los lugares dónde la habíamos utilizado.

¡Ya casi hemos acabado! Todavía no hemos creado contenido.

Paso 8: Activar el panel de administración de Django

Otra de las grandes virtudes de Django es su panel de administración. Vamos a activarlo.

Primero vamos al archivo de URLs principal. Tenemos que añadir estas dos líneas antes de la definición del urlpatterns:

from django.contrib import admin
admin.autodiscover()

Y además vamos añadir esta URL dentro de las urlpatterns antes de la URL que nos derivaba al fichero de URLs de poster:

url(r'^admin/', include(admin.site.urls)),

Tenemos que ser cuidadosos con el orden de las URLs ya que podrían resultar no alcanzables. Hecho esto vamos de nuevo al fichero de settings.py y añadimons en las INSTALLED_APPS la aplicación:

    'django.contrib.admin',

Ahora vamos a hacer accesibles nuestros modelos desde el panel de administración. Para ello vamos a añadir un fichero admin.py en el directorio poster indicándoselo. Debería quedar así:

from django.contrib import admin
from poster.models import Category
from poster.models import Post

admin.site.register(Post)
admin.site.register(Category)

Símplemente registramos los modelos en el panel de administración. Por último, vamos a ejecutar de nuevo el comando para sincronizar la base de datos, ya que Django Admin añadirá alguna tabla más:

%> python manage.py syncdb

Paso 9: ¡Lo ponemos en marcha!

Ya iba siendo hora, ¿verdad? Django incluye su propio servidor de pruebas. Podemos arrancarlo ejecutando:

%> python manage.py runserver

Hecho esto tendremos nuestra aplicación funcionando en http://localhost:8000/. Si entramos, deberíamos ver una pantalla en blanco. Si entramos en http://localhost:8000/admin/ podremos ver el panel de administración.

Meted el nombre de usuario y contraseña que elegimos cuando creamos la base de datos. Y cread algunos posts y categorys de prueba. Si ahora entráis en la página principal del proyecto deberíais ver un listado de posts.

También podemos probar URLs de un solo post, por ejemplo http://localhost:8000/post/1/. O URLs dónde filtramos por category, por ejemplo http://localhost:8000/category/1/.

Un pequeño extra

En el panel de administración, cuando creamos una category o un post nos lo lista como "post object" o como "category object". Para evitar esto debemos añadir un método a los modelos correspondientes como los que siguen. Para post:

    def __unicode__(self):
        return self.title

Para category:

    def __unicode__(self):
        return self.name

Y ya está. ¡Espero que os haya gustado este tutorial!

Si detectas algún error, tienes alguna sugerencia o duda, o hay algo que no te ha quedado claro, no dudes en contactarme.

Puedes descargar este proyecto ya construido aquí.