Django 1.3 en “cero coma” I

Puedes encontrar una versión actualizada de este tutorial en ’Django 1.5 in a nutshell I‘ para Django 1.5

Esta semana son las I Jornadas del Software Libre de la Facultad de Informática de la UCM y me toca dar un par de charlas. Una sobre lenguajes y paradigmas de programación y otra sobre framworks de programación de aplicaciones web. En especial, la charla tratará sobre Ruby on Rails y Django. De la de Rails se encargará mi compañero del Grupo de Usuarios de Línux de la Complutense y Vicepresidente de KDE España, Rafael Fernández y de la de Django, un humilde servidor.

Resulta que Rails tiene un tutorial para desarrollar una aplicación rápida de blog que presenta las principales características del framework y aunque el tutorial de Django es excelente y presenta muchas características del software, yo necesito algo más rápido, que me de tiempo a codificar en una hora que es lo que dura mi exposición.

Así que este post es eso, un tutorial rápido de Django donde desarrollar un blog muy sencillo y parecido al del tutorial de Rails, presentando las características principales del lenguaje pudiendo desarrollarse en menos de una hora. Espero que lo disfruteis.

0.- Requisitos previos

Asumiremos que trabajamos en Línux, y que disponemmos de un intérprete Python 2.7, la base de datos sqlite3 y el software de instalación de paquetes python easy_install. También vamos a asumir que hemos instalado correctamente Django 1.3 bien desde el servicio de paquetes de nuestra distribución de Linux, bien desde el comprimido que encontraremos en la web del proyecto.

Con easy_install instalaremos south, la aplicación de migración de base de datos para Django, de la que hablaremos más adelante. Bastará con escribir el siguiente comando (como administrador):

# easy_install south

1.- Configuración inicial

Ahora sí, el primer paso será crear la aplicación con el comando django-admin.py (a veces se encuentra disponible sin la extensión .py), así que tecleamos en una consola (no es necesario ser administrador):

$ django-admin.py startproject djangoblog

Con esto habremos creado la carpeta blog que contiene la estructura principal de nuestro proyecto. Vamos a configurar el proyecto de cara a la primera ejecución de prueba, para ello editaremos el archivo settings.py que se encuentra en la carpeta del proyecto.

Aplicaremos unos truquitos que me ha enseñado la experiencia con Django y que hacen nuestras aplicaciones más flexibles y reaprovechables.

Para empezar, añadiremos esto al comienzo del archivo:

import os
import sys
PROJECT_PATH = os.path.dirname("__file__")
sys.path.insert(0, PROJECT_PATH)

Con ello creamos una nueva variable de configuración que guarda la ruta del proyecto, de manera que ahora podemos indicar el resto de rutas de forma relativa a la nueva variable. Además añadimos esta ruta al path de python lo que repercutirá en ventajas futuras.

Lo siguiente será configurar la base de datos, usaremos SQLite3 por lo que modificaremos la variable DATABASES para que quede:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
        'NAME': os.path.join(PROJECT_PATH, 'db/database.db'), # Or path to database file if using sqlite3.
        'USER': '',                      # Not used with sqlite3.
        'PASSWORD': '',                  # Not used with sqlite3.
        'HOST': '',                      # Set to empty string for localhost. Not used with sqlite3.
        'PORT': '',                      # Set to empty string for default. Not used with sqlite3.
     }
}

También modificaremos las siguientes variables:

TIME_ZONE = 'Europe/Madrid'

STATICFILES_DIRS = (
    os.path.join(PROJECT_PATH, 'staticfiles'),
)

TEMPLATE_DIRS = (
    os.path.join(PROJECT_PATH, 'templates'),
)

MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
#    'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
)

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django.contrib.admin',
    'django.contrib.admindocs',
    'south',
)

Y crearemos los direcorios db, staticfiles y templates a los que nos acabamos de referir mediante:

$ mkdir db staticfiles templates

Además, modificaremos el archivo urls.py descomentando las lineas que guarden relación con la herramienta de administración:

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

# Uncomment the next two lines to enable the admin:
from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns('',
# Examples:
# url(r'^$', 'djangoblog.views.home', name='home'),
# url(r'^djangoblog/', include('djangoblog.foo.urls')),

# Uncomment the admin/doc line below to enable admin documentation:
# url(r'^admin/doc/', include('django.contrib.admindocs.urls')),

# Uncomment the next line to enable the admin:
url(r'^admin/', include(admin.site.urls)),
)

2.- Sincronización de la base de datos

