7

Inside my view.py, I have two functions, one that processes input from a form and outputs a filtered list, and another that is supposed to export this list to CSV.

Here is the return of my first function:

return render_to_response('templateX.html',
{
 'queryset': queryset,
 'filter_form': filter_form,
 'validated': validated,
},
 context_instance = RequestContext(request)
 )

Here is the exporting function:

def export_to_csv(request):
    # get the response object, this can be used as a stream.
    response = HttpResponse(mimetype='text/csv')
    # force download.
    response['Content-Disposition'] = 'attachment;filename=export.csv'
    # the csv writer
    writer = csv.writer(response)
    qs = request.session['queryset']
    for cdr in qs:
        writer.writerow([cdr['calldate'], cdr['src'], cdr['dst'], ])
    return response   

I'm not sure how to get the queryset from my first function, which contains a list of the items I want in my CSV and use it in my export_to_csv function. Or would the best way be combining these two functions and have the user click on a checkbox whether he/she wants to download a CSV file. Any help would be appreciated.

chiurox
  • 1,549
  • 3
  • 18
  • 28

4 Answers4

7

I'd recommend combining these into one view function which takes an extra parameter:

def my_view(request, exportCSV):
    # ... Figure out `queryset` here ...

    if exportCSV:
        response = HttpResponse(mimetype='text/csv')
        response['Content-Disposition'] = 'attachment;filename=export.csv'
        writer = csv.writer(response)
        for cdr in queryset:
            writer.writerow([cdr['calldate'], cdr['src'], cdr['dst'], ])
        return response
    else:
        return render_to_response('templateX.html', {'queryset': queryset,
            'filter_form': filter_form, 'validated': validated},
            context_instance = RequestContext(request))

Then, in your urls.py, put something like this in your urlpatterns:

url(r'^form', 'my_view', {"exportCSV": False}, name="form"),
url(r'^csv', 'my_view', {"exportCSV": True}, name="export"),
Mike DeSimone
  • 41,631
  • 10
  • 72
  • 96
  • I tried it this way, but when I click on my "export" button, which goes to the url: localhost:8000/cdr/export_csv, it loses all the GET requests and as a consequence there's no processing of queryset, so my CSV file is empty. So how can my "export" button send all the requests as if I'm clicking on my "search" button? – chiurox Nov 02 '10 at 00:04
  • Your Export button should either 1) target the export URL and include the query parameters, or 2) be part of a form which has the query parameters, as hidden fields if necessary. It's hard for me to make a specific recommendation since I'm not completely clear on how your pages relate to one another. – Mike DeSimone Nov 02 '10 at 04:10
  • Yes, I decided to do it the way you described as your first option. Now 'hidden fields,' that's interesting, I shall look at that later when I come across a similar situation. – chiurox Nov 03 '10 at 13:08
  • Hidden fields are ``. I don't know if Django has some special magic to generate them, but they're pretty easy to generate on your own. Just remember, even if the form parameter came from a hidden field, don't trust it implicitly. Validate it. – Mike DeSimone Nov 03 '10 at 14:04
  • 1
    `mimetype` should be `content_type`. It is changed now – Marlon Abeykoon May 20 '17 at 18:32
4

IMHO, the best would be to combine them and generate the CSV data from an explicit queryset. This could then be rewritten to something general like(not tested):

def export_to_csv(request, queryset, fields):
    response = ...
    writer = csv.writer(response)
    for obj in queryset:
        writer.writerow([getattr(obj, f) for f in fields])
    return response

Which you can use like this:

def my_view(request):
    calls = Call.objects.all()
    return export_to_csv(request, calls, fields = ('calldate', 'src', 'dst'))

--

The example code you provided assumes the QuerySet is set in the session data, which could cause you tons of bugs as well as security problems. If you store sessions in your database, you could end up reading data, just to write it back in a much less efficient form.

knutin
  • 5,033
  • 19
  • 26
0

The following takes in a Django queryset and spits out a CSV file.

Usage::

from utils import dump2csv

from dummy_app.models import *

qs = DummyModel.objects.all()

dump2csv.dump(qs, './data/dump.csv')

The script:

import csv
from django.db.models.loading import get_model

def dump(qs, outfile_path):

    model = qs.model
writer = csv.writer(open(outfile_path, 'w'))

headers = []
for field in model._meta.fields:
    headers.append(field.name)
writer.writerow(headers)

for obj in qs:
    row = []
    for field in headers:
        val = getattr(obj, field)
        if callable(val):
            val = val()
        if type(val) == unicode:
            val = val.encode("utf-8")
        row.append(val)
    writer.writerow(row)
Armance
  • 5,350
  • 14
  • 57
  • 80
0

I found a way to do it that's different than knutin's. I declared an empty list called csv_list = [] outside my functions, or global variable.

In my main function (the one that processes and filters based on user input), I make this csv_list global so that it gets set to the "updated" version of the queryset. Then to generate the csv, it's as easy as: for call in csv_list: writer.writerow([call.src, call.dst]) return response

It's working reasonably now.

chiurox
  • 1,549
  • 3
  • 18
  • 28
  • It's dangerous to use globals to pass data through views; Django can be threaded, and this will probably break on deployment. You won't see this bug while testing on your own, and since it's based on a race condition, tracking it down will be hard. – Mike DeSimone Nov 01 '10 at 23:13
  • Could you elaborate more on the dangerousness and how it might break on deployment? – chiurox Nov 01 '10 at 23:18
  • User 1 posts a request for your form. This calls your view function which sets `csv_list`. User 2 does the same thing, which again sets `csv_list`. User 1 then exports the CSV and gets user 2's data via `csv_list` instead. – Mike DeSimone Nov 01 '10 at 23:21
  • Ok, got it. That's disastrous. – chiurox Nov 01 '10 at 23:46