I have written a custom Renderer that allows rendering nested json data as flat Excel files.
class ExcelRenderer(renderers.BaseRenderer):
"""Custom renderer for exporting XLS files"""
media_type = "application/xlsx"
format = "excel"
render_style = 'binary'
def render(self, data, accepted_media_type=None, renderer_context=None):
query_params = renderer_context["request"].query_params
query_params = "_".join([f"{v.split('T')[0]}" for k,v in query_params.items()])
"""
Render `data` into XLSX workbook, returning a workbook.
"""
try:
df = json_into_flat_dataframe(data)
except Exception as e:
logger.error(e, exc_info=True)
df = pd.DataFrame()
if len(df.index) == 0:
raise rest_exceptions.APIException("No data!")
output = io.BytesIO()
# Use a temp filename to keep pandas happy.
writer = pd.ExcelWriter(output, engine='xlsxwriter')
# Write the data frame to the StringIO object.
df.to_excel(writer, sheet_name='Sheet1', index=False)
writer.save()
data = output.getvalue()
#Enable direct download.
renderer_context['response']["content-disposition"] = f"attachment; filename=vodepro_export_{query_params}.xlsx"
return data
This works as expected as long as there is not exception (e.g. ValidationError
) in the process before rendering. When a ValidationError is raised, my Custom Renderer tries to render it (and fails, since this was not even designed behaviour of Renderer).
I would like to implement a renderer, that would not try to parse ValidationErrors (or any other Exceptions in fact) into Excel, but would fallback to a normal DRF behaviour (json or BrowsableAPI renderer) in such case?
To be a complete answer here's the code:
from rest_framework.renderers import JSONRenderer
from rest_framework.views import exception_handler
def custom_exception_handler(exc, context):
""" Switch from PDFRenderer to JSONRenderer for exceptions """
if context['request'].accepted_renderer.format == 'pdf':
context['request'].accepted_renderer = JSONRenderer()
return exception_handler(exc, context)
If you already have a custom exception handler, replace it by the default one from rest_framework.views in the snippet above.
You then need to set it on settings:
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'my_project.my_app.utils.custom_exception_handler'
}
You may refer to this answer.