This will generically add csv exporting to your views in the admin. It will default to exporting the entire table you see (without paging). If the table only has one column, it will export the fields the the model. You can overide this functionality.
I ended up creating my own admin/change_list.html to apply this functionality universally:
{% extends "admin/base_site.html" %} {% load adminmedia admin_list i18n %} {% block stylesheet %}{% admin_media_prefix %}css/changelists.css{% endblock %} {% block bodyclass %}change-list{% endblock %} {% if not is_popup %}{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">{% trans "Home" %}</a> › {{ cl.opts.verbose_name_plural|capfirst|escape }}</div>{% endblock %}{% endif %} {% block coltype %}flex{% endblock %} {% block content %} <div id="content-main"> {% 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 %} <div class="module{% if cl.has_filters %} filtered{% endif %}" id="changelist"> {% block search %}{% search_form cl %}{% endblock %} {% block date_hierarchy %}{% date_hierarchy cl %}{% endblock %} {% block filters %}{% filters cl %}{% endblock %} {% block result_list %}{% result_list cl %}{% endblock %} {% block pagination %}{% pagination cl %}{% endblock %} </div> </div> {% endblock %}
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
- Template tag - list punctuation for a list of items by shapiromatron 10 months, 2 weeks ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 10 months, 3 weeks ago
- Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
- Help text hyperlinks by sa2812 1 year, 6 months ago
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.site
object (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():
#
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
#
Please login first before commenting.