Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
net/static/django-tutorial/
Tutorial de Django
Introducción
Requisitos
El ecosistema de Django
Estructura de (una aplicación) Django
Nuestra aplicación web: Trivial trivial
Creación del proyecto y la aplicación
Definiendo los modelos
Siguente: implementando la autentificación
Formulario de preguntas
Respondiendo a la pregunta
Conclusiones
Notas
Introducción
Django es un "framework" para el desarrollo de aplicaciones Web basado en el
lenguaje de programación Python y que sigue el patrón de diseño MVC. En los
últimos tiempos la palabra "framework" parece que se ha convertido en el "Santo
Grial" del desarrollo de aplicaciones Web.
Uno de los "framework" (1) más famosos o populares que se ha dado a conocer
ha sido Ruby On Rails. ¿Qué aporta esta herramienta frente a otras alternativas
más conocidas y establecidas?.
Basándose en algunas de las ideas de Rails y otras propias han surgido bastantes
herramientas de desarrollo de aplicaciones web con una orientación similar: poco
código, reusable, etc.
En este artículo presentaremos Django, que recoge muchas de las ideas de Rails
y aporta algunas soluciones propias. Desarrollaremos una aplicación viendo los
problemas que nos encontramos y cómo los solucionamos. En casi todos los
tutoriales que hemos leído, se presenta sólo lo más básico, pero a la hora de
1 de 17 26/08/10 13:15
T utorial de Django http://davidasorey.net/static/django-tutorial/
avanzar o tratar de hacer algo más "real" nos encontramos con dificultades. Este
artículo trata de paliar ésto.
Requisitos
Para seguir este tutorial es necesario conocer el lenguaje Python, y haber leído
el tutorial de Django. Por supuesto, asumimos que el lector sabe HTML y
tecnologías relacionadas (protocolo HTTP, CSS, etc.)
El ecosistema de Django
Es bastante habitual en este mundillo de los "framework MVC - que te cagas" (2)
que haya una (sana) competencia entre las distintas herramientas y lenguajes.
Podemos encontrar numerosas comparativas, tutoriales, guías, etc, pero todo este
(útil) material sólo rasca la superficie de lo que supone trabajar con estas
herramientas.
A veces da la impresión de que hay un concurso del tipo ¡Con XXX monto una
aplicación del tipo YYY en ZZZ minutos ! ó ¡Mi framework lava más blanco!
Esto puede resultar confuso, puesto que antes de empezar a desarrollar una
aplicación del tipo que sea hay que documentarse bastante para elegir la
herramienta adecuada. En cuanto a los " Web Frameworks" (otra vez la palabreja
;-) también hay bastantes alternativas. Las más conocidas son Django, TurboGears y
Pylons.
Los programadores del lenguaje Ruby no tienen que estrujarse tanto la cabeza.
La herramienta por antonomasia para desarrollo web de Ruby es Ruby On Rails. No
hay mucho más donde elegir. Esto tiene ventajas (centralización de esfuerzos,
uniformidad, ...) e inconvenientes (menor flexibilidad, ...). Cada uno debe valorar
qué es lo que prefiere.
Por último, un breve apunte sobre los lenguajes y sus modismos. Ruby es un
lenguaje con una filosofía parecida a Perl: "hay más de una forma de hacerlo",
Python tiene una aproximación diferente: "hacer las cosas de una sóla manera, la
más sencilla". De nuevo, aquí intervienen las preferencias personales de cada uno a
la hora de programar. Django sigue bastante la " filosofía pythonera" y tratan de
2 de 17 26/08/10 13:15
T utorial de Django http://davidasorey.net/static/django-tutorial/
seguir una serie de principios de diseño (mejor explícito que implícito, asumir que
el desarrollador sabe lo que está haciendo, ...).
Para crear una aplicación nueva dentro del proyecto ejecutamos python manage.py
startapp miaplicacion .
Este comando crea el directorio miaplicacion y los
ficheros __init__.py , views.py , y models.py .
Especificaciones:
3 de 17 26/08/10 13:15
T utorial de Django http://davidasorey.net/static/django-tutorial/
Tras esta breve disquisición, pasemos a definir los modelos. Encontramos las
siguientes entidades:
4 de 17 26/08/10 13:15
T utorial de Django http://davidasorey.net/static/django-tutorial/
settings.py (extracto):
MEDIA_ROOT = '/home/david/desarrollo/Trivial/site_media/'
MEDIA_URL = 'http://localhost:8000/site_media/'
ADMIN_MEDIA_PREFIX = '/media/'
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.admin',
'Trivial.juego',
)
urls.py (extracto):
También tenemos que crear la base de datos sqlite (comando sqlite data/datos.db ).
class Usuario(User):
def __str__(self):
return self.username
5 de 17 26/08/10 13:15
T utorial de Django http://davidasorey.net/static/django-tutorial/
class Admin:
pass
class Categoria(models.Model):
nombre = models.CharField("Categoría", maxlength=200)
def __str__(self):
return self.nombre
class Admin:
pass
class Pregunta(models.Model):
categoria = models.ForeignKey(Categoria, verbose_name="Categoría la que pertenece")
titulo = models.CharField("Título", maxlength=200)
texto = models.TextField("Texto de la pregunta")
respuesta_1 = models.CharField(maxlength=200)
respuesta_2 = models.CharField(maxlength=200)
respuesta_3 = models.CharField(maxlength=200)
respuesta_4 = models.CharField(maxlength=200)
respuesta_correcta = models.CharField(maxlength=200)
foto = models.CharField(maxlength=200)
def __str__(self):
return self.titulo
class Admin:
pass
class Respuesta(models.Model):
tiempo = models.IntegerField("Tiempo en segs.")
resultado = models.IntegerField("0 -> incorrecto, 1 -> correcto")
pregunta = models.ForeignKey(Pregunta, verbose_name="Pregunta que se responde")
usuario = models.ForeignKey(User, verbose_name="Usuario que responde")
def __str__(self):
return str(self.pregunta) + " (Usuario: " + str(self.usuario) + ")"
class Admin:
pass
Por último, haremos que Django sincronice la información que tiene de los
modelos con el sistema relacional (vamos, que cree las tablas necesarias): python
manage.py syncdb Este comando también creará las tablas necesarias para la aplicación
administrativa y el sistema de gestión de usuarios (de hecho nos pedirá los datos
6 de 17 26/08/10 13:15
T utorial de Django http://davidasorey.net/static/django-tutorial/
7 de 17 26/08/10 13:15
T utorial de Django http://davidasorey.net/static/django-tutorial/
En urls.py añadimos una entrada para "mapear" la dirección "/" (raíz del sitio) a
la función " index " en views.py :
urls.py (extracto):
@login_required
def index(request):
categorias = Categoria.objects.all()
preguntas = Pregunta.objects.all()
respuestas = Respuesta.objects.filter(usuario=request.user)
return render_to_response('index.html',
{'categorias': categorias,
'preguntas': preguntas,
'respuestas': respuestas,
'usuario': request.user,}
)
¿Qué hace este index ? Recoge todas las categorías, preguntas y respuestas del
usuario validado y se las "pasa" a una plantilla o template llamada " index.html ".
8 de 17 26/08/10 13:15
T utorial de Django http://davidasorey.net/static/django-tutorial/
También le pasa los datos del usuario (request.user). Como hemos especificado que
hay un login previo, podemos estar seguros de que esta variable "usuario" tiene
datos correctos.
{% extends "base.html" %}
{% block cuerpo %}
<strong>Listado de preguntas</strong>
{% if categorias %}
{% regroup preguntas by categoria as agrupado %}
<ul>
{% for grupo in agrupado %}
<li>{{ grupo.grouper }}</li>
<ul>
{% for item in grupo.list %}
<li><a href="pregunta/{{item.id}}/">{{ item.titulo }}</a><br>
{% for r in respuestas %}
{% ifequal item r.pregunta %}
La pregunta ya ha sido respondida.
{% endifequal %}
{% endfor %}
</li>
{% endfor %}
</ul>
{% endfor %}
</ul>
{% else %}
<p>No hay categorías</p>
{% endif %}
<p><a href="/accounts/logout/">Desconectar</a></p>
{% endblock %}
{% for r in respuestas %}
{% ifequal item r.pregunta %}
La pregunta ya ha sido respondida.
{% endifequal %}
{% endfor %}
Esta comprobación es algo ineficiente (en cada pregunta itera sobre todas las
respuestas) pero no lo hemos refinado por mantener la simplicidad. Seguro que se
puede hacer mejor ;-)
9 de 17 26/08/10 13:15
T utorial de Django http://davidasorey.net/static/django-tutorial/
con unos bloques de contenido que cada una de las plantillas "hijas" se encarga de
completar con {% block loquesea %}
Formulario de preguntas
Cuando el usuario sigue el enlace ( <a href="pregunta/{{item.id}}/">{{ item.titulo }}</a> )
que presenta cada pregunta en la plantilla index.html se le dirige a la página que
llamaremos "ficha de pregunta". Estas son las modificaciones que hemos
introducido:
urls.py (extracto):
urlpatterns = patterns('',
(r'^/?$', 'Trivial.juego.views.index'),
(r'^pregunta/(\d+)/$', 'Trivial.juego.views.pregunta'),
(r'^admin/', include('django.contrib.admin.urls')),
)
10 de 17 26/08/10 13:15
T utorial de Django http://davidasorey.net/static/django-tutorial/
{% extends "base.html" %}
{% block cuerpo %}
{% if texto_error %}
<p class="error">{{ texto_error }}</p>
{% endif %}
{% if respuesta %}
<p>Ya has respondido antes a la pregunta.</p>
<p>Tiempo empleado: {{ respuesta.tiempo }} segundos.</p>
<p>El resultado fue
{% if respuesta.resultado %}
CORRECTO
{% else %}
INCORRECTO
{% endif %}
</p>
{% else %}
<form method="post" action="/responder/">
<input type="hidden" name="pregunta" value="{{ pregunta.id }}">
<input type="hidden" name="tiempo" value="{{ tiempo }}">
<input type="radio" value="{{ pregunta.respuesta_1 }}"
name="respuesta">{{ pregunta.respuesta_1 }}<br>
<input type="radio" value="{{ pregunta.respuesta_2 }}"
name="respuesta">{{ pregunta.respuesta_2 }}<br>
<input type="radio" value="{{ pregunta.respuesta_3 }}"
name="respuesta">{{ pregunta.respuesta_3 }}<br>
<input type="radio" value="{{ pregunta.respuesta_4 }}"
name="respuesta">{{ pregunta.respuesta_4 }}<br>
11 de 17 26/08/10 13:15
T utorial de Django http://davidasorey.net/static/django-tutorial/
<br>
<input type="submit" value="Responder">
</form>
{% endif %}
{% endblock %}
Nos encontramos en esta plantilla con una variable ( texto_error ) que no hemos
asignado desde la función pregunta . Esta variable puede tener un valor cuando esta
plantilla es invocada desde otra función definida en views.py ( respuesta ). Lo veremos
un poco más adelante.
12 de 17 26/08/10 13:15
T utorial de Django http://davidasorey.net/static/django-tutorial/
Respondiendo a la pregunta
De nuevo, añadimos una regla al fichero urls.py para procesar las respuestas de
los usuarios. El fichero quedaría así (versión final):
urlpatterns = patterns('',
(r'^/?$', 'Trivial.juego.views.index'),
(r'^pregunta/(\d+)/$', 'Trivial.juego.views.pregunta'),
(r'^responder/$', 'Trivial.juego.views.respuesta'),
(r'^accounts/login/$', login),
(r'^accounts/logout/$', logout, {'template_name': 'registration/logout.html' }),
(r'^admin/', include('django.contrib.admin.urls')),
)
urlpatterns += patterns('django.views',
(r'^site_media/(.*)$', 'static.serve', {'document_root': MEDIA_ROOT}),
)
@login_required
def respuesta(request):
pregunta = Pregunta.objects.get(id=request.POST['pregunta'])
if not request.POST.has_key('respuesta') or request.POST['respuesta'] == "":
13 de 17 26/08/10 13:15
T utorial de Django http://davidasorey.net/static/django-tutorial/
Primero localizamos la pregunta (nos llega el id en la variable POST ' pregunta ').
Después comprobamos que han pulsado uno de los "radiobutton" de respuesta
( request.POST['respuesta'] ). Si no han respondido, redirigimos de nuevo a la página de
pregunta pasando un mensaje de error.
14 de 17 26/08/10 13:15
T utorial de Django http://davidasorey.net/static/django-tutorial/
respuesta.html :
{% extends "base.html" %}
{% block cuerpo %}
<h2>Resultado de la pregunta</h2>
<h3>{{ pregunta.titulo }}</h3>
<img class="foto" src="/site_media/{{ pregunta.foto }}">
15 de 17 26/08/10 13:15
T utorial de Django http://davidasorey.net/static/django-tutorial/
Conclusiones
Hemos hecho una aplicación web muy básica y sencilla, pero demuestra que
muchas tareas complicadas y/o tediosas de implementar nos la proporciona la
herramienta Django. La interfaz administrativa y el sistema de
autentificación nos han salido "gratis". Probablemente, la implementación de
estas dos funcionalidades nos hubiesen llevado bastante tiempo.
Observamos también la extrema sencillez en el desarrollo: con cuatro
elementos con unas funciones muy concretas (urls, modelos, vistas y
plantillas) tenemos perfectamente separadas la lógica, los datos y la
presentación. Compárese esta simplicidad con la típica aplicación J2EE, por
sencilla que sea. No hay color. Que las soluciones basadas en J2EE sean más
"potentes", escalables, robustas, etc. no lo voy a negar. Pero, ¿acaso se
necesita siempre esa "potencia"?.
No hemos desarrollado una aplicación similar con Rails, por lo que no sería
muy justo decir que Django es más fácil o mejor. Lo que si que podemos decir,
por lo que hemos visto y leído es que Django aporta varias características que
ahorran mucho trabajo, en este caso la interfaz administrativa y el sistema de
autentificación ( 9).
16 de 17 26/08/10 13:15
T utorial de Django http://davidasorey.net/static/django-tutorial/
Notas
1. Estoy un poco cansado de la palabra "framework". A partir de ahora y en lo
que queda de tutorial voy a emplear el término "herramienta", aunque no sea
el más adecuado ;-)
2. Disculpen el lenguaje soez, a veces se me escapa alguna palabrota ;-)
3. La documentación que he utilizado son estas dos fuentes: la documentación de
Django y el libro "The Django Book".
4. Una de las cosas que más me ha gustado de Django es la documentación:
completa, accesible y, sobre todo, centralizada. Me gusta mucho más que,
por ejemplo, la documentación de Rails: tutoriales dispersos por internet, falta
de un índice por tópicos, ... No voy a ser malpensado, pero da la impresión
que lo que quieren es que compres el libro que ha escrito el autor de Rails.
5. No voy a ser muy estricto con cuestiones de normalización del diseño de la
base de datos. Probablemente sería mejor tener una tabla aparte para las
posibles respuestas con una clave apuntando a la pregunta. Mantendremos
este diseño (incorrecto) por sencillez.
6. Es una aplicación web muy bien terminda y personalizable, nada que ver con
el "scaffold" básico que nos puede proporcionar Ruby On Rails. Si una
aplicación requiere una interfaz administrativa para manejar los distintos
objetos en Rails hay que programarla. Django nos la da ya hecha.
7. Rails por defecto no tiene nada similar, aunque existe un "plugin" para Rails
que implementa un sistema de autentificación. No lo he utilizado, así que no
puedo valorarlo.
8. Función, no método. Intencionadamente, los diseñadores de Django han
decidido que no hay necesidad de lidiar con objetos para atender a una simple
petición.
9. Rails también proporciona un montón de funciones, atajos y utilidades que
ahorran tiempo (la primera que se me viene a la cabeza es la maravillosa
options_from_collection_for_select).
10. Rollo legal: este es un documento de libre difusión (licencia Creative
Commons "by-nc-sa") con algunas limitaciones. Leer el texto de la licencia
completo para más detalles.
Subir
17 de 17 26/08/10 13:15