Generic CSV Export

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import csv
from django.http import HttpResponse, HttpResponseForbidden
from django.template.defaultfilters import slugify
from django.db.models.loading import get_model

def export(qs, fields=None):
    model = qs.model
    response = HttpResponse(mimetype='text/csv')
    response['Content-Disposition'] = 'attachment; filename=%s.csv' % slugify(model.__name__)
    writer = csv.writer(response)
    # Write headers to CSV file
    if fields:
        headers = fields
    else:
        headers = []
        for field in model._meta.fields:
            headers.append(field.name)
    writer.writerow(headers)
    # Write data to CSV file
    for obj in qs:
        row = []
        for field in headers:
            if field in headers:
                val = getattr(obj, field)
                if callable(val):
                    val = val()
                row.append(val)
        writer.writerow(row)
    # Return CSV file to browser as download
    return response

def admin_list_export(request, model_name, app_label, queryset=None, fields=None, list_display=True):
    """
    Put the following line in your urls.py BEFORE your admin include
    (r'^admin/(?P<app_label>[\d\w]+)/(?P<model_name>[\d\w]+)/csv/', 'util.csv_view.admin_list_export'),
    """
    if not request.user.is_staff:
        return HttpResponseForbidden()
    if not queryset:
        model = get_model(app_label, model_name)
        queryset = model.objects.all()
        filters = dict()
        for key, value in request.GET.items():
            if key not in ('ot', 'o'):
                filters[str(key)] = str(value)
        if len(filters):
            queryset = queryset.filter(**filters)
    if not fields:
        if list_display and len(queryset.model._meta.admin.list_display) > 1:
            fields = queryset.model._meta.admin.list_display
        else:
            fields = None
    return export(queryset, fields)
    """
    Create your own change_list.html for your admin view and put something like this in it:
    {% block object-tools %}
    <ul class="object-tools">
        <li><a href="csv/{%if request.GET%}?{{request.GET.urlencode}}{%endif%}" class="addlink">Export to CSV</a></li>
    {% if has_add_permission %}
        <li><a href="add/{% if is_popup %}?_popup=1{% endif %}" class="addlink">{% blocktrans with cl.opts.verbose_name|escape as name %}Add {{ name }}{% endblocktrans %}</a></li>
    {% endif %}
    </ul>
    {% endblock %}
    """

More like this

  1. Admin action for a generic "CSV Export" by javinievas 3 years, 1 month ago
  2. Admin action for a generic "CSV Export" (fix for unicode) by __alexander__ 4 months, 2 weeks ago
  3. FCKEditor replace all vLargeTextField in admin by aronchi 5 years, 5 months ago
  4. Pagination Alphabetically compatible with paginator_class by vascop 1 year, 12 months ago
  5. load m2m fields objects by dirol 3 years, 10 months ago

Comments

zbyte64 (on June 10, 2008):

Special thanks to snippet 591 which I shamelessly used to write this code

#

illsci (on September 14, 2008):

Can you show an example where you send the view a queryset and a set of fields please? I would like to see how to use this with a model class that has a reverse relation to another model class. For example if you had an inline edited object and wanted to output the total fields and associated records across that relation...

#

zbyte64 (on October 8, 2008):

If you want to export inline objects, csv is probably not a good idea. If you really really wanted to do csv export for such a thing there are a variety of ways of going about it, hence why this code does not support it.

As for queryset/filtering, it simply takes advantage of the admin filtering

#

denilton (on October 13, 2008):

Can you show an example of the view? 'util.csv_view.admin_list_export'

#

zehi (on November 28, 2008):

It works very nice with English characters. Ones I use something different, like Finnish for example, it gives a error: utils/csv_view.py in export, line 28

'ascii' codec can't encode character u'\xe4' in position 1: ordinal not in range(128)

#

Beuc (on January 6, 2009):

Here's a work-around for the unicode limitation of the csv module.

if callable(val):
    val = val()
# work around csv unicode limitation
if type(val) == unicode:
    val = val.encode("utf-8")
row.append(val)

I think this snippets needs an update. 2 things don't work:

  • 'queryset.model._meta.admin' isn't an object, but a string, resulting in an Exception

  • in the admin template ({%if request.GET%}) it seems 'request' isn't part of the context anymore.

#

sto (on January 29, 2009):

I've fixed the first problem (queryset.model._meta.admin) using the admin.site object (it keeps a registry of models and admins); the patch in diff format is:

-    if not fields:
-        if list_display and len(queryset.model._meta.admin.list_display) > 1:
-            fields = queryset.model._meta.admin.list_display
-        else:
-            fields = None
+    if not fields and list_display:
+        from django.contrib import admin
+        ld = admin.site._registry[queryset.model].list_display
+        if ld and len(ld) > 0: fields = ld

#

ssavelan (on February 21, 2009):

Thanks for the snippet, and nice patch, sto, that fixed up my problem!

#

cameronoliver (on February 23, 2009):

Hi. I've come up with a solution to the second problem (request not being part of the context anymore). Delete the code {%if request.GET%}?{{request.GET.urlencode}}{%endif%} and replace it with the following: {% for key, value in cl.params.items %}{% if forloop.first %}?{% else %}&{% endif %}{{ key }}={{ value }}{% endfor %}

Also, the snippet above doesn't account for pagination. The line if key not in ('ot', 'o'): should be replaced with if key not in ('ot', 'o', 'p'):

#

wardb (on March 31, 2009):

I had this working yesterday. Then /django/contrib/admin/templates/admin/change_list.html changed (r10121) to include a select "Actions" feature. Now exporting CVS, produces an error:

Exception Type: AttributeError
Exception Value: '<INSERT MODEL TO BE EXPORTED NAME HERE>' object has no attribute 'action_checkbox'
Exception Location: /<SNIP>/util/csv_view.py in export, line 24

And solution suggestions?

#

tnovelli (on May 27, 2009):

Solution to the last problem -- put this just before "# Write data to CSV file" in export():

# Exclude checkbox for Django 1.1 Admin Actions
if headers[0] == 'action_checkbox':
    del headers[0]

#

Pete (on August 9, 2011):

To fix the actionbox problem I changed as stated but somehow the export stops after the first fields in my model? Is this the wrong position?

if fields:
    headers = fields
    if headers[0] == 'action_checkbox':
       del headers[0]
else:
    headers = []
    for field in model._meta.fields:
        headers.append(field.name)

#

darkpixel (on December 1, 2011):

Something must be b0rked with the markdown formatter. I can't get my 4-space-indented text to show up as code blocks, and it is therefore unreadable on this site.

I added the ability to access related objects: https://gist.github.com/1418860

#

(Forgotten your password?)