Mostrando entradas con la etiqueta django. Mostrar todas las entradas
Mostrando entradas con la etiqueta django. Mostrar todas las entradas

martes, 4 de febrero de 2014

Django y las class based views


Cuando se trabaja en django resulta claro que hay muchas labores que son mas y mas fáciles, pero manejar la lógica de una petición mediante una función puede ser perjudicial a la hora de manejar muchas vistas. Es por eso que en este post vamos a ver cómo usar class bases views, una forma mas ordenada de encapsular la lógica.

¿Qué son?
Class-Based-Views (CBV en adelante) es una forma alternativa para crear vistas en django, no pretende reemplazar  las Function-Based-Views (FBV en adelante) , pero si tienen como objetivo permitir al desarrollador:
  • Organización del código relacionado con metodos HTTP específicos (GET, POST, PUT, etc) y que el router pueda acceder a estos sin tener que usar condicionales específicas.
  • Técnicas orientadas a objetos (Como los Mixins) para solucionar ciertos problemas.
¿Por qué usarlas?
La pregunta clave es ¿por qué? y ante esto hay varias cosas que la respaldan:

  • Orden.
  • Estética del Código.
  • Reciclaje del Código.
  • Uso de herramientas de OOP.
  • Simpleza.
  • Estructura bien definida (definición de acciones para cada protocolo, definición de respuestas para casos de éxito y falla con métodos como get_success_url).
¿Cómo funcionan?
Para entender mejor ciertos aspectos de cómo funcionan las CBV debemos ver un poco de FBV

# views.py
from django.shortcuts import render

def index(request):
    if request.method == 'GET':
        # TODO: GET ACTIONS
        return render(request,"template_get.html")
    elif request.method == 'POST':
        # TODO: POST ACTIONS
        return render(request,"template_POST.html")
    elif request.method == 'PUT':
        # TODO: PUT ACTIONS
        return render(request,"template_put.html")
    elif request.method == 'DELETE':
        # TODO: DELETE ACTIONS
        return render(request,"template_del.html")

# end views.py
# ----------------------
# urls.py

from django.conf.urls import patterns

urlpatterns = patterns('',
    (r'^$', "myapp.views.index"),
)
# end urls.py

Como vemos en el código anterior en una sola función la lógica de múltiples ti pos de respuestas, y esto no es adecuado porque restara mantenibilidad y escalabilidad a nuestra aplicación, para evitar eso están las CBV, veamos a continuación cómo sería esto:
# views.py
from django.views.generic.base import View
from django.shortcuts import render

class IndexView(View):

    def get(self, request):
        # TODO: GET ACTIONS
        return render(request,"template_get.html")

    def post(self, request):
        # TODO: POST ACTIONS
        return render(request,"template_POST.html")

    def put(self, request):
        # TODO: PUT ACTIONS
        return render(request,"template_put.html")

    def delete(self, request):
        # TODO: DELETE ACTIONS
        return render(request,"template_del.html")

# end views.py
# ----------------------
# urls.py

from django.conf.urls import patterns
from myapp.views import IndexView

urlpatterns = patterns('',
    (r'^$', IndexView.as_view()),
)

# end urls.py

Como vemos en el código anterior la legibilidad del código mejoraría en caso de que existiesen muchas líneas de código por cada método, además de que ya sería posible usar ciertas técnicas de programación orientada a objetos como los mixins.

Espero que les sea de utilidad.

Si te gusto el post 
compartelo... :D

miércoles, 29 de enero de 2014

Haciendo una API REST en 20 minutos con Python y Django


Hace poco vi en uno de los blogs que leo un muy interesante artículo acerca de cómo hacer una API REST en una hora, pero ¿Por qué no hacer una en 15 minutos? Para esto vamos a usar python, dajngo y dajngo-rest-framework.

Instalando los componentes
En un artículo pasado habíamos hablado de virtualenv y pip, en este caso usaremos virtualenvwrapper para crear un nuevo entorno y pip para instalar lo necesario:

mkvirtualenv entorno_rest --no-site-packages
workon entorno_rest
pip install django djangorestframework
django-admin.py startproject test_rest
cd test_rest
django-admin.py startapp apprest

Hora de codificar
Primero que todo es necesario configurar las preferencias del proyecto, es decir, editar el archivo settings dentro de la carpeta test_rest
En éste archivo es necesario incluir en las aplicaciones instaladas nuestra aplicación rest y el framework así:

INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'apprest',
)
Además de esto si quieren que la aplicación solicite usuario y contraseña (mediante Basic Auth) agregamos al archivo:

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated',
    ),
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.BasicAuthentication',
    ),
}

