Skip to content

Commit

Permalink
option to set filename in view (fixes #31)
Browse files Browse the repository at this point in the history
  • Loading branch information
sheppard committed Mar 28, 2019
1 parent 6cc74f2 commit a43c29b
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 4 deletions.
12 changes: 12 additions & 0 deletions README.md
Expand Up @@ -171,6 +171,18 @@ class TimeSeriesView(PandasView):
renderer_classes = [PandasCSVRenderer, PandasExcelRenderer]
# You can also set the default renderers for all of your pandas views by
# defining the PANDAS_RENDERERS in your settings.py.

# Step 5 (Optional). The default filename may not be particularly useful
# for your users. To override, define get_pandas_filename() on your view.
# If a filename is returned, rest_pandas will include the following header:
# 'Content-Disposition: attachment; filename="Data Export.xlsx"'
def get_pandas_filename(self, request, format):
if format in ('xls', 'xlsx'):
# Use custom filename and Content-Disposition header
return "Data Export" # Extension will be appended automatically
else:
# Default filename from URL (no Content-Disposition header)
return None
```

#### Django Pandas Integration
Expand Down
37 changes: 34 additions & 3 deletions rest_pandas/views.py
Expand Up @@ -75,6 +75,31 @@ def get_serializer_class(self):
else:
return self.serializer_class

def get_pandas_filename(self, request, format):
return None

def get_pandas_headers(self, request):
format = request.accepted_renderer.format
filename = self.get_pandas_filename(request, format)
if not filename:
return {}

extension = '.' + format
if not filename.endswith(extension):
filename += extension

return {
'Content-Disposition': 'attachment; filename="{}"'.format(
filename
)
}

def update_pandas_headers(self, response):
headers = self.get_pandas_headers(self.request)
for key, val in headers.items():
response[key] = val
return response


class PandasViewBase(PandasMixin):
renderer_classes = PANDAS_RENDERERS
Expand All @@ -97,18 +122,24 @@ def get(self, request, *args, **kwargs):
data = self.get_data(request, *args, **kwargs)
serializer_class = self.get_serializer_class()
serializer = serializer_class(data, many=True)
return Response(serializer.data)
response = Response(serializer.data)
return self.update_pandas_headers(response)


class PandasView(PandasViewBase, ListAPIView):
"""
Pandas-capable model list view
"""
pass

def list(self, request, *args, **kwargs):
response = super(PandasView, self).list(request, *args, **kwargs)
return self.update_pandas_headers(response)


class PandasViewSet(PandasViewBase, ListModelMixin, GenericViewSet):
"""
Pandas-capable model ViewSet (list only)
"""
pass
def list(self, request, *args, **kwargs):
response = super(PandasViewSet, self).list(request, *args, **kwargs)
return self.update_pandas_headers(response)
2 changes: 2 additions & 0 deletions runserver.sh
@@ -0,0 +1,2 @@
#!/bin/bash
python3 -m django runserver --settings=tests.settings
4 changes: 3 additions & 1 deletion tests/settings.py
Expand Up @@ -8,7 +8,7 @@
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': ':memory:',
'NAME': 'rest_pandas_test.sqlite3',
}
}
ROOT_URLCONF = "tests.urls"
Expand All @@ -19,6 +19,8 @@
},
]

DEBUG = True


try:
import matplotlib # noqa
Expand Down
8 changes: 8 additions & 0 deletions tests/test_excel.py
Expand Up @@ -17,6 +17,10 @@ def setUp(self):

def test_xls(self):
response = self.client.get("/timeseries.xls")
self.assertEqual(
'attachment; filename="Time Series.xls"',
response['content-disposition'],
)
xlfile = open('tests/output.xls', 'wb')
xlfile.write(response.content)
xlfile.close()
Expand All @@ -28,6 +32,10 @@ def test_xls(self):

def test_xlsx(self):
response = self.client.get("/timeseries.xlsx")
self.assertEqual(
'attachment; filename="Time Series.xlsx"',
response['content-disposition'],
)
xlfile = open('tests/output.xlsx', 'wb')
xlfile.write(response.content)
xlfile.close()
Expand Down
6 changes: 6 additions & 0 deletions tests/testapp/views.py
Expand Up @@ -43,6 +43,12 @@ class TimeSeriesView(PandasView):
def get_template_context(self, data):
return {'name': data['name'] + ' Custom'}

def get_pandas_filename(self, request, format):
if format in ('xls', 'xlsx'):
return self.get_view_name()
else:
return None


class TimeSeriesNoIdView(PandasView):
queryset = TimeSeries.objects.all()
Expand Down

0 comments on commit a43c29b

Please sign in to comment.