diff options
-rw-r--r-- | wkhtmltopdf/tests.py | 52 | ||||
-rw-r--r-- | wkhtmltopdf/utils.py | 6 | ||||
-rw-r--r-- | wkhtmltopdf/views.py | 63 |
3 files changed, 98 insertions, 23 deletions
diff --git a/wkhtmltopdf/tests.py b/wkhtmltopdf/tests.py index 1802440..764c7f2 100644 --- a/wkhtmltopdf/tests.py +++ b/wkhtmltopdf/tests.py @@ -117,7 +117,13 @@ class TestViews(TestCase): with override_settings( MEDIA_URL='/media/', + MEDIA_ROOT='/tmp/media', STATIC_URL='/static/', + STATIC_ROOT='/tmp/static', + TEMPLATE_CONTEXT_PROCESSORS=[ + 'django.core.context_processors.media', + 'django.core.context_processors.static', + ], TEMPLATE_LOADERS=['django.template.loaders.filesystem.Loader'], TEMPLATE_DIRS=[os.path.join(os.path.dirname(__file__), '_testproject', 'templates')], @@ -151,7 +157,7 @@ class TestViews(TestCase): self.assertTrue(pdf_content.startswith('%PDF-')) self.assertTrue(pdf_content.endswith('%%EOF\n')) - # Header + # Footer filename = 'output.pdf' footer_template = 'footer.html' cmd_options = {'title': 'Test PDF'} @@ -170,20 +176,53 @@ class TestViews(TestCase): tempfile = response.render_to_temporary_file(footer_template) tempfile.seek(0) footer_content = tempfile.read() - self.assertTrue('MEDIA_URL = {}'.format(settings.MEDIA_URL) - in footer_content) - self.assertTrue('STATIC_URL = {}'.format(settings.STATIC_URL) - in footer_content) + + media_url = 'MEDIA_URL = file://{}/'.format(settings.MEDIA_ROOT) + self.assertTrue( + media_url in footer_content, + "{!r} not in {!r}".format(media_url, footer_content) + ) + + static_url = 'STATIC_URL = file://{}/'.format(settings.STATIC_ROOT) + self.assertTrue( + static_url in footer_content, + "{!r} not in {!r}".format(static_url, footer_content) + ) pdf_content = response.rendered_content self.assertTrue('\0'.join('{title}'.format(**cmd_options)) in pdf_content) + # Override settings + response = PDFTemplateResponse(request=request, + template=template, + context=context, + filename=filename, + footer_template=footer_template, + cmd_options=cmd_options, + override_settings={ + 'STATIC_URL': 'file:///tmp/s/' + }) + tempfile = response.render_to_temporary_file(footer_template) + tempfile.seek(0) + footer_content = tempfile.read() + + static_url = 'STATIC_URL = {}'.format('file:///tmp/s/') + self.assertTrue( + static_url in footer_content, + "{!r} not in {!r}".format(static_url, footer_content) + ) + self.assertEqual(settings.STATIC_URL, '/static/') + def test_pdf_template_view(self): """Test PDFTemplateView.""" with override_settings( MEDIA_URL='/media/', STATIC_URL='/static/', + TEMPLATE_CONTEXT_PROCESSORS=[ + 'django.core.context_processors.media', + 'django.core.context_processors.static', + ], TEMPLATE_LOADERS=['django.template.loaders.filesystem.Loader'], TEMPLATE_DIRS=[os.path.join(os.path.dirname(__file__), '_testproject', 'templates')], @@ -193,7 +232,8 @@ class TestViews(TestCase): template = 'sample.html' filename = 'output.pdf' view = PDFTemplateView.as_view(filename=filename, - template_name=template) + template_name=template, + footer_template='footer.html') # As PDF request = RequestFactory().get('/') diff --git a/wkhtmltopdf/utils.py b/wkhtmltopdf/utils.py index 03a5f82..69da74f 100644 --- a/wkhtmltopdf/utils.py +++ b/wkhtmltopdf/utils.py @@ -5,6 +5,7 @@ from itertools import chain from os import fdopen import sys from tempfile import mkstemp +import urllib import warnings from django.conf import settings @@ -124,6 +125,11 @@ def http_quote(string): return '"{!s}"'.format(string.replace('\\', '\\\\').replace('"', '\\"')) +def pathname2fileurl(pathname): + """Returns a file:// URL for pathname. Handles OS-specific conversions.""" + return 'file://' + urllib.pathname2url(pathname) + + try: # From Django 1.4 from django.conf import override_settings diff --git a/wkhtmltopdf/views.py b/wkhtmltopdf/views.py index c337b6c..4bceb27 100644 --- a/wkhtmltopdf/views.py +++ b/wkhtmltopdf/views.py @@ -5,12 +5,12 @@ from tempfile import NamedTemporaryFile import warnings from django.conf import settings -from django.contrib.sites.models import Site from django.http import HttpResponse from django.template.response import TemplateResponse from django.views.generic import TemplateView -from .utils import (content_disposition_filename, wkhtmltopdf) +from .utils import (content_disposition_filename, override_settings, + pathname2fileurl, wkhtmltopdf) class PDFResponse(HttpResponse): @@ -51,7 +51,8 @@ class PDFTemplateResponse(TemplateResponse, PDFResponse): def __init__(self, request, template, context=None, mimetype=None, status=None, content_type=None, current_app=None, filename=None, header_template=None, footer_template=None, - cmd_options=None, *args, **kwargs): + cmd_options=None, override_settings=None, + *args, **kwargs): super(PDFTemplateResponse, self).__init__(request=request, template=template, @@ -70,12 +71,23 @@ class PDFTemplateResponse(TemplateResponse, PDFResponse): cmd_options = {} self.cmd_options = cmd_options + self.override_settings = override_settings + def render_to_temporary_file(self, template_name, mode='w+b', bufsize=-1, suffix='', prefix='tmp', dir=None, delete=True): template = self.resolve_template(template_name) - context = self.resolve_context(self.context_data) - content = template.render(context) + + # Since many things require a sensible settings.MEDIA_URL and + # settings.STATIC_URL, including TEMPLATE_CONTEXT_PROCESSORS; + # the settings themselves need to be overridden when rendering. + # + # This allows django-wkhtmltopdf to play nicely with the + # staticfiles app, for instance. + with override_settings(**self.get_override_settings()): + context = self.resolve_context(self.context_data) + content = template.render(context) + tempfile = NamedTemporaryFile(mode=mode, bufsize=bufsize, suffix=suffix, prefix=prefix, dir=dir, delete=delete) @@ -132,6 +144,31 @@ class PDFTemplateResponse(TemplateResponse, PDFResponse): for f in filter(None, (input_file, header_file, footer_file)): f.close() + def get_override_settings(self): + """Returns a dictionary of settings to override for response_class""" + overrides = { + 'MEDIA_ROOT': settings.MEDIA_ROOT, + 'MEDIA_URL': settings.MEDIA_URL, + 'STATIC_ROOT': settings.STATIC_ROOT, + 'STATIC_URL': settings.STATIC_URL, + } + if self.override_settings is not None: + overrides.update(self.override_settings) + + has_scheme = compile(r'^[^:/]+://') + + # If MEDIA_URL doesn't have a scheme, we transform it into a + # file:// URL based on MEDIA_ROOT. + urls = [('MEDIA_URL', 'MEDIA_ROOT'), + ('STATIC_URL', 'STATIC_ROOT')] + for url, root in urls: + if not has_scheme.match(overrides[url]): + overrides[url] = pathname2fileurl(overrides[root]) + if not overrides[url].endswith('/'): + overrides[url] += '/' + + return overrides + class PDFTemplateView(TemplateView): """Class-based view for HTML templates rendered to PDF.""" @@ -184,34 +221,26 @@ class PDFTemplateView(TemplateView): PendingDeprecationWarning, 2) return self.get_cmd_options() - def get_context_data(self, **kwargs): - context = super(PDFTemplateView, self).get_context_data(**kwargs) - - match_full_url = compile(r'^https?://') - if not match_full_url.match(settings.STATIC_URL): - context['STATIC_URL'] = 'http://' + Site.objects.get_current().domain + settings.STATIC_URL - if not match_full_url.match(settings.MEDIA_URL): - context['MEDIA_URL'] = 'http://' + Site.objects.get_current().domain + settings.MEDIA_URL - - return context - def render_to_response(self, context, **response_kwargs): """ Returns a PDF response with a template rendered with the given context. """ filename = response_kwargs.pop('filename', None) cmd_options = response_kwargs.pop('cmd_options', None) + override_settings = response_kwargs.pop('override_settings', None) if issubclass(self.response_class, PDFTemplateResponse): if filename is None: filename = self.get_filename() + if cmd_options is None: cmd_options = self.get_cmd_options() + return super(PDFTemplateView, self).render_to_response( context=context, filename=filename, header_template=self.header_template, footer_template=self.footer_template, - cmd_options=cmd_options, + cmd_options=cmd_options, override_settings=override_settings, **response_kwargs ) else: |