aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Law <simon.law@ecometrica.com>2012-07-25 12:51:26 -0400
committerSimon Law <simon.law@ecometrica.com>2012-07-25 12:51:26 -0400
commit5a1847309e7fa431c98565805d88a21a40d01406 (patch)
tree9aa1b5ac50efd0bd39f1a38130ec9d56e4a59302
parenta6f0a53702a940bf055dadb3bf558aea49c6d862 (diff)
downloaddjango-wkhtmltopdf-5a1847309e7fa431c98565805d88a21a40d01406.tar.gz
django-wkhtmltopdf-5a1847309e7fa431c98565805d88a21a40d01406.tar.bz2
django-wkhtmltopdf-5a1847309e7fa431c98565805d88a21a40d01406.zip
MEDIA_URL and STATIC_URL overrides PDFTemplateResponse.get_override_settings()
MEDIA_URL and STATIC_URL used to be set only in get_context_data(), but there are apps such as staticfiles and Django Compressor where this won't work well. Instead, they need to be overridden at the settings level, not at the context level. This allows template context processors to populate a RequestContext with the right values. In addition, MEDIA_URL and STATIC_URL are now overridden as file:// URLs, based on MEDIA_ROOT and STATIC_ROOT. This allows developers to access these views in runserver, against their current codebase. It also means faster access for wkhtmltopdf, since the files are stored locally.
-rw-r--r--wkhtmltopdf/tests.py52
-rw-r--r--wkhtmltopdf/utils.py6
-rw-r--r--wkhtmltopdf/views.py63
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: