Permalink
Browse files

added CSVTemplateResponseMixin

  • Loading branch information...
1 parent 0ed6ca8 commit cb86fcf3dd142a44bc78d3aec4d928a1e4538d27 @rasca rasca committed Jun 19, 2011
Showing with 127 additions and 7 deletions.
  1. +51 −5 enhanced_cbv/response.py
  2. +37 −1 enhanced_cbv/utils.py
  3. +39 −1 enhanced_cbv/views/base.py
View
@@ -1,5 +1,10 @@
from django.template.response import TemplateResponse
-from enhanced_cbv.utils import fetch_resources
+from enhanced_cbv.utils import fetch_resources, UnicodeWriter
+try:
+ from cStringIO import StringIO
+except ImportError:
+ from StringIO import StringIO
+
class PDFTemplateResponse(TemplateResponse):
@@ -26,10 +31,6 @@ def render(self):
# The following is required for PDF generation
- try:
- from cStringIO import StringIO
- except ImportError:
- from StringIO import StringIO
import ho.pisa as pisa
if not self._is_rendered:
@@ -56,3 +57,48 @@ def render(self):
for post_callback in self._post_render_callbacks:
post_callback(self)
return self
+
+
+class CSVTemplateResponse(TemplateResponse):
+
+
+ def __init__(self, request, template, context=None,
+ mimetype='text/csv', status=None, content_type=None,
+ current_app=None, filename=None, rows=None):
+ """Simple adds a default mimetype for CSVs and a filename"""
+
+ self.filename = filename
+ self.rows = rows
+
+ super(CSVTemplateResponse, self).__init__(request,
+ template, context, mimetype, status, content_type)
+
+ def render(self):
+ """This is the tricky part, whith the rendered_content create a CSV"""
+
+
+ if not self._is_rendered:
+
+ # File pointer needed to create the CSV in memory
+ buffer = StringIO()
+ writer = UnicodeWriter(buffer)
+
+ for row in self.rows:
+ writer.writerow([unicode(value).encode('utf-8') for value
+ in row])
+
+ # Get the value of the StringIO buffer and write it to the response.
+ csv = buffer.getvalue()
+ buffer.close()
+ self.write(csv)
+
+ # Sets the appropriate CSV headers.
+ self['Content-Disposition'] = 'attachment; filename=%s' % (
+ self.filename, )
+
+ # The CSV has been generated
+ self._is_rendered = True
+
+ for post_callback in self._post_render_callbacks:
+ post_callback(self)
+ return self
View
@@ -1,4 +1,4 @@
-import os
+import os, csv, codecs, cStringIO
from django.conf import settings
@@ -14,3 +14,39 @@ def fetch_resources(uri, rel):
path = os.path.join(settings.MEDIA_ROOT, uri.replace(settings.MEDIA_URL, ""))
return path
+
+class UnicodeWriter:
+ """
+ A CSV writer which will write rows to CSV file "f",
+ which is encoded in the given encoding.
+ """
+
+ def __init__(self, f, dialect=csv.excel, encoding="utf-8", **kwds):
+ # Redirect output to a queue
+ self.queue = cStringIO.StringIO()
+ self.writer = csv.writer(self.queue, dialect=dialect, **kwds)
+ self.stream = f
+ self.encoder = codecs.getincrementalencoder(encoding)()
+
+ def writerow(self, row):
+ encoded_row = []
+ for s in row:
+ try:
+ encoded_row.append(s.encode("utf-8"))
+ except AttributeError:
+ encoded_row.append(s)
+
+ self.writer.writerow(encoded_row)
+ # Fetch UTF-8 output from the queue ...
+ data = self.queue.getvalue()
+ data = data.decode("utf-8")
+ # ... and reencode it into the target encoding
+ data = self.encoder.encode(data)
+ # write to the target stream
+ self.stream.write(data)
+ # empty queue
+ self.queue.truncate(0)
+
+ def writerows(self, rows):
+ for row in rows:
+ self.writerow(row)
View
@@ -1,5 +1,5 @@
from django.views.generic.base import TemplateResponseMixin
-from enhanced_cbv.response import PDFTemplateResponse
+from enhanced_cbv.response import PDFTemplateResponse, CSVTemplateResponse
class PDFTemplateResponseMixin(TemplateResponseMixin):
"""
@@ -23,3 +23,41 @@ def render_to_response(self, *args, **kwargs):
"""
kwargs.update({'filename': self.get_filename()})
return super(PDFTemplateResponseMixin, self).render_to_response(*args, **kwargs)
+
+
+class CSVTemplateResponseMixin(TemplateResponseMixin):
+ """
+ Extends the TemplateResponseMixin with a filename for render_to_response
+ """
+
+ response_class = CSVTemplateResponse
+ filename = None
+
+ def get_filename(self):
+ if self.filename is None:
+ raise ImproperlyConfigured(
+ "PDFTemplateResponseMixin requires either a definition of "
+ "'filename' or an implementation of 'get_filename()'")
+ else:
+ return self.filename
+
+ def render_to_response(self, *args, **kwargs):
+ """
+ Returns a response with a template rendered with the given context.
+ """
+ rows = [self.get_header()]
+ for obj in self.get_queryset():
+ rows.append(self.get_row(obj))
+ kwargs.update({
+ 'rows': rows,
+ 'filename': self.get_filename()
+ })
+ return super(CSVTemplateResponseMixin, self).render_to_response(*args, **kwargs)
+
+ def get_header(self):
+ """Must return a list of strings for the header"""
+ return NotImplementedError
+
+ def get_row(self, obj):
+ """Must return a list of strings for a row"""
+ return NotImplementedError

0 comments on commit cb86fcf

Please sign in to comment.