Comunidad de diseño web y desarrollo en internet online

Crear PDF en Django y Virtualenv (II)

Luego de ver toda la instalación de las librerías y el wrapper de Django (Django-wkhtmltopdf) a través de pip, ahora veremos la configuración del mismo. Lo primero que debemos hacer es dirigirnos al settings.py del proyecto y configurar los valores iniciales de wkhtmltopdf como lo es la consola que usará:

Código :

WKHTMLTOPDF_CMD = “/direccion/al/ejecutable/wkhtmltopdf”


En mi caso quedo de la siguiente manera:
Si en Linux no saben donde buscar, entonces el comando whereis es tu amigo ;)


Código :

WKHTMLTOPDF_CMD = “/usr/bin/wkhtmltopdf”


También hay que configurar las opciones de ejecución de la consola en el settings.py, en este caso, un diccionario. Veamos la configuración más simple:

settings.py

Código :

WKHTMLTOPDF_CMD_OPTIONS = {
   'quiet':True,
}

y por último en las INSTALLED_APPS agregar wkhtmltopdf:

settings.py

Código :

INSTALLED_APPS =(
   …,
   'wkhtmltopdf',
   ….,
)


Luego veremos qué es lo que queremos convertir a pdf, en mi caso seguiré un proyecto el cual tengo 'forkeado' en mi (github). El proyecto trata sobre una tienda online, así que lo que haré es convertir el carrito de compras en una compra real, y hacer una factura en PDF sobre lo que compró el cliente... Quedando así, comienzo por la view.

ventas/views.py

Código :

from wkhtmltopdf.views import PDFTemplateResponse
….
def to_pdf(request):
   def to_pdf(request):
       c_compra = request.session["carrito_de_compra"] #obtengo el carrito de compras de la session
   vtotal = 0 # valor donde acumulare el monto total a pagar por el usuario
   f = Factura.objects.count() + 1 # obtener numero de facturas existentes y le sumo uno, como un preview del n de la fact.
       for key,value in c_compra.items(): # el carrito es un diccionario, lo recorro key= nombre del prod. 
# el value es una lista donde la pos 0 es la cantidad y la pos 1 el producto
           p_pro = value[1].precio # obtengo el valor del producto (se corre esta identacion ojo) 
      iva = value[1].iva # obtengo el iva aplicado al producto
      p_total = (float(p_pro)*(1+iva))*float(value[0]) # obtengo el valor total del producto x iva x cantidad
      value.append(p_total) # agrego al final de la lista (value) del diccionario el valor total
      vtotal += p_total # sumatoria de los valores totales
       fecha = date.today() # obtengo la fecha de hoy
   return PDFTemplateResponse(request,"ventas/factura.html",               {"vtotal":vtotal,"productos":c_compra,"fecha":fecha,"nf":f})
....

Ojo, si se corre una linea, queda sin identación :S :cry:
y hacer la respectiva llamada a la view desde el url.py

ventas/url.py

Código :

….
url(r'^topdf/$','to_pdf',name= "to_pdf"),
….


Recuerda que en tu template, debes tener definido una etiqueta meta de la siguiente manera...

Código :

<meta http-equiv="Content-Type" content="text/html; charset=utf-8">


En mi caso, tengo un template de la siguiente manera...

Aquí primero colocaré el style que está dentro de mi html (recordemos que es mejor que esté todo dentro del html que renderizaremos para evitar costos de tiempo, en que la librería haga peticiones por los css).

ventas/facturapdf.html (sólo etiqueta style)

Código :

<style>
        #page-wrap { width: 80%;  font: 14px/1.4 Georgia, serif; }

#page-wrap p { border: 0; font: 14px Georgia, Serif; overflow: hidden; resize: none; }
#page-wrap table { border-collapse: collapse; }
#page-wrap table td,#page-wrap table th { border: 1px solid black; padding: 5px; }

#header {  width: 40%; margin: 20px 0; background: #222; text-align: center; color: white; font: bold 15px Helvetica, Sans-Serif; text-decoration: uppercase; }

#address { width: 250px; height: 150px; float: left; }
#customer { overflow: hidden; }

#logo { text-align: right; float: right; position: relative; margin-top: 25px; border: 1px solid #fff; max-width: 540px; max-height: 100px; overflow: hidden; }
#header{ width:100%}

#meta { margin-top: 1px; width: 300px; float: right; }
#meta td { text-align: right;  }
#meta td.meta-head { text-align: left; background: #eee; }
#meta td textarea { width: 100%; height: 20px; text-align: right; }

