February 8th, 2007
django: Todo hecho f谩cil
Django es la neta del planeta para mi 馃檪
Lo conoc铆 gracias a un comentario del “buena voz mother fucker” hace unos meses, y como python es mi lenguaje favorito cuando se trata de aprender algo nuevo, pues inmediatamente me puse a checar ese nuevo “framework” y en cuesti贸n de horas estaba totalmente enamorado :P.
Cuando se trata de programar en Python, simplemente no soy bueno, durante un par de a帽os he hecho cosas sencillas, incluso con Glade y GTK, y recuerdo cuando estaba en tmz que me avente un “servidor de chat” muy simple, que tiempos aquellos, pero simplemente no soy muy productivo escribiendo Python, as铆 que a veces me toma muchas horas hacer algo sencillo, de hecho luego sacando cuentas el numero de lineas por hora que escribo llega a ser muy cercano a 1 !! pero siempre quedo contento porque a pesar de terminar con muy pocas lineas, siempre logro tener algo que, al menos para mi, se ve muy elegante, y hace exactamente lo que quiero.
Y volviendo a Django, simplemente es asombroso lo que se puede hacer con tan poco conocimiento, voy a exponer unas cuantas lineas de un sistema pque帽o que estoy haciendo para guardar datos de usuarios en la red, es realmente simple, pero prefer铆 hacerlo en Python/Django que en PHP que es mi lenguaje m谩s productivo en general (para web casi exclusivamente), y la raz贸n de la elecci贸n fu茅 que Django me permite hacer trivialmente varias tareas: autenticaci贸n, localizaci贸n (traducci贸n), separaci贸n MVC (al estilo Django, pero muy elegante), portabilidad en la base de datos, uso de plantillas, URL bonitos, y como dicen en su sitio, “no me repito”, o sea, las cosas se hacen de la mejor manera para que sea trivial reproducirlas cuantas veces sea necesario sin mucho c贸digo (one-liners seria la “palabra correcta”), y adem谩s de todo me crea un panel de control en el que puedo editar datos a placer.
Como antecedente, me tomo unas 4 a 6 horas hacer lo que voy a escribir aqui, y este es el resumen de programaci贸n:
Lineas Archivo (wc -l)
10 urls.py
37 base/models.py
8 base/urls.py
40 base/views.py
95 total
en las plantillas tengo mas de 130 lineas, pero pues ni vale la pena contar eso porque el gaste de HTML puede ser arbitrario, depende de cuando dise帽o le quieras meter, y que tan h谩bil seas para hacerlo de manera elegante.
En realidad esta no es la aplicaci贸n terminada, solo hice un ejemplo que necesito para reproducirlo para todas las tablas que sean necesarias, este ejemplo es un listado paginado, con soporte de todo lo que dije arriba.
No quiero irme sobre todos los detalles de la configuraci贸n (settings.py) de la aplicaci贸n, simplemente voy a remitirme a la parte del listado paginado.
en una “sub aplicaci贸n” de la aplicaci贸n principal tengo este url.py:
from django.conf.urls.defaults import *
from iplist.base import models, views
urlpatterns = patterns('',
(r'^$', 'iplist.base.views.myview'),
(r'^view/$', views.object_list, {'model': models.People}),
)
Lo que hace es definir 2 listados parecidos, el segundo por medio de las vistas gen茅ricas que proporciona Django (generic.views), ambos paginan, pero el segundo es realmente elegante y portable, mientras que el primero es una vista a una sola tabla en particular.
Este es el archivo view.py, que contiene las 2 vistas en cuestion:
from django.template import Context, loader
from django.contrib.auth.decorators import login_required
from iplist.base.models import People
from django.core.paginator import ObjectPaginator, InvalidPage
from django.http import HttpResponse, HttpResponseRedirect
from django.views.generic import list_detail
@login_required
def myview(request):
people_list = ObjectPaginator(People.objects.all().order_by('name'), 3)
t = loader.get_template('base/list.html')
try:
page = int(request.GET.get('page'));
except:
page = 0
print page, people_list.pages
if (page < people_list.pages ):
c = Context({
'people_list': people_list.get_page(page),
'page': page,
'has_next': people_list.has_next_page(page),
'has_prev': people_list.has_previous_page(page),
'next_page': page+1 ,
'prev_page': page-1 ,
'paginator': people_list,
})
return HttpResponse(t.render(c))
else:
return HttpResponseRedirect('/list/')
@login_required
def object_list(request, model):
try:
return list_detail.object_list(
request,
queryset = model.objects.all().order_by('name'),
template_name = 'base/%s_list.html' % model.__name__.lower(),
paginate_by = 3,
)
except:
return HttpResponseRedirect('/list/view')
Las primeras lineas de “import” son necesarias para la vista de la tabla People paginada, los ultimos 2 “import”s son los que se necesitan para la vista gen茅rica, de hecho el 煤ltimo contiene 2 funciones necesaria para la primera vista, y otra para la segunda, y la linea que importa login_required se usa en los 2 (5 contra 3 imports!).
En la primera vista usamos la herramienta de paginaci贸n de Django, y seleccionamos una tabla sobre la cual vamos a iterar en la plantilla, despu茅s tenemos que tomar la plantilla y alimentarla con los datos necesarios. Aqu铆 un detalle importante, las plantillas no pueden accesar a m茅todos, solo accesan valores, arreglos, etc, por lo que es necesario alimentar bastante, en ves de por ejemplo pasar el objeto people_list con todas sus capacidades.
La segunda vista/clase/funcion/como-quieras-llamarle toma el parametro “page” autom谩ticamente de GET, pagina, y manda los par谩metros relevantes a la plantilla (pagina proxima, numero total de paginas, de registros, etc) con solo agregar “paginate_by” a la funcion list_detail.object_list.
NOTA: la linea precediendo a cada funci贸n hace necesario estar logueado con un usuario para ver la vista, las tablas y sistema de autenticacion los creo Django por mi, si quieres tener una forma de logueo solo creas una plantilla b谩sica y agregas una linea a url.py y listo ! todo lo dem谩s Django lo hace sin ayuda 馃檪
De tal manera que esta plantilla (templates/base/people_list.html) hace todo lo que se esperar铆a de una lista paginada:
{% load i18n %}
{% extends "base.html" %}
{% block title %}{% trans "Generic List" %}{% endblock %}
{% block content %}
{% for entry in object_list %}
<div style="border:1px solid black;">
<h1>{{ entry.name }}</h1>
<p>{{ entry.comments }} ({{ entry.department }})</p>
</div>
{% endfor %}
{% if has_previous %}
<a href="?page={{ previous }}"><</a>
{% else %}
{% trans "No Previous Page" %}
{% endif %}
|
{% if has_next %}
<a href="?page={{ next }}">></a>
{% else %}
{%trans "No More Pages" %}
{% endif %}
<br />
{% endblock %}
a trav茅s del segundo patr贸n en el url.py que puse arriba, y si notan todos los textos est谩n marcados para traducci贸n (notar la primera linea, es importante que sea as铆), pero para no hacer el cuento muy largo eso lo dejo para otro post (si es que lo llego a hacer :P).
Y dejo aqu铆 la plantilla de la vista “no gen茅rica”:
{% load i18n %}
{% extends "base.html" %}
{% block title %}{% trans "Another Testing Template" %}{% endblock %}
{% block content %}
{% for entry in people_list %}
<div style="border='1px';">
<h2>{{ entry.name }}</h2>
<p>{{ entry.comments }}</p>
</div>
{% endfor %}
{% if has_prev %}
<a href="?page={{ prev_page }}"><</a>
{% else %}
{% trans "No Previous Page" %}
{% endif %}
|
{% if has_next %}
<a href="?page={{ next_page }}">></a>
{% else %}
{%trans "No More Pages" %}
{% endif %}
{% endblock %}
En conclusi贸n, podemos lograr en 11 lineas de programaci贸n tener una funci贸n que nos pagina, y protege con autenticaci贸n el listado de cualquier tabla, ya que el nombre de la tabla lo mandamos a trav茅s de un par谩metro en una linea de url.py, de tal manera que podemos hacer cuantos querramos y reutilizar la misma vista, y al final solo tenemos que crear plantillas personalizadas para cada vista, ya que cada tabla tienen campos diferentes, o no ?? 馃檪
O en otras palabras, copias una linea en url.py, le cambias lo esencial, y copias la plantilla y haces lo mismo, igual y quieres un dise帽o totalmente diferente para cada listado :), y es todo, volvemos a煤n mas aburrido el trabajo del desarrollador web ! y le ahorramos un buen de tiempo, ni siquiera tiene que aprender struts !!
Hasta la proxima
P.D. Notaron la elegancia de la validaci贸n para page ?? es o no bonito Python ??