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 %}
"""
Comments
Special thanks to snippet 591 which I shamelessly used to write this code
#
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...
#
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
#
Can you show an example of the view? 'util.csv_view.admin_list_export'
#
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)
#
Here's a work-around for the unicode limitation of the csv module.
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.
#
I've fixed the first problem (queryset.model._meta.admin) using the
admin.siteobject (it keeps a registry of models and admins); the patch in diff format is:#
Thanks for the snippet, and nice patch, sto, that fixed up my problem!
#
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 withif key not in ('ot', 'o', 'p'):#
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:
And solution suggestions?
#
Solution to the last problem -- put this just before "# Write data to CSV file" in export():
#
I have made an exporter that also takes search parameters into account, and exports in multiple data formats. This might be useful for people looking at this snippet:
http://www.djangosnippets.org/snippets/1792/
#
I'm getting the following error:
admin_list_export() takes at least 3 non-keyword arguments (2 given)
Request Method: GET Request URL: http://127.0.0.1:8000/project/bootcamp/csv/ Django Version: 1.2.1 Exception Type: TypeError Exception Value:
admin_list_export() takes at least 3 non-keyword arguments (2 given)
Exception Location: /Library/Python/2.6/site-packages/django/core/handlers/base.py in get_response, line 100 Python Executable: /usr/bin/python Python Version: 2.6.1
Does anyone know how to resolve this?
#
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?
#
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
#