El libro de Django 1.0

11.2. Producción de CSV

CSV es un formato de datos sencillo que suele ser usada por programas de hojas de cálculo. Básicamente es una serie de filas en una tabla, cada celda en la fila está separada por comas (CSV significa comma-separated values). Por ejemplo, aquí tienes una lista de pasajeros "problemáticos" en líneas aéreas en formato CSV:

Year,Unruly Airline Passengers
    1995,146
    1996,184
    1997,235
    1998,200
    1999,226
    2000,251
    2001,299
    2002,273
    2003,281
    2004,304
    2005,203

Nota El listado precedente contiene números reales; cortesía de la Administración Federal de Aviación (FAA) de E.E.U.U. Vea http://www.faa.gov/data_statistics/passengers_cargo/unruly_passengers/.

Aunque CSV parezca simple, no es un formato que ha sido definido formalmente. Diferentes programas producen y consumen diferentes variantes de CSV, haciendo un poco complicado usarlo. Afortunadamente, Python incluye una librería estándar para CSV, csv, que es bastante robusta.

Debido a que el módulo csv opera sobre objetos similares a ficheros, es muy fácil usar un HttpResponse en lugar de un fichero:

import csv
from django.http import HttpResponse

# Número de pasajeros problematicos por año entre 1995 - 2005.
# En una aplicación real esto vendría desde una base de datos o cualquier
# otro medio de almacenamiento.
UNRULY_PASSENGERS = [146,184,235,200,226,251,299,273,281,304,203]

def unruly_passengers_csv(request):
    # Creamos el objeto Httpresponse con la cabecera CSV apropiada.
    response = HttpResponse(mimetype='text/csv')
    response['Content-Disposition'] = 'attachment; filename=unruly.csv'

    # Creamos un escritor CSV usando a HttpResponse como "fichero"
    writer = csv.writer(response)
    writer.writerow(['Year', 'Unruly Airline Passengers'])
    for (year, num) in zip(range(1995, 2006), UNRULY_PASSENGERS):
        writer.writerow([year, num])

    return response

El código y los comentarios deberían ser bastante claros, pero hay unas pocas cosas que merecen mención especial:

  • Se le da a la respuesta el tipo MIME text/csv (en lugar del tipo predeterminado text/html). Esto le dice a los navegadores que el documento es un fichero CSV.
  • La respuesta obtiene una cabecera Content-Disposition adicional, la cual contiene el nombre del fichero CSV. Esta cabecera (bueno, la parte "adjunta") le indicará al navegador que solicite la ubicación donde guardará el fichero (en lugar de simplemente mostrarlo). El nombre de fichero es arbitrario; llámalo como quieras. Será usado por los navegadores en el cuadro de diálogo "Guardar como..."
  • Usar el API de generación de CSV es sencillo: basta pasar response como primer argumento a csv.writer. La función csv.writer espera un objeto de tipo fichero, y los de tipo HttpResponse se ajustan.
  • Por cada fila en el fichero CSV, invocamos a writer.writerow, pasándole un objeto iterable como una lista o una tupla.
  • El módulo CSV se encarga de poner comillas por ti, así que no tendrás que preocuparte por escapar caracteres en las cadenas que tengan comillas o comas en su interior. Limítate a pasar la información a writerow(), que hará lo correcto.

Este es el patrón general que usarás siempre que necesites retornar contenido no HTML: crear un objeto HttpResponse de respuesta (con un tipo MIME especial), pasárselo a algo que espera un fichero, y luego devolver la respuesta.

Veamos unos cuántos ejemplos más.