Ultimate(?) export/download CSV admin action

  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 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

  1. Generic CSV export admin action factory with labels by losttrekker 2 years, 1 month ago
  2. Admin action for a generic "CSV Export" by javinievas 3 years, 2 months ago
  3. Generic CSV export admin action factory with relationship spanning fields and labels by blackrobot 1 year, 4 months ago
  4. Generic admin action export selected rows to excel by jordic 2 years, 6 months ago
  5. Admin action for a generic "CSV Export" (fix for unicode) by __alexander__ 4 months, 3 weeks ago

Comments

piotr.szwed (on October 22, 2013):

the following import is missing:

from django.http import HttpResponse

#

Ambroise (on October 28, 2013):

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

#

chriswhsu (on January 24, 2014):

Great snippet. Thanks.

For my purposes I've changed this line in prep_field:

return unicode(output).encode('utf-8') if output else ""

To this:

return unicode(output).encode('utf-8') if output is not None else ""

In order to prevent Boolean 'False' or Numeric 0 values from ending up as empty fields.

#

(Forgotten your password?)