Django es el entorno de desarrollo web para perfeccionistas con límites de tiempo

Entradas sobre "snippets":

Generar archivos PDF con Django y Pisa

Pisa es un conversor de HTML/XHTML a PDF escrito en Python. Vamos a ver cómo utilizar Pisa en nuestras vistas para convertir nuestras plantillas HTML a PDF.

Lo primero que tenemos que hacer es descargar e instalar Pisa. Pisa requiere ReportLab Toolkit, HTML5lib, pyPdf y PIL (útil: Cómo instalar PIL en Mac OSX). La forma más sencilla de instalar Pisa es mediante easy_install:

easy_install pisa

También podemos bajar el código fuente e instalarlo mediante el siguiente comando (en Linux y Mac Osx puedes necesitar incluir sudo delante):

python setup.py install

Una vez instalado Pisa añadiremos a views.py una función que nos permita convertir nuestras plantillas HTML a PDF. Utilizaremos una vista y una plantilla HTML que la función se encargará de convertir a formato PDF y devolverlo en la petición HTTP. La función recibirá la plantilla HTML renderizada previamente en nuestra vista mediante render_to_string.

Vamos a ver cómo quedaría nuestro views.py para una vista que reciba el id de un hipotético modelo Libro y devuelva un PDF con la información del mismo:

# -*- coding: utf-8 -*-

import ho.pisa as pisa
import cStringIO as StringIO
import cgi
from django.template import RequestContext
from django.template.loader import render_to_string
from django.http import HttpResponse

def generar_pdf(html):
    # Función para generar el archivo PDF y devolverlo mediante HttpResponse
    result = StringIO.StringIO()
    pdf = pisa.pisaDocument(StringIO.StringIO(html.encode("UTF-8")), result)
    if not pdf.err:
        return HttpResponse(result.getvalue(), mimetype='application/pdf')
    return HttpResponse('Error al generar el PDF: %s' % cgi.escape(html))

def libro_pdf(request, id):
    # vista de ejemplo con un hipotético modelo Libro
    libro=get_object_or_404(Libro, id=id)
    html = render_to_string('libro_pdf.html', {'pagesize':'A4', 'libro':libro}, context_instance=RequestContext(request))
    return generar_pdf(html)

Ahora sólo nos falta crear la plantilla libro_pdf.html para mostrar la información del libro:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
</head>    
<body>
    <h1>{{ libro.titulo }}</h1>
    <p>{{ libro.descripcion }}</p>
</body>
</html>

Incluir imágenes en los PDFs generados con Pisa

Si queremos incluir imágenes en nuestro PDF deberemos utilizar su ruta local en la plantilla en vez de su URL. Imaginando que nuestro modelo Libro tuviera un campo imagen de tipo ImageField podríamos hacerlo de la siguiente forma:

<img src="{{ libro.imagen.path }}" />

Las imágenes suelen quedar más grandes de lo normal debido al mapeo de píxeles a puntos que hace Pisa. Para que las imágenes salgan con un tamaño adecuado conviene incluirles el estilo CSS zoom:

<img src="{{ libro.imagen.path }}" style="zoom: 80%;" />

Etiquetas HTML especiales de Pisa

Hay varias etiquetas especiales que aporta Pisa y que podemos utilizar en nuestras plantillas. Las más útiles son: pdf:pagenumber y pdf:nextpage. La primera nos permite incluir número de página en cada una de las páginas del documento PDF y la segunda nos permite definir dónde comienza otra página. Su uso en las plantillas HTML es muy sencillo:

<pdf:pagenumber>
<pdf:nextpage>

Como véis, las posibilidades son múltiples a la hora de generar PDFs dinámicamente con Python. Aquí tenéis un ejemplo de PDF que se genera dinámicamente mediante este método: Receta de dátiles con bacon

Publicado por Antonio Melé el Miércoles 4 d Agosto d 2010 | 5 comentarios | Categorías: plantillas, snippets, tutorial

Formularios con Django sin SPAM

¿Quieres luchar contra el SPAM en tus formularios sin tener que modificarlos? Una forma sencilla de evitar gran parte del SPAM automático son los snippets AntiSpamForm y AntiSpamModelForm. Están basados en los formularios anti-spam que utiliza la aplicación django.contrib.comments.

Utilizarlos es tan sencillo como guardarlos en nuestro proyecto (por ejemplo en un archivo llamado antispam_forms.py) y usarlos en la definición de nuestros formularios:

from antispam_forms import AntiSpamForm

class MiFormulario(AntiSpamForm):
    # campos del formulario...

AntiSpamForm y AntiSpamModelForm utilizan tres métodos para prevenir el SPAM:

Campo oculto timestamp

