- Author:
- anentropic
- Posted:
- October 2, 2013
- Language:
- Python
- Version:
- 1.5
- Score:
- 0 (after 0 ratings)
This owes a debt to a number of earlier snippets by myself and others, including: (most directly) #2868, plus #2020, #2712, #1697
Use of OrderedDict means it requires Python 2.7+. You also need to pip install singledispatch
which is a backport of a Python 3.4 feature.
Singledispatch (along with custom attributes instead of a factory function) gives a very clean interface in your ModelAdmin, whether or not you need a custom short_description
.
This version allows you to (optionally) specify custom column labels, or to (optionally) use Django's usual prettifying mechanism (field.verbose_name
).
Thanks to #2868 it can do relation-spanning double-underscore lookups and also model attribute/method (rather than field) lookups for columns, just like you can in your ModelAdmin list_display
.
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 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | import csv
from collections import OrderedDict
from functools import wraps
from django.db.models import FieldDoesNotExist
from django.http import HttpResponse
from singledispatch import singledispatch # pip install singledispatch
def prep_field(obj, field):
"""
(for download_as_csv action)
Returns the field as a unicode string. If the field is a callable, it
attempts to call it first, without arguments.
"""
if '__' in field:
bits = field.split('__')
field = bits.pop()
for bit in bits:
obj = getattr(obj, bit, None)
if obj is None:
return ""
attr = getattr(obj, field)
output = attr() if callable(attr) else attr
return unicode(output).encode('utf-8') if output is not None else ""
@singledispatch
def download_as_csv(modeladmin, request, queryset):
"""
Generic csv export admin action.
Example:
class ExampleModelAdmin(admin.ModelAdmin):
raw_id_fields = ('field1',)
list_display = ('field1', 'field2', 'field3',)
actions = [download_as_csv,]
download_as_csv_fields = [
'field1',
('foreign_key1__foreign_key2__name', 'label2'),
('field3', 'label3'),
],
download_as_csv_header = True
"""
fields = getattr(modeladmin, 'download_as_csv_fields', None)
exclude = getattr(modeladmin, 'download_as_csv_exclude', None)
header = getattr(modeladmin, 'download_as_csv_header', True)
verbose_names = getattr(modeladmin, 'download_as_csv_verbose_names', True)
opts = modeladmin.model._meta
def fname(field):
if verbose_names:
return unicode(field.verbose_name).capitalize()
else:
return field.name
# field_names is a map of {field lookup path: field label}
if exclude:
field_names = OrderedDict(
(f.name, fname(f)) for f in opts.fields if f not in exclude
)
elif fields:
field_names = OrderedDict()
for spec in fields:
if isinstance(spec, (list, tuple)):
field_names[spec[0]] = spec[1]
else:
try:
f, _, _, _ = opts.get_field_by_name(spec)
except FieldDoesNotExist:
field_names[spec] = spec
else:
field_names[spec] = fname(f)
else:
field_names = OrderedDict(
(f.name, fname(f)) for f in opts.fields
)
response = HttpResponse(mimetype='text/csv')
response['Content-Disposition'] = 'attachment; filename=%s.csv' % (
unicode(opts).replace('.', '_')
)
writer = csv.writer(response)
if header:
writer.writerow(field_names.values())
for obj in queryset:
writer.writerow([prep_field(obj, field) for field in field_names.keys()])
return response
download_as_csv.short_description = "Download selected objects as CSV file"
@download_as_csv.register(basestring)
def _(description):
"""
(overridden dispatcher)
Factory function for making a action with custom description.
Example:
class ExampleModelAdmin(admin.ModelAdmin):
raw_id_fields = ('field1',)
list_display = ('field1', 'field2', 'field3',)
actions = [download_as_csv("Export Special Report"),]
download_as_csv_fields = [
'field1',
('foreign_key1__foreign_key2__name', 'label2'),
('field3', 'label3'),
],
download_as_csv_header = True
"""
@wraps(download_as_csv)
def wrapped_action(modeladmin, request, queryset):
return download_as_csv(modeladmin, request, queryset)
wrapped_action.short_description = description
return wrapped_action
|
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
the following import is missing:
#
Hi, thanks for the wonderful snippet. I have a model wich I want to export, containing a many2many relation to 0-n objects, and I need to export some of their infos as well... How could I make it work ? Thanks in advance,
regards
#
Great snippet. Thanks.
For my purposes I've changed this line in prep_field:
To this:
In order to prevent Boolean 'False' or Numeric 0 values from ending up as empty fields.
#
@chriswhsu thanks, I think that makes sense so I've updated with that change
#
Please login first before commenting.