Add generic support for downloading list results as CSV

This commit is contained in:
Lance Edgar 2017-09-14 21:57:37 -05:00
parent f6d9f7a913
commit 9ff6df83e5
2 changed files with 53 additions and 0 deletions

View file

@ -69,6 +69,9 @@
</%def> </%def>
<%def name="context_menu_items()"> <%def name="context_menu_items()">
% if master.results_downloadable_csv and request.has_perm('{}.results_csv'.format(permission_prefix)):
<li>${h.link_to("Download results as CSV", url('{}.results_csv'.format(route_prefix)))}</li>
% endif
% if master.creatable and request.has_perm('{}.create'.format(permission_prefix)): % if master.creatable and request.has_perm('{}.create'.format(permission_prefix)):
<li>${h.link_to("Create a new {}".format(model_title), url('{}.create'.format(route_prefix)))}</li> <li>${h.link_to("Create a new {}".format(model_title), url('{}.create'.format(route_prefix)))}</li>
% endif % endif

View file

@ -40,6 +40,7 @@ from rattail.db.continuum import model_transaction_query
from rattail.util import prettify from rattail.util import prettify
from rattail.time import localtime #, make_utc from rattail.time import localtime #, make_utc
from rattail.threads import Thread from rattail.threads import Thread
from rattail.csvutil import UnicodeDictWriter
import formalchemy as fa import formalchemy as fa
from pyramid import httpexceptions from pyramid import httpexceptions
@ -64,6 +65,7 @@ class MasterView(View):
checkboxes = False checkboxes = False
listable = True listable = True
results_downloadable_csv = False
creatable = True creatable = True
viewable = True viewable = True
editable = True editable = True
@ -1384,6 +1386,46 @@ class MasterView(View):
""" """
return False return False
def results_csv(self):
"""
Download current list results as CSV
"""
results = self.get_effective_data()
fields = self.get_csv_fields()
data = six.StringIO()
writer = UnicodeDictWriter(data, fields)
writer.writeheader()
for obj in results:
writer.writerow(self.get_csv_row(obj, fields))
response = self.request.response
response.text = data.getvalue().decode('utf_8')
data.close()
response.content_length = len(response.text)
response.content_type = b'text/csv'
response.content_disposition = b'attachment; filename={}.csv'.format(self.get_grid_key())
return response
def get_csv_fields(self):
"""
Return the list of fields to be written to CSV download.
"""
fields = []
mapper = orm.class_mapper(self.model_class)
for prop in mapper.iterate_properties:
if isinstance(prop, orm.ColumnProperty):
fields.append(prop.key)
return fields
def get_csv_row(self, obj, fields):
"""
Return a dict for use when writing the row's data to CSV download.
"""
csvrow = {}
for field in fields:
value = getattr(obj, field)
csvrow[field] = '' if value is None else six.text_type(value)
return csvrow
############################## ##############################
# CRUD Stuff # CRUD Stuff
############################## ##############################
@ -1799,6 +1841,14 @@ class MasterView(View):
config.add_view(cls, attr='mobile_index', route_name='mobile.{}'.format(route_prefix), config.add_view(cls, attr='mobile_index', route_name='mobile.{}'.format(route_prefix),
permission='{}.list'.format(permission_prefix)) permission='{}.list'.format(permission_prefix))
if cls.results_downloadable_csv:
config.add_tailbone_permission(permission_prefix, '{}.results_csv'.format(permission_prefix),
"Download {} as CSV".format(model_title_plural))
config.add_route('{}.results_csv'.format(route_prefix), '{}/csv'.format(url_prefix))
config.add_view(cls, attr='results_csv', route_name='{}.results_csv'.format(route_prefix),
permission='{}.results_csv'.format(permission_prefix))
# create # create
if cls.creatable or cls.mobile_creatable: if cls.creatable or cls.mobile_creatable:
config.add_tailbone_permission(permission_prefix, '{}.create'.format(permission_prefix), config.add_tailbone_permission(permission_prefix, '{}.create'.format(permission_prefix),