Es hora de crear la base de datos para el proyecto actual. Puede parecer que no hemos hecho nada pero es que Django ya incluye algunas aplicaciones preinstaladas muy útiles como lo és la gestión de usuarios y grupos. Para ello escribiremos primero:

$ chmod u+x manage.py
$ ./manage.py syncdb

La primera línea otorga permisos de ejecución al archivo manage.py mientras que la segunda creará la base de datos al tiempo que nos preguntará por añadir un usuario administrador. Contestamos yes, respondemos a las preguntas pertinentes y listo.

3.- Probando el proyecto

Podemostio  escribir:

$ ./manage.py runserver

y probar el servidor. Si todo está funcionando correctamente, al entrar en la URL provista por el script deberíamos encontrar una página de error indicando que la URL no se corresponde con ninguna vista y proponiendo los patrones válidos. Esto es así porque la variable DEBUG está establecida en True. Lo que sí funcionará será la dirección /admin/ que nos permitirá acceder al sitio de administración. Modificaremos el usuario administrador para indicar nuestro nombre y apellidos.

Podemos detener el servidor pulsando Ctrl+C en la consola dónde lo estemos ejecutando.

4.- Añadir una nueva aplicación

Es hora de crear nuestra primera aplicación. Será con la que gestionemos los posts del blog y se llamará (en un alarde de originalidad e ingenio) posts. Para ello escribiremos, dentro de la carpeta del poryecto, lo siguiente:

$ django-admin.py startapp posts

El comando creará una carpeta posts y dentro, encontraremos tres archivos:

  • models.py contendrá los modelos de la aplicación
  • tests.py contendrá las pruebas para la verificación de la aplicación
  • views.py contendrá las vistas de la aplicación

Tendremos que editar el archivo settings.py una vez más para añadir la aplicacion posts a la lista de aplicaciones instaladas, justo antes de south (south siempre debe quedar la última):

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django.contrib.admin',
    'django.contrib.admindocs',
    'posts',
    'south',
)

5.- El modelo de la aplicación posts

Llegó la hora del turrón, toca definir el modelo para la aplicación posts por lo que editaremos el archivo models.py dejándolo como sigue:

from django.db import models
from django.contrib import auth

class Post(models.Model):

    title = models.CharField(max_length=255)

    machine_name = models.SlugField(max_length=255, primary_key=True)

    content = models.TextField(blank=True)

    publication_date = models.DateTimeField(auto_now_add=True)

    def __unicode__(self):
        return self.title

    def briefing(self):
        return self.content[:300] + '...'

    class Meta:
        ordering = ['-publication_date']

Esto define title como un campo de hasta 255 caracteres, una clave primaria machine_name con el tipico_formato_slug formado por sólo caracteres alfabéticos americanos, sin espacios y con guiones bajos como elemento de separación; el contenido, content, como un campo de texto que podríamos dejar en blanco y la fecha de publicación, publication_date, para la que especificamos que se autoestablezca en el momento de salvar un nuevo post.

Además creamos el método briefing() y __unicode__(). Los métodos de los modelos que funcionan sin parámetros se comportan como si de campos corrientes se tratasen. El método especial __unicode__() proporciona una representación textual del objeto (como si fuera el nombre propio de cada objeto), bien para la aplicación de administración, bien para tareas de depuración por nuestra cuenta.

La clase interna Meta nos permite establecer cierta información extra sobre el modelo. En nuestro caso especificamos la ordenación por defecto que será por fecha de publicación en orden descendente (del más reciente al más antiguo).

6.- Sincronizar la base de datos, el primer snapshot

Si no utilizáramos south, deberíamos sincronizar la base de datos mediante:

$ ./manage.py posts syncdb

Esto haría que Django creara las tablas que se corresponden al modelo de la aplicación indicada. Pero sin south, una modificación en el modelo nos obligaría a cambiar la base de datos manualmente para que coincidiese con el modelo. Gracias a Dio, south realiza capturas o snapshots de la base de datos y comprueba qué ha cambiado (casi sin nuestra intervención)  de una versión a otra.

Como esta es la primera vez que generamos el modelo de posts, usaremos el siguiente comando para crear el snapshot:

$ ./manage.py schemamigration posts --initial

Y este otro para aplicar los cambios y crear efectivamente, las tablas correspondientes:

$ ./manage.py migrate posts

7.- Registrando el modelo en el panel de administración

Antes de poder empezar a crear posts, es necesario registrar el modelo en la aplicación admin de Django. Para ello, bajo la carpeta posts crearemos un archivo admin.py con el siguiente contenido:

from posts.models import Post
from django.contrib import admin