Un campo oculto timestamp que adquiere la fecha actual cuando se renderiza el formulario y que se controla en el envío del formulario para evitar envíos excesivamente rápidos (menos de 5 segundos) que generalmente son automáticos. También evita envíos retrasados (más de 2 horas) que pueden ser realizados por bots que primero guardan el formulario y lo tratan de enviar en algún otro momento.

Hash de seguridad

Un campo oculto de hash de seguridad que se genera a partir del timestamp y del setting SECRET_KEY. El hash de seguridad enviado se compara con el hash de seguridad esperado cuando se envía el formulario para rechazar envíos que no incluyan el hash adecuado.

Campo Honeypot

Un campo honeypot que utiliza un CSS display:none; para evitar ser mostrado en el navegador. Los usuarios no lo verán pero la mayoría de spambots tratarán de rellenarlo. El campo honeypot no será validado correctamente si contiene alguna información.

Publicado por Antonio Melé el Jueves 8 d Julio d 2010 | 1 comentario | Categorías: forms, snippets, spam

Subdominios con Django

En ocasiones nos interesa trabajar con subdominios en nuestros proyectos Django. Para ello podemos utilizar un sencillo middleware para subdominios que podemos encontrar en djangosnippets. Basta con guardarlo en nuestro proyecto e incluirlo en la lista MIDDLEWARE_CLASSES de nuestro settings.py. De esta forma tendremos el subdominio de la petición en la variable subdomain del objeto request.

El problema al trabajar con subdominios es que las sesiones iniciadas en un subdominio no se mantienen cuando se cambia a otro. Para poder mantener activas las sesiones entre subdominios tenemos que añadir la siguiente línea al settings.py de nuestro proyecto:

SESSION_COOKIE_DOMAIN = '.mi-dominio.com'

De esta forma la cookie de sesión que se almacena en el navegador valdrá para todos los subdominios.

Subdominios para usuarios

Si por ejemplo queremos dar un subdominio a cada usuario de nuestro proyecto podemos modificar el middleware para que busque el usuario adecuado y lo incluya en el objeto request. El middleware quedaría de la siguiente manera:

from django.http import Http404
from django.core.exceptions import ObjectDoesNotExist
from django.contrib.auth.models import User

class SubdomainMiddleware:
    """ Make the subdomain publicly available to classes """

    def process_request(self, request):
        domain_parts = request.get_host().split('.')
        if (len(domain_parts) > 2):
            subdomain = domain_parts[0]
            if (subdomain.lower() == 'www'):
                subdomain = ''
            domain = '.'.join(domain_parts[1:])
        else:
            subdomain = ''
            domain = request.META['HTTP_HOST']

        request.subdomain = subdomain
        request.domain = domain

        if subdomain != 'www' and subdomain != '':
            # Buscamos el usuario del subdominio
            try:
                request.usuario_subdominio = User.objects.filter(username=subdomain)
            except ObjectDoesNotExist:
                raise Http404

De esta forma el usuario correspondiente al subdominio que se visita está disponible en la variable usuario_subdominio del objeto request. Si utilizamos variables de contexto y tenemos 'django.core.context_processors.request' en la lista de procesadores de contexto (setting TEMPLATE_CONTEXT_PROCESSORS) podemos obtener el usuario actual en nuestras plantillas mediante:

{{ request.usuario_subdominio }}

Publicado por Antonio Melé el Domingo 2 d Agosto d 2009 | 6 comentarios | Categorías: middleware, snippets, trucos, urls

Templatetag {% if %} con más comparaciones

Este snippet reemplaza la funcionalidad del templatetag {% if %} permitiendo realizar comparaciones con operadores >, <, >=, <=, != además de las comparaciones que permite hacer {% if %} por defecto. Por ejemplo una comparación con el operador mayor-igual-que para mostrar algo si hay más de 5 artículos sería:

{% if articulos|length >= 5 %} ... {% endif %}

Para utilizarlo en nuestro proyecto nos basta con seguir los siguientes pasos:

  1. Descargamos smart_if desde djangosnippets.
  2. Lo guardamos como smart_if.py en la carpeta templatetags de alguna de las aplicaciones de nuestro proyecto.
  3. Añadimos el tag {% load smartif %} en las plantillas que queramos utilizarlo y listo.

También hay otro snippet, elif for smart if tag, que permite utilizar el tag {% elif %} para realizar otras comparaciones dentro de un bucle if con nuestro tag smart_if. Este snippet incluye sólo la función smart_if para que la reemplacemos en nuestro smart_if.py original.

Publicado por Antonio Melé el Domingo 19 d Julio d 2009 | 2 comentarios | Categorías: plantillas, snippets, templatetags, trucos