I'm having an issue when trying to render a PDF through xhtml2pdf in Django(1.10.4) when using django-admin-tools(0.8.0) & django-tables2(1.5) together. I've done enough reading to understand the basis of what's going on but have not idea how to fix it. I think it's got something to do with the django-admin-tools custom loaders.
Link to the exception I'm getting from django-tables.
This SO question led me to asking a question.
The gist of what I'm trying to do is create a custom Admin 'action' through the drop-down box of my AdminModel in the django-admin interface that turns the queryset given into a PDF document.
According to the django-tables2 docs the render() function takes 3 arguments (request, 'template_name.html', {'people': Person.objects.all()}). So I added a queryset to my context_dict and tried using it in a for loop in the template below but no dice. The template renders the html to pdf just fine without django-tables2 but if I try to convert the tables to PDF I get the following...
Environment:
Request Method: POST
Request URL: http://127.0.0.1:9999/admin/research/labsample/
Django Version: 1.10.4
Python Version: 3.5.2
Installed Applications:
('admin_tools',
'admin_tools.theming',
'admin_tools.menu',
'admin_tools.dashboard',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django_tables2',
'import_export',
'chemicals',
'suppliers',
'customers',
'recipes',
'research')
Installed Middleware:
('django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware')
Template error:
In template /home/dan/Projects/research/lib/python3.5/site-packages/django_tables2/templates/django_tables2/table.html, error at line 13
Tag {% querystring %} requires django.template.context_processors.request to be in the template configuration in settings.TEMPLATES[]OPTIONS.context_processors) in order for the included template tags to function correctly. 3 :
4 : <div class="table-container">
5 : {% block table %}
6 : <table{% if table.attrs %} {{ table.attrs.as_html }}{% endif %}>
7 : {% block table.thead %}
8 : {% if table.show_header %}
9 : <thead>
10 : <tr>
11 : {% for column in table.columns %}
12 : {% if column.orderable %}
13 : <th {{ column.attrs.th.as_html }}><a href=" {% querystring table.prefixed_order_by_field=column.order_by_alias.next %} ">{{ column.header }}</a></th>
14 : {% else %}
15 : <th {{ column.attrs.th.as_html }}>{{ column.header }}</th>
16 : {% endif %}
17 : {% endfor %}
18 : </tr>
19 : </thead>
20 : {% endif %}
21 : {% endblock table.thead %}
22 : {% block table.tbody %}
23 : <tbody>
Traceback:
File "/home/dan/Projects/research/lib/python3.5/site-packages/django/core/handlers/exception.py" in inner
39. response = get_response(request)
File "/home/dan/Projects/research/lib/python3.5/site-packages/django/core/handlers/base.py" in _get_response
187. response = self.process_exception_by_middleware(e, request)
File "/home/dan/Projects/research/lib/python3.5/site-packages/django/core/handlers/base.py" in _get_response
185. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/dan/Projects/research/lib/python3.5/site-packages/django/contrib/admin/options.py" in wrapper
544. return self.admin_site.admin_view(view)(*args, **kwargs)
File "/home/dan/Projects/research/lib/python3.5/site-packages/django/utils/decorators.py" in _wrapped_view
149. response = view_func(request, *args, **kwargs)
File "/home/dan/Projects/research/lib/python3.5/site-packages/django/views/decorators/cache.py" in _wrapped_view_func
57. response = view_func(request, *args, **kwargs)
File "/home/dan/Projects/research/lib/python3.5/site-packages/django/contrib/admin/sites.py" in inner
211. return view(request, *args, **kwargs)
File "/home/dan/Projects/research/lib/python3.5/site-packages/django/utils/decorators.py" in _wrapper
67. return bound_func(*args, **kwargs)
File "/home/dan/Projects/research/lib/python3.5/site-packages/django/utils/decorators.py" in _wrapped_view
149. response = view_func(request, *args, **kwargs)
File "/home/dan/Projects/research/lib/python3.5/site-packages/django/utils/decorators.py" in bound_func
63. return func.__get__(self, type(self))(*args2, **kwargs2)
File "/home/dan/Projects/research/lib/python3.5/site-packages/django/contrib/admin/options.py" in changelist_view
1569. response = self.response_action(request, queryset=cl.get_queryset(request))
File "/home/dan/Projects/research/lib/python3.5/site-packages/django/contrib/admin/options.py" in response_action
1305. response = func(self, request, queryset)
File "/home/dan/Projects/research/mysite/research/admin.py" in GeneratePdf
27. pdf = render_to_pdf('pdf/quote_recipe.html', content)
File "/home/dan/Projects/research/mysite/research/utils.py" in render_to_pdf
40. html = template.render(context_dict)
File "/home/dan/Projects/research/lib/python3.5/site-packages/django/template/backends/django.py" in render
66. return self.template.render(context)
File "/home/dan/Projects/research/lib/python3.5/site-packages/django/template/base.py" in render
208. return self._render(context)
File "/home/dan/Projects/research/lib/python3.5/site-packages/django/template/base.py" in _render
199. return self.nodelist.render(context)
File "/home/dan/Projects/research/lib/python3.5/site-packages/django/template/base.py" in render
994. bit = node.render_annotated(context)
File "/home/dan/Projects/research/lib/python3.5/site-packages/django/template/base.py" in render_annotated
961. return self.render(context)
File "/home/dan/Projects/research/lib/python3.5/site-packages/django/template/defaulttags.py" in render
209. nodelist.append(node.render_annotated(context))
File "/home/dan/Projects/research/lib/python3.5/site-packages/django/template/base.py" in render_annotated
961. return self.render(context)
File "/home/dan/Projects/research/lib/python3.5/site-packages/django_tables2/templatetags/django_tables2.py" in render
154. return template.render(context.flatten())
File "/home/dan/Projects/research/lib/python3.5/site-packages/django/template/backends/django.py" in render
66. return self.template.render(context)
File "/home/dan/Projects/research/lib/python3.5/site-packages/django/template/base.py" in render
208. return self._render(context)
File "/home/dan/Projects/research/lib/python3.5/site-packages/django/template/base.py" in _render
199. return self.nodelist.render(context)
File "/home/dan/Projects/research/lib/python3.5/site-packages/django/template/base.py" in render
994. bit = node.render_annotated(context)
File "/home/dan/Projects/research/lib/python3.5/site-packages/django/template/base.py" in render_annotated
961. return self.render(context)
File "/home/dan/Projects/research/lib/python3.5/site-packages/django/template/loader_tags.py" in render
61. result = self.nodelist.render(context)
File "/home/dan/Projects/research/lib/python3.5/site-packages/django/template/base.py" in render
994. bit = node.render_annotated(context)
File "/home/dan/Projects/research/lib/python3.5/site-packages/django/template/base.py" in render_annotated
961. return self.render(context)
File "/home/dan/Projects/research/lib/python3.5/site-packages/django/template/loader_tags.py" in render
61. result = self.nodelist.render(context)
File "/home/dan/Projects/research/lib/python3.5/site-packages/django/template/base.py" in render
994. bit = node.render_annotated(context)
File "/home/dan/Projects/research/lib/python3.5/site-packages/django/template/base.py" in render_annotated
961. return self.render(context)
File "/home/dan/Projects/research/lib/python3.5/site-packages/django/template/defaulttags.py" in render
315. return nodelist.render(context)
File "/home/dan/Projects/research/lib/python3.5/site-packages/django/template/base.py" in render
994. bit = node.render_annotated(context)
File "/home/dan/Projects/research/lib/python3.5/site-packages/django/template/base.py" in render_annotated
961. return self.render(context)
File "/home/dan/Projects/research/lib/python3.5/site-packages/django/template/defaulttags.py" in render
209. nodelist.append(node.render_annotated(context))
File "/home/dan/Projects/research/lib/python3.5/site-packages/django/template/base.py" in render_annotated
961. return self.render(context)
File "/home/dan/Projects/research/lib/python3.5/site-packages/django/template/defaulttags.py" in render
315. return nodelist.render(context)
File "/home/dan/Projects/research/lib/python3.5/site-packages/django/template/base.py" in render
994. bit = node.render_annotated(context)
File "/home/dan/Projects/research/lib/python3.5/site-packages/django/template/base.py" in render_annotated
961. return self.render(context)
File "/home/dan/Projects/research/lib/python3.5/site-packages/django_tables2/templatetags/django_tables2.py" in render
59. raise ImproperlyConfigured(context_processor_error_msg % 'querystring')
Exception Type: ImproperlyConfigured at /admin/research/labsample/
Exception Value: Tag {% querystring %} requires django.template.context_processors.request to be in the template configuration in settings.TEMPLATES[]OPTIONS.context_processors) in order for the included template tags to function correctly.
def GeneratePdf(self, request, queryset):
data = queryset.values()
content = {}
rec = RecipeIngredients.objects.filter(recipe_id=data.values()[0]['lab_recipe_id'])
content.update({'recipe':{rec.values()}})
content.update(data[0])
pdf = render_to_pdf('pdf/quote_recipe.html', content)
return HttpResponse(pdf, content_type='application/pdf')
def render_to_pdf(template_src, context_dict={}):
template = get_template(template_src)
html = template.render(context_dict)
result = BytesIO()
pdf = pisa.pisaDocument(BytesIO(html.encode("UTF-8")), result, link_callback=link_callback)
if not pdf.err:
return HttpResponse(result.getvalue(), content_type='application/pdf')
return None
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS':[BASE_DIR+'/templates/',],
'OPTIONS': {
'context_processors': [
'django.template.context_processors.request',
'django.template.context_processors.i18n',
'django.template.context_processors.media',
'django.template.context_processors.static',
'django.template.context_processors.tz',
'django.template.context_processors.debug',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
'loaders':[
'admin_tools.template_loaders.Loader',
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
]
},
},
]
{% load render_table from django_tables2 %}
{% load static %}
<!doctype html>
<html>
<body>
{% for r in recipe %}
{% render_table r %}
{% endfor %}
</body>
</html>
To use the template context processors, you must pass the request
object when rendering the template:
def render_to_pdf(template_src, context_dict={}):
template = get_template(template_src)
html = template.render(context_dict, request=request)
...
You can simplify the code slightly by using render_to_string
.
from django.template.loader import render_to_string
def render_to_pdf(template_src, context_dict=None):
if context_dict is None:
context_dict = {}
html = render_to_string(template_src, context_dict, request=request)
...
As an aside, you shouldn't use mutable values as defaults when defining functions. In the second example above, I have shown how you can avoid issues by using None
as the default.