admin.site.register(Post)

8.- El panel de administración autogenerado para posts

Podemos probar la aplicación con:

$ ./manage.py runserver

Puedes crear un nuevo artículo. No olvides el nombre para la máquina y observa cómo Django transforma los nombres de los campos en nombres humanizados sustiyendo los guiones bajos por espacios, trasladando a minúsculas el nombre de los modelos y añadiendoles una ‘s’ cuando los utiliza en plural. Puedes cometer errores (por ejemplo incluyendo espacios o caracteres acentuados en el campo Machine name) y probar la validación automática de los campos. La aplicación admin de Django es realmente potente y volveremos a ella más adelante.

Crearemos unos cuatro o cinco posts de prueba, más o menos largos.

9.- Vistas en Django

Las vistas y las plantillas son la base de la interacción usuario / aplicación en Django. El ciclo es el siguiente:

  1. Se produce una petición mediante una URL
  2. Ésta se compara con un patrón definido en urls.py que determina la vista a usar
  3. La vista genera un diccionario llamado contexto o context que pasa a una plantilla a la que está asociada
  4. La plantilla se interpreta con el contexto pasado por la vista y se genera el HTML oportuno

Una de las novedades de Django 1.3 es la inclusión de clases-vista en sustitución de las funciones-vistas. Si a esto añadimos el paquete de vistas genéricas que proporciona el framework nos encontramos que desarrollar vistas es algo increiblemente sencillo.

Editaremos el archivo views.py de la carpeta posts e incluiremos el siguiente código:

# Create your views here.
from django.views.generic import ListView
from posts.models import Post

class PostList(ListView):
    template_name="postlist.html"
    model = Post

Por heredar de ListView y gracias a especificar la propiedad model como Post, la vista introducirá automáticamente un objeto en el contexto llamado object_list con todos los objetos Post de la aplicaciones.

Ahora tenemos que unir esta vista a una URL, permitiremos dos formas de acceder a la lista de grupos:

  • /posts/list
  • / – como atajo

Para ello modifica el archivo urls.py de la aplicación de forma que quede de la siguiente manera:

from posts.views import PostList
from django.conf.urls.defaults import patterns, include, url

# Uncomment the next two lines to enable the admin:
from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns('',
# Examples:
# url(r'^$', 'djangoblog.views.home', name='home'),
# url(r'^djangoblog/', include('djangoblog.foo.urls')),

# Uncomment the admin/doc line below to enable admin documentation:
# url(r'^admin/doc/', include('django.contrib.admindocs.urls')),

# Uncomment the next line to enable the admin:
url(r'^admin/', include(admin.site.urls), name='adminpage'),

url(r'^$', PostList.as_view(), name='mainpage'),
url(r'^posts/', PostList.as_view(), name='postlist'),
)

Presta atención al import inicial y al parámetro name añadido a las funciones url de admin y posts.

10.- Plantillas en Django

Cuando se diseña una aplicación web no toda la página cambia según la funcionalidad que se ofrece en cada momento. Algunos elementos como la cabecera o el pie de página no varían. Esto sugiere el uso de plantillas con huecos que llenar por el contenido variable. Las plantillas de Django funcionan de una manera muy similar.

Para empezar, crearemos el archivo base.html en la carpeta templates del proyecto. Su contenido es el siguiente:

<!DOCTYPE HTML>
<html>
<head>
<title>Mi blog | {% block title %}Un título genérico{% endblock %}</title>
</head>

<body>
        <header>
            <h1><a href="{% url mainpage %}">Mi blog</a></h1>
        </header>
        {% block content %}Un contenido genérico{% endblock %}
</body>
</html>

El archivo define dos bloques: title y content. Con Django podemos crear plantillas que extiendan la plantilla base proporcionando sus propios bloques title y content. Con esto en mente crearemos el archivo postlist.html, también dentro de la carpeta templates del proyecto, con el siguiente contenido:

{% extends "base.html" %}

{% block title %}Post list{% endblock %}

{% block content %}
<section id="postlist">

{% for post in object_list %}
<article class="post">
<header>
<h2>{{ post.title }}</h2>
</header>
<section>
{{ post.content|safe }}
</section>
<aside>
<p>Fecha de publicación: {{ post.publication_date|date:"r" }}</p>
<!-- Aquí irán los comentarios -->
</aside>
</article>
{% endfor %}

</section>
{% endblock %}

Las marcas {% y %} sirven para introducir etiquetas. Por ejemplo, la etiqueta {% for … in …. %} permite recorrer los elementos de una colección. Usamos el objeto object_list del contexto proporcionado por la vista genérica como colección. La etiqueta {% block %} introduce un bloque y la etiqueta {% endblock %} lo cierra. La etiqueta {% extends … %} indica cuál es la plantilla base cuyos bloques serán sustituidos por los presentes en la actual. La etiqueta {% url %} del archivo base.html genera la URL que corresponde con ese nombre a partir de la expresión regular.

Las marcas {{ y }} sirven para introducir el contenido de una variable. Las variables pueden filtrarse utilizando el operador de tubería |. Por ejemplo, el filtro safe indica que el contenido del contenido del post es seguro en cuanto a contenido HTML por lo que los caracteres propios de HTML, que se escapan por defecto, no serán reinterpretados y se escribirán sin más. Así permitimos HTML en el cuerpo de los posts pero no en el título, por ejemplo.

Los filtros pueden aceptar parámetros como le ocurre a date usando dos puntos y el parámetro. En este caso la cadena indica un formato estándar de fecha larga.

Ahora estamos listos para probar la vista. Volvemos a lanzar el servidor de pruebas y, esta vez sí, accedemos a él sin añadir nada más a la URL.

$ ./manage runserver

Conclusión

Con eso hemos terminado con la primera parte del tutorial. No sé si me dará tiempo a contar las dos partes del tutorial en la presentación pero esta primera parte debería haberos convencido de cuáles son dos de las principales ventajas de Django: reutilización del código y backend.

Una de las máximas de Django es la reutilización de código, el ejemplo debería dejar claro esto también aunque quizá este tutorial de la sensación de que Django está orientados a casos muy concretos y… es verdad. Es verdad que el framework ofrece atajos arquitectónicos para los casos más comunes pero también es verdad que no hemos expuesto ni una décima parte del potencial de Django.

Por otro lado, la aplicación admin del framewor, tan potente y configurable (ya lo vereis en la segunda parte del tutorial) hace que podamos poner más énfasis y atención en la experiencia de usuario puesto que, en general, el administrador está algo más acostumbrado a lidiar con las tripas de las aplicaciones.

En la próxima entrega de este tutorial continúré con la mejora de la aplicación de administración, la adición de un sistema de comentarios, un sencillo formulario de búsqueda y una migración administrada por south para terminar de presentar las ventajas de este, parafraseando al equipo de desarrollo, “framework para perfeccionistas con las fechas de entrega”.

EDIT: Continúa en Django en “cero coma” II

Anuncios

13 comentarios en “Django 1.3 en “cero coma” I

  1. Estoy siguiendo tu tutorial para aprender Django (y Python) y me encuentro con que lo que dices en el punto 8 no es exacto, al menos en mi caso.

    Cuando añado elementos de tipo posts, en el campo “machine name” no me permite poner ni espacios ni tildes, si los escribo me da la siguiente advertencia:
    Enter a valid ‘slug’ consisting of letters, numbers, underscores or hyphens.

    Es sólo un detalle.

    1. Hola Granjero

      Precisamente el punto 8 pone:

      “Puedes cometer errores (por ejemplo incluyendo espacios o caracteres acentuados en el campo Machine name) y probar la validación automática de los campos.”

      Si añades tildes o espacios (es decir, cometes errores) saltará la advertencia (es decir, la validación automática).

      ¿O te he entendido mal?

      1. Ah! Ok, ok.
        Entonces ha sido error mío, un error de “comprensión lectora”, si es que leyendo a las tantas de la noche después de un día sin parar es normal entenderlo todo al revés.

        Estás en lo cierto, la validación funciona perfectamente.

        Lo que yo había entendido era que si usaba tildes o espacios en el “machine name” Django convertía estos caracteres: los espacios por guiones bajos y las tildes por las vocales sin tilde.

        Error mío. Perdón.

  2. Hola, primero que todo, decir que estña genial está guia, gracias.

    bueno, he revizado todo lo que he echo y he seguido paso a paso está primera parte… pero ya cuando he llegado al dina de esta primera parte e intento probar recibo este error:

    ImportError at /admin
    cannot import name ListView
    Request Method: GET
    Request URL: http://127.0.0.1:8000/admin
    Django Version: 1.2.5
    Exception Type: ImportError
    Exception Value: cannot import name ListView

    algúna idea?

    1. Hola Barceló, el problema que tienes es muy sencillo de resolver. Tienes la versión 1.2.5 de Django pero el tutorial es para la versión 1.3 tal y como pone al principio en 0.- Requisitos Previos.

      Precisamente ListView es una de las novedades de la versión 1.3

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s