Muy bien, luego creamos los modelos de nuestra aplicación, para ello editamos el archivo models.py de la carpeta apprest y definimos lo necesario, yo solo voy a definir dos modelos, pero podrían ser cuantos quisieran:
class Autor(models.Model):
    nombre = models.TextField(max_length=100)
    apellido = models.TextField(max_length=100)
    
class Libro(models.Model):
    nombre = models.TextField(max_length=100)
    editorial = models.TextField(max_length=100)
    genero = models.TextField(max_length=100)
    autor = models.ForeignKey(Autor)

Ahora bien, es necesario crear 3 elementos auxiliares,  serializadores, que convierten los datos recuperados de la base de datos en json, viewsets, que son los elementos que de acuerdo al tipo de petición renderizan los datos y un router, el cual se encarga de indicar al proyecto django cuáles son las URLs de nuestra API. El serializer se creará en un nuevo archivo que está destinado para todos los serializers, estará dentro de apprest y se llamará serializers.py, veamos:

from rest_framework import serializers
from .models import Libro, Autor

class LibroSerializer(serializers.ModelSerializer):
    class Meta:
        model = Libro
        fields = ('id', 'nombre', 'editorial', 'genero', 'autor',)

class AutorSerializer(serializers.ModelSerializer):
    class Meta:
        model = Autor
        fields = ('id', 'nombre', 'apellido',)

Ahora es necesario definir los viewsets, esto se hace en un archivo llamado viewsets.py, que esta ubicado en apprest. A continuación su contenido:

from .models import Libro, Autor
from .serializers import LibroSerializer, AutorSerializer
from rest_framework import viewsets

class LibroViewSet(viewsets.ModelViewSet):
 
    serializer_class = LibroSerializer
    queryset = Libro.objects.all()

class AutorViewSet(viewsets.ModelViewSet):
 
    serializer_class = AutorSerializer
    queryset = Autor.objects.all()
    
Finalmente es necesario crear el router y enlazarlo a nuestro proyecto, en esta oportunidad lo haremos directamente en el archivo urls.py del proyecto, ubicado en test_rest, en este definiremos un router, el cual tendrá referencias a los elementos creados anteriormente. Esto se hará así:

from apprest.viewsets import LibroViewSet, AutorViewSet
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'libros', LibroViewSet)
router.register(r'autores', AutorViewSet)
   
y en la tupla urlpatterns agregamos:
    ...
    url(r'^', include(router.urls)),
    url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
    ...
para obtener finalmente algo así:
from django.conf.urls import patterns, include, url
from django.contrib import admin
admin.autodiscover()

from apprest.viewsets import LibroViewSet, AutorViewSet
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'libros', LibroViewSet)
router.register(r'autores', AutorViewSet)

urlpatterns = patterns('',
    # Examples:
    # url(r'^$', 'test_rest.views.home', name='home'),
    # url(r'^blog/', include('blog.urls')),
    url(r'^', include(router.urls)),
    url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),

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

para obtener finalmente:
Pueden encontrar el código fuente en github, espero que les sirva de ayuda.

Si te gusto el post
compartelo... :D

lunes, 25 de noviembre de 2013

Puesta en producción de una aplicación web con Django y Nginx


Cuando se usan frameworks para desarrollo web todo se hace más fácil, excepto la puesta en producción, una muestra clara de esto es django, un framework que permite que cosas como hacer un entorno de administración parezca juego de niños y que realizar consultas que parecen concebidas en la maligna mente del mismo Satanás sea una tarde de picnic. Desafortunadamente a la hora de poner en producción un proyecto empezamos a ver dificultades. Este post tiene como objetivo mostrar una sencilla manera de poner en producción nuestros proyectos con Django mediante el servidor Nginx.

Nota: Todo el proceso de configuración esta preparado para Linux Debian y derivados, si usas una distribución diferente es probable que cambien las rutas de los archivos de configuración.

Primero que todo, ¿Qué es Nginx?

Nginx es un servidor web y proxy inverso ligero de muy alto rendimiento, es software libre de código abierto y multiplataforma, tiene las siguientes características:
  • Servidor de archivos estáticos, índices y autoindexado.
  • Proxy inverso con opciones de caché.
  • Permite realizar balanceo de carga.
  • Tolerancia a fallos.
  • Soporte de HTTP sobre SSL.
  • Soporte para FastCGI con opciones de caché.
  • Servidores virtuales basados en nombre y/o en dirección IP.
  • Streaming de archivos FLV y MP4.
  • Soporte para autenticación.
  • Compatible con IPv6.
  • Soporte para protocolo SPDY.
  • Soporte para compresión gzip de paquetes.
  • Habilitado para soportar más de 10.000 conexiones simultáneas.
