Login

A streamable "export to CSV" admin action

Author:
namlook
Posted:
December 1, 2016
Language:
Python
Version:
1.10
Score:
0 (after 0 ratings)

This snippets is inspired from #2995 but add the following:

  • get rid of singledispatch so we can use it with python2 without pip installing anything
  • streaming capabilities
  • the configuration params (header, fields, exclude...) are passed to the action function so we don't pollute the ModelAdmin class
  • fix utf8 issue in header
  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
import csv
from collections import OrderedDict
from django.db.models import FieldDoesNotExist
from django.http import StreamingHttpResponse


def prep_field(obj, field):
    """
    (for export_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 ""



class Echo(object):
    """An object that implements just the write method of the file-like
    interface.
    """
    def write(self, value):
        """Write the value by returning it, instead of storing in a buffer."""
        return value


def export_as_csv(description, fields=None, exclude=None, use_verbose_names=True, include_header=True):
    def _export_as_csv(modeladmin, request, queryset):
        """
        Usage:

            class ExampleModelAdmin(admin.ModelAdmin):
                list_display = ('field1', 'field2', 'field3',)
                actions = [
                    export_as_csv(
                        'export to csv',
                        fields=['field1', '('foreign_key1__foreign_key2__name', 'label2'),],
                        include_header=True
                    )
                ]
        """
        opts = modeladmin.model._meta

        def field_name(field):
            if use_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, field_name(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] = field_name(f)
        else:
            field_names = OrderedDict(
                (f.name, field_name(f)) for f in opts.fields
            )

        pseudo_buffer = Echo()
        writer = csv.writer(pseudo_buffer)

        def content_iterator():
            if include_header:
                yield [i.encode('utf8') for i in field_names.values()]
            for obj in queryset.iterator():
                yield [prep_field(obj, field) for field in field_names.keys()]

        response = StreamingHttpResponse(
            (writer.writerow(line) for line in content_iterator()),
            content_type='text/csv'
        )
        response['Content-Disposition'] = 'attachment; filename=%s.csv' % (unicode(opts).replace('.', '_'))
        return response

    _export_as_csv.short_description = description
    return _export_as_csv

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 10 months, 1 week ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 10 months, 2 weeks ago
  3. Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
  5. Help text hyperlinks by sa2812 1 year, 6 months ago

Comments

Please login first before commenting.