Login

Export queryset to Excel workbook

Author:
masida
Posted:
September 15, 2014
Language:
Python
Version:
1.7
Tags:
export queryset xlwt
Score:
0 (after 0 ratings)

How to use

Save the snippet to a file utils.py, and add the following view to your Django app:

from django.http import HttpResponse
from .utils import queryset_to_workbook

def download_workbook(request):
    queryset = User.objects.all()
    columns = (
        'first_name',
        'last_name',
        'email',
        'is_staff',
        'groups')
    workbook = queryset_to_workbook(queryset, columns)
    response = HttpResponse(mimetype='application/vnd.ms-excel')
    response['Content-Disposition'] = 'attachment; filename="export.xls"'
    workbook.save(response)
    return response

Note: you can use dotted notation ('foreign_key.foreign_key.field') in the columns parameter to access fields that are accessible through the objects returned by the queryset (in that case you probably want to use select_related with your queryset).

 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
import xlwt
import datetime

from django.forms.forms import pretty_name
from django.core.exceptions import ObjectDoesNotExist

HEADER_STYLE = xlwt.easyxf('font: bold on')
DEFAULT_STYLE = xlwt.easyxf()
CELL_STYLE_MAP = (
    (datetime.date, xlwt.easyxf(num_format_str='DD/MM/YYYY')),
    (datetime.time, xlwt.easyxf(num_format_str='HH:MM')),
    (bool,          xlwt.easyxf(num_format_str='BOOLEAN')),
)

def multi_getattr(obj, attr, default=None):
    attributes = attr.split(".")
    for i in attributes:
        try:
            obj = getattr(obj, i)
        except AttributeError:
            if default:
                return default
            else:
                raise
    return obj

def get_column_head(obj, name):
    name = name.rsplit('.', 1)[-1]
    return pretty_name(name)

def get_column_cell(obj, name):
    try:
        attr = multi_getattr(obj, name)
    except ObjectDoesNotExist:
        return None
    if hasattr(attr, '_meta'):
        # A Django Model (related object)                                                                                                                                                                          
        return unicode(attr).strip()
    elif hasattr(attr, 'all'):
        # A Django queryset (ManyRelatedManager)                                                                                                                                                                   
        return ', '.join(unicode(x).strip() for x in attr.all())
    return attr

def queryset_to_workbook(queryset, columns, header_style=None,
                         default_style=None, cell_style_map=None):
    workbook = xlwt.Workbook()
    report_date = datetime.date.today()
    sheet_name = 'Export {0}'.format(report_date.strftime('%Y-%m-%d'))
    sheet = workbook.add_sheet(sheet_name)

    if not header_style:
        header_style = HEADER_STYLE
    if not default_style:
        default_style = DEFAULT_STYLE
    if not cell_style_map:
        cell_style_map = CELL_STYLE_MAP

    obj = queryset.first()
    for y, column in enumerate(columns):
        value = get_column_head(obj, column)
        sheet.write(0, y, value, header_style)

    for x, obj in enumerate(queryset, start=1):
        for y, column in enumerate(columns):
            value = get_column_cell(obj, column)
            style = default_style
            for value_type, cell_style in cell_style_map:
                if isinstance(value, value_type):
                    style = cell_style
            sheet.write(x, y, value, style)

    return workbook

More like this

Comments

bbratchiv (on February 29, 2016):

How to make it work with annotate queries?

#

bahiamartins (on April 25, 2017):

I'm getting error when I need to show in columns "FileField.url" and "DateTimeField". I'm using Django 1.8. Any clues?

#

Please login first before commenting.