Muy bien, pero ¿Qué pasa con lo clásico?
Muchos se preguntaran ¿Por qué Nginx y no Apache2?, pienso que hay varias cosas al respecto:
  • La cantidad de recursos que demanda Apache2 son mucho mayores que los demandados por Nginx.
  • La velocidad alcanzada por Nginx es mucho mayor que la de Apache2 a la hora de responder peticiones.
  • Nginx es muchisimo más fácil de configurar que Apache2 puesto que Apache2 usa engorrosos XMLs, Nginx no.
La información mencionada la obtuve de aquí, aquí y aquí.

Muy bien, entonces, manos a la obra
En este momento me parece adecuado explicar cómo funciona la conexión entre django y Nginx:

Nginx es el encargado de atender las peticiones externas por el puerto 80 (protocolo HTTP), éste a su vez envía los datos de la petición a uWSGI mediante un puerto auxiliar, quien hace de intermediario en el proceso de comunicación y finalmente uWSGI entrega los datos de petición a nuestro proyecto django para que éste ejecute la acción correspondiente e inicie el proceso inverso para responder a la petición. Escogí uWSGI porque me pareció muy fácil de usar y en éste enlace hay una comparativa de rendimiento en la cual el tiempo de respuesta es mejor que el de Gunicorn.

Configuremos Nginx
Debemos crear el archivo de configuración para nuestro proyecto, recuerda que si el puerto 80 está siendo usado por Apache2, por alguna otra aplicación o por el mismo Nginx por otra configuración debes liberar primero el puerto, en caso de ser Apache2 debes editar el archivo /etc/apache2/ports.conf y cambiar el puerto 80 por uno diferente, luego reiniciar el servicio Apache2 así:
$ service apache2 restart
Después de esto debes crear el archivo de configuración en la carpeta /etc/nginx/sites-avaliable, el nombre del archivo lo eliges tú, yo elegí como nombre archivo.conf. El contenido del archivo debe ser el siguiente:
# archivo.conf
server {
    listen      80; # puerto por el que escucha la aplicación
    server_name midominio.co; # nombre o ip del servidor
    charset     utf-8;

    # tamaño maximo de subida
    client_max_body_size 75M;

    # Ruta de la carpeta media
    location /media  {
        alias /var/www/proyecto/media; # reemplace por su ruta a media
    }

    # Ruta de la carpeta static
    location /static {
        alias /var/www/proyecto/static; # reemplace por su ruta a media
    }

    # Configuración de las peticiones que no son resueltas con las
    # reglas anteriores. Estas son reenviadas al servidor django
    location / {
        uwsgi_pass 127.0.0.1:8001; # 127.0.0.1:PUERTO_AUXILIAR
        include     uwsgi_params;
    }
}
Cabe resaltar que el puerto auxiliar seleccionado es el 8001, este dato es importante puesto que debemos tenerlo en cuenta para iniciar la labor de uWSGI.
Después de esto es necesario habilitar la nueva configuración creando un enlace simbólico del archivo en la carpeta sites-avaliable de Nginx así:
$ cd /etc/nginx/sites-enabled
$ ln -s ../sites-avaliable/archivo.conf .
Finalmente reiniciamos el servidor Nginx
$ service nginx restart

Configuremos nuestro entorno
Primero es necesario crear un entorno para el proyecto con la orden mkvirtualenv vista en el post anterior y luego activamos el entorno creado con la orden workon:
$ mkvirtualenv entorno
$ workon entorno
después de ésto es necesario instalar las dependencias de nuestro proyecto django, personalmente pienso que es bueno trabajar con django 1.5 o superior, puesto que aasi tendremos muchas de las nuevas características, además django 1.6 fue liberado hace un par de semanas.
$ pip install django
$ pip install ....
$ pip install ....
Ahora necesitamos instalar uWSGI:
$ pip install uwsgi
Nota: Para instalar uWSGI es necesario tener instalados en el sistema el compilador gcc y los paquetes de desarrollo de python, para el caso de linux debian y derivados con python2.7 deben instalarse como superusuario así:
$ apt-get install gcc-4.5 python2.7-dev
Finalmente creamos el archivo para iniciar el proceso de conexión entre Nginx y django, este archivo es un script bash al que yo llame start.sh, este debe tener el siguiente contenido:
#!/bin/bash
uwsgi --socket :8001 --wsgi-file wsgi.py -d mensajes.log

Aquí viene la parte importante, cuando django genera un proyecto él crea una carpeta de proyecto, dentro de ésta hay una carpeta con los archivos de configuración, éstos son settings.py, urls.py, __init__.py y wsgi.py


debe hacer una copia del archivo wsgi.py a la raíz del proyecto y en éste mismo lugar poner el archivo start.sh así:

Después es necesario permitir la ejecución de este archivo mediante la orden:
$ chmod +x start.sh
Luego podemos iniciar el proceso así:
$ ./start.sh
Si tienes alguna duda o comentario puedes hacerla en la sección de comentarios, espero que sea útil.

Si te gusto el post
compartelo... :D