Posts Tagged ‘ django

django-thumbs ahora en blanco y negro

Trabajando con miniaturas de imágenes, nos ha surgido la necesidad de hacer algunas en blanco y negro.

Con este objetivo he decido mejorar el módulo django-thumbs añadiendo soporte para esto y de paso me he animado y ha realizado mi primera publicación en http://djangosnippets.org.

Toda la documentación está en el snippet que podéis encontrarlo aquí http://djangosnippets.org/snippets/2817/ :)

Extraño fallo en python reportlab 2.5

En el proyecto que ando inmerso actualmente estamos generando documentación en PDF continuamente.

 

Para ello renderizo un HTML con django para después haciendo uso de la biblioteca xhtml2pdf convertirlo a PDF, con muy buenos resultados. Esta biblioteca a su vez hace uso de reportlab (un toolkit para generar PDF), en su binding para python, concretamente estoy hablando de la versión 2.5.

Pues bien, aquí me he encontrado con un fallo en dicha biblioteca, que además está muy documentado y saltaba en documentos concretos, siendo muy difícil localizarlo en el documento origen.

 

Antes de andar buscando e indagando porque ciertos HTML provocaban el error he decido parchear el mismísimo reportlab, con sólo añadir 2 líneas tenemos el problema solucionado :)

 

Aquí les dejo el parche. Aplicar con patch -p1 -i reportlab_fix.patch en el directorio raíz de reportlab.

Mejorando django-simple-captcha

Esta semana estamos siendo objeto de un ataque de spam (basado en comentarios) en thechurchofhorrors.com, que aparte de lo absurdo de su objetivo es un auténtico incordio.

TheChurchofHorrors es un Magazine Cultural que yo mismo desarrollé hace un tiempo que sigo manteniendo y mejorando, el código fuente completo lo podemos encontrar en https://github.com/dugo/The-Church-of-Horrors

Al parecer el posible atacante ha sido capaz de romper la protección captcha de los comentarios, basada en django simple captcha versión 0.3.0

Recuerdo que se acordó la publicación de comentarios libre para fomentar la participación y la libertad de expresión, de hecho en las implementaciones iniciales mi idea era aprobar los comentarios, algo que molestó a mis compañeros.

Para solucionar el problema he implementado las mejoras expuestas en http://hacktimes.com/reforzando_django_simple_captcha/ pero con algunas modificaciones propias.

Continue reading “Mejorando django-simple-captcha” »

django-minipressroom

Aquí dejo mi nueva contribución al mundo del software libre, se trata de una aplicación de django muy simple pero funcional que puede ser muy útil para aprender.

La podréis encontrar de nuevo en GitHub https://github.com/dugo/django-minipressroom

La aplicación consiste en una sala de prensa, pensada para mostrar una serie de entradas organizadas por medios (tv, radio, etc)

Click para ver más grande


 De forma muy simple pero ilustrativa podemos encontrar un ejemplo de: modelos, vistas, plantillas , fixtures, admininistración e internacionalización.

Para usarla tendremos que instalar django-transmeta (pip install django-transmeta) que es necesario para algunos modelos.

 

Here is the attachments of this Post

Django y cross-origin

A muchos programadores web os sonará, otros quizá no os habréis topado con él.

El problema que voy a tratar en esta entrada es el de cross-domain o cross-origin (dominio cruzado u origen cruzado resp.) y su solución en Django. Para empezar debería hacer una introducción en que casos se puede dar este problema y las políticas que nos restringen poder hacerlo.

 

Supongamos que tenemos nuestra web alojada en http://mydomain.com y que desde https://mydomain.com/login queremos hacer una carga por AJAX de cualquier contenido o información que nos puede ser necesaria, nos topamos con que los navegadores (sobre todo los más modernos) nos impiden dicha petición argumentando algo similar a Access-Control-Allow-Origin not allowed

Continue reading “Django y cross-origin” »

Google App Engine como tu CDN dinámico

Debido a un proyecto he necesitado estudiar diferentes alternativas como CDN, y me decidí a estudiar GAE como una posibilidad.

Mi intención era subir contenidos multimedia (imágen, vídeo y sonido) dinámicamente y restringir el acceso a dicha información.

Para la subida hago un simple POST con la información y para la seguridad ideé un sistema basado en slots el cual explicaré más abajo.

Al ajo, vamos con el modelo:

# coding=utf-8
 
from google.appengine.ext import db
 
class Slot(db.Model):
    id = db.IntegerProperty(required=True)
    hash = db.StringProperty(multiline=False,required=True)
 
class File(db.Model):
    id = db.IntegerProperty(required=True)
    content = db.BlobProperty(required=True)

Como vemos, la clase File encapsula el fichero en sí, con un identificador y un BlobProperty que almacenará los datos.

