[Answer]-How do I create a CSV file from a model & then add it as an email attachment?

1👍

csvwriter can write to any object with a write() method, according to the docs. The usual Python class to use when you want an in-memory filelike object is a StringIO instance, or cStringIO if you’re sure you won’t be writing Unicode objects to it. So your export_to_csv function should look something like:

import cStringIO

def export_as_csv(report, fields, force_fields):
    # define field_names as above, assuming your indentation is correct
    csv_buffer = cStringIO.StringIO()        
    writer = csv.writer(csv_buffer, delimiter=',', quoting=csv.QUOTE_ALL)
    field_names = list(field_names) # loses order, but at least it's consistent
    writer.writerow(field_names)
    row = []
    for field in field_names:
        row.append(getattr(report, field).encode('utf-8'))
    writer.writerow(row)
    return csv_buffer

Then something like:

mail.attach("report.csv", export_as_csv(report, fields=list_display, force_fields=True).getvalue() , "text/csv")

Key differences are:

1) csv_buffer is an in-memory filelike object. Nothing is written to the file system.

2) This handles field lookup for the simple model fields 'primary_id', 'created_date', 'report_size' that you show in your example code. If you actually need to handle the names of callables in yourfield_names sequence, it gets harder.

3) This uses a single variable to hold the field_names after converting to a list. It will probably work to use list(field_names) and for field in field_names while field_names is a set, Python sets and dictionaries should be order-stable as long as no modifications are made, but I find it clearer and more reliable to be explicit about making sure the order syncs up.

4) This returns the cStringIO.StringIO object rather than the writer. The writer’s work is done once you’ve written everything you want to to it. The cStringIO object, conversely, should return the buffered CSV contents when you call getvalue() on it.

Leave a comment