Q:

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

I'm trying to generate a CSV from an object stored in my database and then add the csv to an email as an attachment. 
I'm using Django 1.7 & Python 2.7.
I'm getting this error with the following code: "AttributeError: '_csv.writer' object has no attribute 'encode'"
Here's my code:
def export_as_csv(report, fields, force_fields):
     print "export to csv started"
     """
    Generic csv export admin action.
    based on http://djangosnippets.org/snippets/2020/
    extended for being able to give list_display as fields and work with     admin-defined functions
    """
    opts = report
    if not force_fields:
        field_names = set([field.name for field in opts.fields])
        if fields:
        fieldset = set(fields)
            field_names = field_names & fieldset
    elif fields:
        field_names = set(fields)
    else:
        raise("option force_fields can only be used in parallel with option fields")

    writer = csv.writer(open("myfile.csv","w"), delimiter=',',quoting=csv.QUOTE_ALL)

    writer.writerow(list(field_names))
    row = []
    for field in field_names:
        try:
        row.append(unicode(getattr(report, field)).encode('utf-8'))
        except AttributeError:
            row.append(unicode((getattr(report, field)(report))).encode('utf-8'))
        except:
            raise
    writer.writerow(row)

    response = writer
    return response

The code below returns the above function's output as a parameter in mail.attach().
The report parameter is my DB model/object that's created & saved before any of this code is called.
list_display = ('primary_id', 'created_date', 'report_size',)
mail = EmailMessage('Email Subject', 'Email Body', None, ['example@example.com'])
mail.attach("report.csv", export_as_csv(report, fields = list_display, force_fields=True) , "text/csv")
mail.send()

Here's the model for a Report() object:
class Report(models.Model):
    primary_id = models.IntegerField('Primary Key', primary_key = True, default=0) 
    created_date = models.DateTimeField('Created date', default=timezone.now)
    report_size = models.IntegerField('Report Size', default=0) 

Any help is greatly appreciated.  
Here's how I ideally want this code to work: export_as_csv() returns a CSV object that I don't need to save as a file or in my database - then the code attaches that to the email directly from a CSV object in memory, not a saved file.  Not sure if this is possible, but that's ideal for my needs.

A:

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.