#items { clear: both; width: 100%; margin: 30px 0 0 0; border: 1px solid black; }
#items th { background: #eee; }
#items textarea { width: 80px; height: 50px; }
#items tr.item-row td { border: 0; vertical-align: top; }
#items td.description { width: 300px; }
#items td.item-name { width: 175px; }
#items td.description textarea, #items td.item-name textarea { width: 100%; }
#items td.total-line { border-right: 0; text-align: right; }
#items td.total-value { border-left: 0; padding: 10px; }
#items td.total-value textarea { height: 20px; background: none; }
#items td.balance { background: #eee; }
#items td.blank { border: 0; }

#terms { text-align: center; margin: 20px 0 0 0; }
#terms h5 { text-transform: uppercase; font: 13px Helvetica, Sans-Serif; border-bottom: 1px solid black; padding: 0 0 8px 0; margin: 0 0 8px 0; }
#terms textarea { width: 100%; text-align: center;}

#page-wrap textarea:hover, #page-wrap textarea:focus, #items td.total-value textarea:hover, #items td.total-value textarea:focus, .delete:hover { background-color:#EEFF88; }

#image{
    width:100px;
}
    </style>


y este es mi html:

ventas/facturapdf.html(solo html)

Código :

<html>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<body>
   <div id="page-wrap">
      <p id="header">Tienda Online</p>
      <div id="identity">
            <p id="address">{{ user.nombre }} {{ user.apellidos }}<br>
                            {{ user.identificacion }}<br>
                            {{ user.direccion }}<br>
                            Telefono: {{ user.telefono }}<br>
                            Ciudad : {{ user.ciudad }}
            </p>
            <div id="logo">
              <img id="image" src="/media/images/logo.jpg" alt="logo" />
            </div>
      </div>
      <div style="clear:both"></div>
      <div id="customer">
            <table id="meta">
                <tr>
                    <td class="meta-head">Pre-Factura #</td>
                    <td><p>{{ nf }}</p></td>
                </tr>
                <tr>
                    <td class="meta-head">Fecha</td>
                    <td><p id="date">{{ fecha }}</p></td>
                </tr>
                <tr>
                    <td class="meta-head">Monto Total</td>
                    <td><div class="due">${{ vtotal }}</div></td>
                </tr>
            </table>
      </div>
      <table id="items">
        <tr>
            <th>Articulo</th>
            <th>Descripcion</th>
            <th>Costo/U.</th>
            <th>Cant.</th>
                      <th>IVA</th>
            <th>Precio</th>
        </tr>
        {% for key,value in productos.items %}
        <tr class="item-row">
            <td class="item-name"><div class="delete-wpr"><p>{{ value.1.id }}</p></div></td>
            <td class="description"><p>{{ key }}</p></td>
            <td><p class="cost">${{ value.1.precio }}</p></td>
            <td><p class="qty">{{ value.0 }}</p></td>
                      <td><p>{{ value.1.iva }}</p></td>
            <td><span class="price">${{ value.2 }}</span></td>
        </tr>
          {% endfor %}
        <tr>
            <td colspan="3" class="blank"> </td>
            <td colspan="2" class="total-line">Total</td>
            <td class="total-value"><div id="total">${{ vtotal }}</div></td>
        </tr>
      </table>
      <div id="terms">
        <h5>Terminos</h5>
        <p>El total del envio sera cubierto completamente por el cliente. 15 dias habiles para la devolucion del producto.</p>
      </div>
   </div>
</body>
</html>


Este ejemplo de factura en html, lo obtuve de CSS-Tricks a ellos el mérito del html de la factura, yo sólo lo adecué e hice el proceso para convertirlo a pdf. Les dejo aquí una imagen del resultado del renderizado:



Y eso es todo, espero que les guste este post. Cualquier cosa estaré respondiendo los comentarios :) muchísimas gracias por leer tan largo post :D jeje ;)

¿Sabes SQL? ¿No-SQL? Aprende MySQL, PostgreSQL, MongoDB, Redis y más con el Curso Profesional de Bases de Datos que empieza el martes, en vivo.

Publica tu comentario

El autor de este artículo ha cerrado los comentarios. Si tienes preguntas o comentarios, puedes hacerlos en el foro

Entra al foro y participa en la discusión

o puedes...

¿Estás registrado en Cristalab y quieres
publicar tu URL y avatar?

¿No estás registrado aún pero quieres hacerlo antes de publicar tu comentario?

Registrate