Definiendo las vistas de django con clases, al estilo de web.py

danigm's picture

Ahora que estoy trabajando con django estoy viendo lo potente que es, pero su sintaxis y forma de hacer las cosas no me acaba de convencer del todo comparandolo con web.py.

Una de las cosas que me gustan mucho de web.py es la forma de definir las vistas, que consiste en una clase donde defines el método GET y el método POST (no son obligatorios) y estos métodos son llamados según sea la petición http. Esto es muy util a la hora de hacer vistas de formularios, que si reciben la petición por GET muestran el formulario y si la reciben por POST hacen lo que sea con los datos, y en django suele haber un if request.method == 'POST' que guarrea la vista completamente.

Por eso hoy me he puesto y he hecho una pequeña prueba de concepto para poder permitir una definición similar en django, y así poder tener una mejor reestructuración del código de las vistas.

Lo he puesto en django snippets, pero también lo voy a poner aquí y lo comento:

[python]
from django.http import HttpResponse as response
from django.http import HttpResponseNotAllowed

class ViewClass:
def __call__(self, request, *args, **kwargs):
self.request = request
methods = ['POST', 'GET']
self.methods = [method for method in dir(self)\
if callable(getattr(self, method)) and method in methods]

if request.method in self.methods:
view = getattr(self, request.method)
return view(*args, **kwargs)
else:
return HttpResponseNotAllowed(self.methods)

class IndexView(ViewClass):
def GET(self):
return response("all ok %s" % self.request.method)

def POST(self):
return response("all ok %s" % self.request.method)

index = IndexView()
[/python]

¿Qué es lo que he hecho? He definido mi vista IndexView como una clase, que hereda de "ViewClass" (ahí está la mágia), donde me he definido mis dos métodos, GET y POST, igual que si lo hubiera hecho con web.py, suponiendo que GET se llamará cuando la petición sea GET y POST cuando sea POST, y el request en lugar de recibirlo como parámetro es un atributo de la clase, así que accedo a él con self.request.

Luego defino la vista como index = IndexView(), una instancia de la clase IndexView.

ViewClass es la clase de la que heredamos y la que implementa el método __call__. En python, si un objeto tiene el método __call__ es callable, y se puede llamar igual que una funcion. Es más, en python, las funciones son objetos que tienen definido el método __call__. Por tanto, como django espera que la vista sea una función, no una clase, django va a llamar al método call y le va a pasar los argumentos de la vista.

En el método __call__ lo que se hace es mirar si el tipo de petición HTTP está implementado en la clase y es uno de los válidos (methods) y si existe en la clase un método con ese nombre, lo llama pasandole los argumentos y poniendo previamente self.request = request. En caso de que el método no esté implementado devuelve responsenotallowed.

Para terminar, quiero comentar que otra de las cosas que no me gusta de django es el sistema de templates, con tantas llaves y porcentages por ahí, me parece mucho más elegante templetor, pero también es verdad que los templates de django son muy potentes y se pueden hacer muchas cosas.

Comments

1
Loren's picture

En la aplicacion django cmsutils hay una clase base para hacer formularios y dos subclases (para añadir y editar objetos) que siguen un patron muy similar. En mi caso la inspiracion fue zope3.

Estoy de acuerdo en que en general las clases son mas potentes y reutilizables para hacer vistas.