# coding: utf-8
"""
Views
=====
A TemplateView that allows to set
* desired output format
* `Content-Disposition` on the response using ``output_name``
* output file name
Usage examples
---------------
Directly in ``urls.py``::
urlpatterns = [
path('', views.IndexView.as_view(), name="appy-examples"),
path('pdf/simple', AppyTemplateView.as_view(
template_name='examples/simple.odt'), name='pdf-simple'),
path('odt/simple', AppyTemplateView.as_view(
template_name='examples/simple.odt', output_type='odt'), name='odt-simple'),
path('odt/context', views.OdtContextView.as_view(), name='odt-context'),
path('pdf/context', views.PdfContextView.as_view(), name='pdf-context'),
]
Or in ``views.py`` as::
class PdfContextView(AppyTemplateView):
template_name = 'examples/context.odt'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['name'] = 'Sandro'
return context
class OdtContextView(PdfContextView):
output_type = 'odt'
.. autoclass:: AppyTemplateView
:members:
.. autoclass:: AppyTemplateMixin
:members:
"""
import os
from django.views.generic import TemplateView
from django.core.exceptions import ImproperlyConfigured
from django.template.response import TemplateResponse
class AppyTemplateResponse(TemplateResponse):
# We need to get to the render function so we can add optional flags
# output_format and external
# output_format is particularly needed to avoid getting to the converter odt => odt
def __init__(self, request, template, context=None, content_type=None,
status=None, charset=None, using=None, output_format=None, external=None):
super().__init__(request, template, context, content_type, status, charset, using)
self.output_format = output_format
self.external = external
@property
def rendered_content(self):
"""Return the freshly rendered content for the template and context
described by the TemplateResponse.
This *does not* set the final content of the response. To set the
response content, you must either call render(), or set the
content explicitly using the value of this property.
"""
template = self.resolve_template(self.template_name)
context = self.resolve_context(self.context_data)
content = template.render(
context, self._request, output_format=self.output_format, external=self.external)
return content
[documenti]class AppyTemplateMixin:
"""Mixin that tries to be smart understanding how to handle an
OpenDocument File as far as:
:content_type: desumed from ``output_format``
:output_name: desumed from ``template_name`` and ``output_format``
"""
#: the content type used in the response
content_type = None
content_type_map = {
'pdf': 'application/pdf',
'odt': 'application/vnd.oasis.opendocument.text',
'ods': 'application/vnd.oasis.opendocument.spreadsheet',
}
#: the desired output type: pdf, odt, ods are supported
output_format = 'pdf'
#: the desired output name. Default is built using template_name and
#: changing the suffix according to the output_format
output_file_name = None
response_class = AppyTemplateResponse
#: forces/blocks use of external service for conversion if POD_CONVERTER is defines
external = None
[documenti] def get_output_name(self):
"""Return the output_file_name or build a suitable one"""
if self.output_file_name:
return self.output_file_name
basename = os.path.basename(self.template_name)
base, ext = os.path.splitext(basename)
return f'{base}.{self.output_format}'
[documenti] def get_content_type(self):
"""Return the deesired output_format according"""
try:
return self.content_type or self.content_type_map[self.output_format]
except KeyError:
raise ImproperlyConfigured(
"""You must set eather content_format or output_format (pdf, odt, ods)"""
)
[documenti] def render_to_response(self, context, **response_kwargs):
"""
Return a response, using the `response_class` for this view, with a
template rendered with the given context.
Pass response_kwargs to the constructor of the response class.
"""
response_kwargs.setdefault('content_type', self.get_content_type())
response_kwargs['output_format'] = self.output_format
response_kwargs['external'] = self.external
output_file_name = self.get_output_name()
response = self.response_class(
request=self.request,
template=self.get_template_names(),
context=context,
using=self.template_engine,
**response_kwargs
)
response['Content-Disposition'] = f'attachment; filename="{output_file_name}"'
return response
[documenti]class AppyTemplateView(AppyTemplateMixin, TemplateView):
"""A templateView that just need a template_name and an output_format"""
pass