La clase Slot encapsula un slot temporal que se ha abierto a ese fichero, consiste en un hash utilizado para cargar el fichero mediante la URL y el identificador del fichero en cuestión.

Las operaciones para acceder a un fichero en el CDN serían las siguientes;

  1. El cliente quiere obtener un fichero
  2. El servidor web (no AppEngine) hace la petición a AppEngine obteniendo el hash pertinente.
  3. El servidor web lleva este hash como URL al cliente, de la forma http://xxxx.appspot.com/[hash]
  4. El cliente obtiene el fichero e inmediantamente el hash es borrado.

De este modo, utilizo dos URLs:

  • /[string]: Siendo string un hash (previamente negociado) mediante GET obtengo el fichero y siendo string un identificador mediante POST subo el fichero concreto
  • /slot/[id]: Siendo id un identificador, obtendría un slot asociado a él.

De ambas funcionalidades la única pública es cargar el fichero con el hash, para las otras dos es necesario subir una contraseña previamente establecida.

Simple, ahora echemos un vistazo al script de AppEngine que gestiona la aplicación:

# coding=utf-8
 
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
from django.utils import simplejson
import hashlib
 
from models import *
 
SECRET = "secreto"
 
class getSlot(webapp.RequestHandler):
    def post(self,id):
        if not id.isdigit():
            self.error(400)
 
        if self.request.get("passwd")==SECRET:
            hash = hashlib.sha256().hexdigest()
            Slot(id=int(id),hash=hash).put()
 
            self.response.headers['Content-Type'] = 'text/plain'
            self.response.out.write(simplejson.dumps(dict(slot=hash)))
        else:
            self.error(500)
 
class FileHandler(webapp.RequestHandler):
    def get(self,hash):
        query = Slot.filter("hash = ",hash)
 
        if query.count()==0:
            self.error(404)
            return
 
        # Get the slot
        slot = query.get()
 
 
        file = File.filter("id = ",slot.id);
        # Get the mimetype
        self.response.headers['Content-Type'] = 'image/jpeg'
        self.response.out.write(file.content)
 
        # Remove the slot
        slot.delete()
 
    def post(self,id):
        if not id.isdigit():
            self.error(400)
 
        if self.request.get("passwd")==SECRET:
 
            File(id=id,content=self.request.get("file")).put()
            self.response.headers['Content-Type'] = 'text/plain'
 
            self.response.out.write("ok")
        else:
            self.error(500)
 
 
application = webapp.WSGIApplication(
                                     [(r'/slot/(.+)', getSlot),
                                      (r'/(.+)', FileHandler),
                                     ],
                                     debug=True)
 
def main():
  run_wsgi_app(application)
 
if __name__ == "__main__":
  main()

Este script hace lo descrito anteriormente, por un lado nos permite subir ficheros al servidor, obtener el hash y acceder a dicho hash publicamente una sóla vez.

Las operaciones privadas se verifican mediante una contraseña que tenemos que establecer previamente, podríamos haber elegido cualquier otro método, pero este es el más simple.

Veamos el script que realiza las operaciones de subida;

# coding=utf-8
from poster.encode import multipart_encode
from poster.streaminghttp import register_openers
import urllib2
 
URL = "http://localhost:8080/"
 
def open_slot(id,passwd):
 
    register_openers()
 
    datagen, headers = multipart_encode({"passwd":passwd})
 
    request = urllib2.Request(URL+"slot/"+str(id), datagen, headers)
 
    print urllib2.urlopen(request).read()
 
def upload(id,file,passwd):
 
    register_openers()
 
 
    datagen, headers = multipart_encode({"file": open(file, "rb"),"passwd":passwd})
 
    request = urllib2.Request(URL+str(id), datagen, headers)
 
    print urllib2.urlopen(request).read()

Previamente necesitamos instalar la biblioteca poster de Python.
Este script mediante las funciones upload() nos permite subir un fichero con una ID determinada, el nombre del mismo y la contraseña para identificarnos.
Hecho esto, podremos abrir un slot asociado a él mediante la función open_slot().

Ahora para acceder al fichero, sólo tendremos que ir a la dirección http://[url principal]/[hash]

Resulta interesante este tipo de ejercicios con diferentes tecnologías para ver las utilidades que nos ofrecen.
Finalmente descarté el invento debido a las restricciones que tiene impuestas, por un lado, los campos Blob no pueden tener más de 1MB y aunque se podría aumentar indexando varios (consumiendo un riquísimo tiempo de CPU) los campos del POST no pueden ocupar más de 10MB.

Aún así, resulta interesante este sistema y porque no, su posible aplicación a otros proyectos.

Auto Lorem Ipsum en Django 1.2.3

Tradicionalmente se han utilizado los textos Lorem Ipsum para hacer rellenos en diseños, Django cuenta con un generador automático de textos Lorem Ipsum a partir de la versión 1.0, para activarlo;

  • Añadimos django.contrib.webdesign a las aplicaciones instaladas (INSTALLED_APPS).
  • En la plantilla ponemos {% load webdesign %} para tener acceso a las templatetags

