Djangonauten: CSV-Export nach Excel mit Umlauten

Über das Web-Framework Django (djangoproject.com) ist alles wesentliche vielfach schon gesagt, wobei dieser Beitrag  hier ein paar wesentliche praktische Aspekte aus der Sicht eines Webworkers flüssig darstellt. Lesenswert!

Nun zum Problem und eigentlichen Thema dieses Artikels, nämlich der Export von Datenbanken bzw. Tabellen aus dem Django-Admin-Backend in ein CSV-verarbeitendes Programm, also Openoffice Calc oder eben Microsoft Excel. Auf djangosnippets.org findet sich ein vernünftiger Ansatz von einem gewissen “Dek“, nur leider funktioniert er nicht mit Umlautenhttp://www.djangosnippets.org/snippets/1697/

Ein Kollege, Anatoly Ivanov hat das Problem auch erkannt und präsentiert hier drei Codeschnipsel: Sein Ansatz benutzt folgerichtig das Unicode-fähige Template-System, aber er schreibt auf eine Datei.

Im Code am Ende des Artikels habe ich die beiden Ansätze kombiniert und hinsichtlich der drei kritischen Punkte bereinigt:

  • direktes Herunterladen und Speichern aus dem Django-Admin-Backend
  • Berücksichtung von Quotations und Textfeldern (mehrzeilig) aus der Datenbank
  • Ausgabeformat Excel-kompatibel (ISO-8859-1)

So sieht eine Ausgabe beispielhaft aus:

id;bundesland;name;statflag
1;Hamburg;Hamburg;0
Django Admin: CSV-Export mit Umlauten.

Django Admin: CSV-Export mit Umlauten.

Wem das | Zeichen als Ersatz für die Hochkomma nicht gefällt, oder der Stern (*) für ein Newline, kann dies selbst austauschen oder weiter ergänzen (replace_dc).

Sinn der .replace()-Funktion ist hier schlicht,  die CSV-Struktur nicht zu zerschießen, womit die ganze Datei ab der betreffenden Zeile unbrauchbar würde.

Hier der Code, mit Dank an Dek und Anatoly:

### models.py (App-Verzeichnis)
 
from testprojekt.actions import export_als_csv
 
class Testmodell (admin.ModelAdmin):
  actions = [export_als_csv]
 
...
 
### actions.py (in der Projekt-Root)
 
import csv, codecs
from django.core.exceptions import PermissionDenied
from django.http import HttpResponse
from django.template import Context, Template
 
def get_csv_from_dict_list(field_list, data):
  csv_line = ";".join(['{{ row.%s|addslashes }}' % field for field in field_list])
  template = "{% for row in data %}" + csv_line + "\n{% endfor %}"
  return Template(template).render(Context({"data" : data}))
 
def export_als_csv(modeladmin, request, queryset):
  if not request.user.is_staff:
    raise PermissionDenied
 
  replace_dc = { '\n' : '* ', '\r' : '', ';' : ',', '\"' : '|', '\'' : '|'}
  opts = modeladmin.model._meta
  response = HttpResponse(mimetype='text/csv')
  response['Content-Disposition'] = 'attachment; filename=%s.csv' % unicode(opts).replace('.', '_')
  w = csv.writer(response, delimiter=';')
  field_names = [field.name for field in opts.fields]
  w.writerow(field_names)
  ax = []
  for obj in queryset:
    acc = {}
    for field in field_names:
      uf = unicode(getattr(obj, field))
      for i, j in replace_dc.iteritems():
        uf = uf.replace(i,j)
        acc[field] = uf
        ax.append(acc)
  response.write (get_csv_from_dict_list(field_names, ax).encode("iso-8859-1"))
  return response
 
export_als_csv.short_description = "Markierte Daten als Excel (CSV) speichern"
 
#