aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJonathan Liuti <liuti.john@gmail.com>2015-11-25 13:29:18 +0100
committerJonathan Liuti <liuti.john@gmail.com>2015-12-01 07:50:46 +0100
commitfbf54be1da1cc90490336a57df6678a2f833a5f7 (patch)
treed8a329fb7c2ce5ae05221dffe411493b4f737e8a
parent1a849e356d1c2487337c62768f16ea80883c9bf1 (diff)
downloaddjango-wkhtmltopdf-fbf54be1da1cc90490336a57df6678a2f833a5f7.tar.gz
django-wkhtmltopdf-fbf54be1da1cc90490336a57df6678a2f833a5f7.tar.bz2
django-wkhtmltopdf-fbf54be1da1cc90490336a57df6678a2f833a5f7.zip
Extracted logic from views - fixed tests.
The logic was coupled with the views which made things difficult to reuse if you wanted to use the pdf generation somehwere else than in a view. With this patch, the logic has been moved to `utils.py` and should be more easy to reuse. Tests have been adapted and made compatible with django > 1.7
-rw-r--r--.travis.yml7
-rw-r--r--wkhtmltopdf/tests/tests.py30
-rw-r--r--wkhtmltopdf/utils.py87
-rw-r--r--wkhtmltopdf/views.py92
4 files changed, 116 insertions, 100 deletions
diff --git a/.travis.yml b/.travis.yml
index 8dae083..0c64cd5 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -8,8 +8,15 @@ env:
- DJANGO="Django>=1.4,<1.5"
- DJANGO="Django>=1.5,<1.6"
- DJANGO="Django>=1.6,<1.7"
+ - DJANGO="Django>=1.7,<1.8"
+ - DJANGO="Django>=1.8,<1.9"
+
matrix:
exclude:
+ - python: "2.6"
+ env: DJANGO="Django>=1.7,<1.8"
+ - python: "2.6"
+ env: DJANGO="Django>=1.8,<1.9"
- python: "3.3"
env: DJANGO="Django>=1.4,<1.5"
- python: "3.4"
diff --git a/wkhtmltopdf/tests/tests.py b/wkhtmltopdf/tests/tests.py
index 48a8b19..c15d88f 100644
--- a/wkhtmltopdf/tests/tests.py
+++ b/wkhtmltopdf/tests/tests.py
@@ -6,6 +6,7 @@ import os
import sys
from django.conf import settings
+from django.template import loader, RequestContext
from django.test import TestCase
from django.test.client import RequestFactory
from django.utils import six
@@ -13,7 +14,7 @@ from django.utils.encoding import smart_str
from wkhtmltopdf.subprocess import CalledProcessError
from wkhtmltopdf.utils import (_options_to_args, make_absolute_paths,
- wkhtmltopdf)
+ wkhtmltopdf, render_to_temporary_file)
from wkhtmltopdf.views import PDFResponse, PDFTemplateView, PDFTemplateResponse
@@ -50,10 +51,8 @@ class TestUtils(TestCase):
def test_wkhtmltopdf(self):
"""Should run wkhtmltopdf to generate a PDF"""
title = 'A test template.'
- response = PDFTemplateResponse(self.factory.get('/'),
- None,
- context={'title': title})
- temp_file = response.render_to_temporary_file('sample.html')
+ template = loader.get_template('sample.html')
+ temp_file = render_to_temporary_file(template, context={'title': title})
try:
# Standard call
pdf_output = wkhtmltopdf(pages=[temp_file.name])
@@ -76,23 +75,20 @@ class TestUtils(TestCase):
def test_wkhtmltopdf_with_unicode_content(self):
"""A wkhtmltopdf call should render unicode content properly"""
title = u'♥'
- response = PDFTemplateResponse(self.factory.get('/'),
- None,
- context={'title': title})
- temp_file = response.render_to_temporary_file('unicode.html')
+ template = loader.get_template('unicode.html')
+ temp_file = render_to_temporary_file(template, context={'title': title})
try:
pdf_output = wkhtmltopdf(pages=[temp_file.name])
self.assertTrue(pdf_output.startswith(b'%PDF'), pdf_output)
finally:
temp_file.close()
- def test_PDFTemplateResponse_render_to_temporary_file(self):
+ def test_render_to_temporary_file(self):
"""Should render a template to a temporary file."""
title = 'A test template.'
- response = PDFTemplateResponse(self.factory.get('/'),
- None,
- context={'title': title})
- temp_file = response.render_to_temporary_file('sample.html')
+
+ template = loader.get_template('sample.html')
+ temp_file = render_to_temporary_file(template, context={'title': title})
temp_file.seek(0)
saved_content = smart_str(temp_file.read())
self.assertTrue(title in saved_content)
@@ -179,7 +175,8 @@ class TestViews(TestCase):
self.assertFalse(response.has_header('Content-Disposition'))
# Render to temporary file
- tempfile = response.render_to_temporary_file(self.template)
+ template = loader.get_template(self.template)
+ tempfile = render_to_temporary_file(template, context=context)
tempfile.seek(0)
html_content = smart_str(tempfile.read())
self.assertTrue(html_content.startswith('<html>'))
@@ -205,7 +202,8 @@ class TestViews(TestCase):
self.assertEqual(response.cmd_options, cmd_options)
self.assertTrue(response.has_header('Content-Disposition'))
- tempfile = response.render_to_temporary_file(self.footer_template)
+ footer_template = loader.get_template(self.footer_template)
+ tempfile = render_to_temporary_file(footer_template, context=RequestContext(request, context))
tempfile.seek(0)
footer_content = smart_str(tempfile.read())
footer_content = make_absolute_paths(footer_content)
diff --git a/wkhtmltopdf/utils.py b/wkhtmltopdf/utils.py
index f9d7ac1..87b214c 100644
--- a/wkhtmltopdf/utils.py
+++ b/wkhtmltopdf/utils.py
@@ -6,6 +6,10 @@ import os
import re
import sys
import shlex
+from tempfile import NamedTemporaryFile
+
+from django.template.context import Context, RequestContext
+from django.utils.encoding import smart_text
try:
from urllib.request import pathname2url
@@ -103,6 +107,59 @@ def wkhtmltopdf(pages, output=None, **kwargs):
return check_output(ck_args, **ck_kwargs)
+def convert_to_pdf(filename, header_filename=None, footer_filename=None, cmd_options=None):
+ # Clobber header_html and footer_html only if filenames are
+ # provided. These keys may be in self.cmd_options as hardcoded
+ # static files.
+ cmd_options = cmd_options if cmd_options else {}
+
+ if header_filename is not None:
+ cmd_options['header_html'] = header_filename
+ if footer_filename is not None:
+ cmd_options['footer_html'] = footer_filename
+ return wkhtmltopdf(pages=[filename], **cmd_options)
+
+def render_pdf_from_template(input_template, header_template, footer_template, context, cmd_options=None):
+ debug = getattr(settings, 'WKHTMLTOPDF_DEBUG', settings.DEBUG)
+ cmd_options = cmd_options if cmd_options else {}
+
+ input_file = header_file = footer_file = None
+ header_filename = footer_filename = None
+
+ try:
+ input_file = render_to_temporary_file(
+ template=input_template,
+ context=context,
+ prefix='wkhtmltopdf', suffix='.html',
+ delete=(not debug)
+ )
+
+ if header_template:
+ header_file = render_to_temporary_file(
+ template=header_template,
+ context=context,
+ prefix='wkhtmltopdf', suffix='.html',
+ delete=(not debug)
+ )
+ header_filename = header_file.name
+
+ if footer_template:
+ footer_file = render_to_temporary_file(
+ template=footer_template,
+ context=context,
+ prefix='wkhtmltopdf', suffix='.html',
+ delete=(not debug)
+ )
+ footer_filename = footer_file.name
+
+ return convert_to_pdf(filename=input_file.name,
+ header_filename=header_filename,
+ footer_filename=footer_filename,
+ cmd_options=cmd_options)
+ finally:
+ # Clean up temporary files
+ for f in filter(None, (input_file, header_file, footer_file)):
+ f.close()
def content_disposition_filename(filename):
"""
@@ -144,7 +201,6 @@ def pathname2fileurl(pathname):
def make_absolute_paths(content):
"""Convert all MEDIA files into a file://URL paths in order to
correctly get it displayed in PDFs."""
-
overrides = [
{
'root': settings.MEDIA_ROOT,
@@ -173,3 +229,32 @@ def make_absolute_paths(content):
occur[len(x['url']):])
return content
+
+def render_to_temporary_file(template, context, mode='w+b', bufsize=-1,
+ suffix='.html', prefix='tmp', dir=None,
+ delete=True):
+ # make sure the context is a context object
+ if not isinstance(context, (Context, RequestContext)):
+ context = Context(context)
+
+ content = smart_text(template.render(context))
+ content = make_absolute_paths(content)
+
+ try:
+ # Python3 has 'buffering' arg instead of 'bufsize'
+ tempfile = NamedTemporaryFile(mode=mode, buffering=bufsize,
+ suffix=suffix, prefix=prefix,
+ dir=dir, delete=delete)
+ except TypeError:
+ tempfile = NamedTemporaryFile(mode=mode, bufsize=bufsize,
+ suffix=suffix, prefix=prefix,
+ dir=dir, delete=delete)
+
+ try:
+ tempfile.write(content.encode('utf-8'))
+ tempfile.flush()
+ return tempfile
+ except:
+ # Clean-up tempfile if an Exception is raised.
+ tempfile.close()
+ raise
diff --git a/wkhtmltopdf/views.py b/wkhtmltopdf/views.py
index d5f189a..f26c9ae 100644
--- a/wkhtmltopdf/views.py
+++ b/wkhtmltopdf/views.py
@@ -1,15 +1,10 @@
from __future__ import absolute_import
-from tempfile import NamedTemporaryFile
-
-from django.conf import settings
from django.http import HttpResponse
from django.template.response import TemplateResponse
from django.views.generic import TemplateView
-from django.utils.encoding import smart_text
-from .utils import (content_disposition_filename, make_absolute_paths,
- wkhtmltopdf)
+from .utils import (content_disposition_filename, render_pdf_from_template)
class PDFResponse(HttpResponse):
@@ -65,47 +60,6 @@ class PDFTemplateResponse(TemplateResponse, PDFResponse):
cmd_options = {}
self.cmd_options = cmd_options
- def render_to_temporary_file(self, template_name, mode='w+b', bufsize=-1,
- suffix='.html', prefix='tmp', dir=None,
- delete=True):
- template = self.resolve_template(template_name)
-
- context = self.resolve_context(self.context_data)
-
- content = smart_text(template.render(context))
- content = make_absolute_paths(content)
-
- try:
- # Python3 has 'buffering' arg instead of 'bufsize'
- tempfile = NamedTemporaryFile(mode=mode, buffering=bufsize,
- suffix=suffix, prefix=prefix,
- dir=dir, delete=delete)
- except TypeError:
- tempfile = NamedTemporaryFile(mode=mode, bufsize=bufsize,
- suffix=suffix, prefix=prefix,
- dir=dir, delete=delete)
-
- try:
- tempfile.write(content.encode('utf-8'))
- tempfile.flush()
- return tempfile
- except:
- # Clean-up tempfile if an Exception is raised.
- tempfile.close()
- raise
-
- def convert_to_pdf(self, filename,
- header_filename=None, footer_filename=None):
- cmd_options = self.cmd_options.copy()
- # Clobber header_html and footer_html only if filenames are
- # provided. These keys may be in self.cmd_options as hardcoded
- # static files.
- if header_filename is not None:
- cmd_options['header_html'] = header_filename
- if footer_filename is not None:
- cmd_options['footer_html'] = footer_filename
- return wkhtmltopdf(pages=[filename], **cmd_options)
-
@property
def rendered_content(self):
"""Returns the freshly rendered content for the template and context
@@ -115,42 +69,14 @@ class PDFTemplateResponse(TemplateResponse, PDFResponse):
response content, you must either call render(), or set the
content explicitly using the value of this property.
"""
- debug = getattr(settings, 'WKHTMLTOPDF_DEBUG', settings.DEBUG)
-
- input_file = header_file = footer_file = None
- header_filename = footer_filename = None
-
- try:
- input_file = self.render_to_temporary_file(
- template_name=self.template_name,
- prefix='wkhtmltopdf', suffix='.html',
- delete=(not debug)
- )
-
- if self.header_template:
- header_file = self.render_to_temporary_file(
- template_name=self.header_template,
- prefix='wkhtmltopdf', suffix='.html',
- delete=(not debug)
- )
- header_filename = header_file.name
-
- if self.footer_template:
- footer_file = self.render_to_temporary_file(
- template_name=self.footer_template,
- prefix='wkhtmltopdf', suffix='.html',
- delete=(not debug)
- )
- footer_filename = footer_file.name
-
- return self.convert_to_pdf(filename=input_file.name,
- header_filename=header_filename,
- footer_filename=footer_filename)
- finally:
- # Clean up temporary files
- for f in filter(None, (input_file, header_file, footer_file)):
- f.close()
-
+ cmd_options = self.cmd_options.copy()
+ return render_pdf_from_template(
+ self.resolve_template(self.template_name),
+ self.resolve_template(self.header_template),
+ self.resolve_template(self.footer_template),
+ context=self.resolve_context(self.context_data),
+ cmd_options=cmd_options
+ )
class PDFTemplateView(TemplateView):
"""Class-based view for HTML templates rendered to PDF."""