Ahora simplemente poniendo {% lorem %} tendremos un bonito Lorem Ipsum.

Para más info http://docs.djangoproject.com/en/dev/ref/contrib/webdesign/

jQuery Autocomplete + Django + AJAX

Ahora voy a explicar algo técnico pero estético, como utilizar la plugin de jQuery Autocomplete con Django+AJAX, con ella se pueden conseguir efectos como este;

jQuery Autocomplete screenshot
jQuery Autocomplete

Trataré de hacer una consulta por AJAX a una URL cuando tipee 3 o más letras, el código puede ser el siguiente;

$('input').autocomplete({
    minLength:3,
    source: function(req, add){
        $.ajax({
            url: '/ajax/url/',
            async: false,
            dataType:'json',
            type:'POST',
            data: {
                start: function() { return $(this).val(); },
            },
            success: function(data) {
 
                //create array for response objects
                var suggestions = [];
 
                //process response
                $.each(data.items, function(i, val){
                    suggestions.push(val[0]);
                });
 
                //pass array to callback
                add(suggestions);
            }
        });
    }
});

Donde:

  • input: Será el selector del campo de texto al que queremos aplicarle el plugin.
  • minLength: La longitud mínima para hacer la consulta.
  • ‘/ajax/url/’: La URL donde haremos la consulta.
  • start: Va a ser el valor actual del campo de texto, el texto que hemos tipeado y por el que queremos buscar.

La vista podría estar definida como sigue:

def search(request):
 
    if request.method=='GET' or not request.POST.__contains__('start'):
        return HttpResponseForbidden()
 
    # Hacemos la consulta para aquellos elementos que empiecen por start ordenados por nombre
    query = Model.objects.filter(name__istartswith=request.POST['start']).order_by('name')
 
    # Serializamos
    objects = u'{items: [\n'
    for i in query:
        objects += u'{"0":"%s"},\n' % (i.name.replace('"',''))
    objects=objects.strip(",\n");
    objects+=u']}\n'
 
    return HttpResponse(objects,mimetype="text/plain")

También se pueden utilizar serializadores para pasar la información a JSON, pero en este caso he preferido hacerlo a mano.

Para más información visita la página de jQuery.

Here is the attachments of this Post

Deploying django

Ya que hace poco que he deployeado (=publicado) un par de proyectos Django voy a explicar cómo hacerlo, entre ellos el blog experimental http://dug0.com/lachispaadecuada .

El deploy lo he hecho con Apache y mod_wsgi, recomiendo descargar el módulo y compilarlo, mejor que descargarlo de los repositorios, está más actualizado y a me dio problemas el de los repos.

Una vez instalado el módulo y activado, copiamos el proyecto a un directorio donde contendremos los proyectos Django, pueder ser /var/webapp o similares.

Hecho esto creamos en el directorio principal de nuestro proyecto el fichero django.wsgi que contendrá;

import os
import sys
from django.core.management import setup_environ


path = sys.path.append(os.path.abspath(os.path.dirname(__file__)))
if path not in sys.path:
    sys.path.append(path)

import settings
setup_environ(settings)

import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()

Que vale para cualquier proyecto Django. ¿No recuerda a http://dug0.com/django/script-python-con-entorno-de-django?

Hecho esto, añadimos a Apache;

WSGIScriptAlias / /var/webapp/MyDjangoProject/django.wsgi

Y ahora tenemos la parte dinámica, pero también queremos tener acceso a la estática, para lo que añadimos;

Alias /media /var/webapp/MyDjangoProject/media

<Directory /var/webapp/MyDjangoProject/media>
Order deny,allow
Allow from all
</Directory>

Y para tener acceso a los contenidos estáticos del área administrativa, que deberá estar situada en /media/admin. Creamos un enlace simbólico con el nombre admin en el directorio /var/webapp/MyDjangoProject/media al destino /usr/local/lib/python2.6/dist-packages/django/contrib/admin/media. Que es la ruta donde están los contenidos estáticos, al menos Python 2.6+Django 1.2.3 en Ubuntu.

¡Reiniciamos Apache y a volar!

Script Python con entorno de Django

Recientemente tuve la necesidad de crearme un script que utilizará el modelo de una aplicación Django sin necesidad de ejecutarlo como una vista, para ello simplemente creamos un script python normal al que añadimos:

from django.core.management import setup_environ

''' Esta ruta debe de ser la del proyecto, en mi caso como el script
estaba en un directorio de aplicación utilizaba la relativa ../'''
sys.path.append(os.path.abspath(os.path.dirname(__file__)+"../"))

import settings

''' Importamos el modelo que nos interese '''
from app.models import *

Ya podemos utilizar el modelo como cualquier vista.