Εξάγοντας αρχεία CSV με το Django¶
Αυτό το άρθρο εξηγεί πως να εξάγετε αρχεία CSV (Comma Separated Values), δυναμικά, χρησιμοποιώντας τα Django views. Για να το κάνετε αυτό, μπορείτε είτε να χρησιμοποιήσετε την βιβλιοθήκη CSV της Python ή το σύστημα template του Django.
Χρησιμοποιώντας τη βιβλιοθήκη CSV της Python¶
Η Python έρχεται με μια βιβλιοθήκη, csv
. Το κλειδί για να την χρησιμοποιήσετε με το Django είναι ότι η δυνατότητα της δημιουργίας CSV του module csv
ενεργεί πάνω σε objects που μοιάζουν με αρχεία και τα objects της κλάσης HttpResponse
του Django είναι objects που μοιάζουν με αρχεία.
Ορίστε ένα παράδειγμα:
import csv
from django.http import HttpResponse
def some_view(request):
# Create the HttpResponse object with the appropriate CSV header.
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="somefilename.csv"'
writer = csv.writer(response)
writer.writerow(['First row', 'Foo', 'Bar', 'Baz'])
writer.writerow(['Second row', 'A', 'B', 'C', '"Testing"', "Here's a quote"])
return response
Παρόλο που ο κώδικας και τα σχόλια είναι αρκετά επεξηγηματικά, θα πρέπει να αναφέρουμε μερικά πράγματα:
Το response παίρνει έναν ειδικό τύπο MIME, text/csv. Αυτό λέει στους browsers ότι το έγγραφο είναι ένα αρχείο CSV, παρά ένα αρχείο HTML. Αν δεν το προσδιορίσετε, οι browsers, πιθανόν, να ερμηνεύσουν το reponse ως ένα αρχείο HTML, που σημαίνει ότι το αποτέλεσμα θα είναι όχι μόνο άσχημο αλλά δεν θα καταλαβαίνετε τίποτα!
Το response παίρνει έναν επιπλέον header με το όνομα
Content-Disposition
, ο οποίος περιέχει το όνομα του αρχείου CSV. Αυτό το όνομα είναι αυθαίρετο. Μπορείτε να το ονομάσετε όπως εσείς θέλετε. Θα χρησιμοποιηθεί από τους browsers στο παράθυρο διαλόγου “Αποθήκευση ως...” κλπ.Η σύνδεση με το CSV-generation API είναι εύκολη: Απλώς περάστε το
response
ως πρώτο όρισμα στη μέθοδοcsv.writer
. Η μέθοδοςcsv.writer
περιμένει ένα object τύπου αρχείου και τα objects της κλάσηςHttpResponse
θα αναλάβουν τα υπόλοιπα.Για κάθε γραμμή μέσα στο αρχείο σας CSV, καλέστε τη μέθοδο
writer.writerow
, περνώντας της ένα iterable object όπως μια λίστα ή ένα tuple.Το CSV module τακτοποιεί τους χαρακτήρες των εισαγωγικών (
’’
ή””
) για εσάς. Οπότε, δεν χρειάζεται να ανησυχείτε για τυχόν escaping των strings σας με εισαγωγικά ή κόμματα μέσα σε αυτά. Απλώς, περάστε τα στη μέθοδοwriterow()
ως έχουν και όλα θα τακτοποιηθούν αυτόματα.
Δουλεύοντας με Unicode στην Python 2
Το csv
module της Python 2 δεν υποστηρίζει Unicode εισόδους (ήτοι χαρακτήρες εκτός ASCII κώδικα). Αφού το Django χρησιμοποιεί, εσωτερικώς, Unicode, αυτό σημαίνει ότι τα strings που διαβάζονται από διάφορες πηγές, όπως το HttpRequest
θα είναι πιθανόν προβληματικά. Υπάρχουν μερικοί τρόποι επίλυσης αυτού:
Κωδικοποιήστε χειροκίνητα όλα τα Unicode objects σε μια συμβατική κωδικοποίηση.
Χρησιμοποιήστε την κλάση
UnicodeWriter
που παρέχετε από την ενότητα παραδειγμάτων του module csv.Χρησιμοποιήστε το python-unicodecsv module, το οποίο εκτός του ότι στοχεύει να αντικαταστήσει το module
csv
, μπορεί να χειριστεί το Unicode πολύ καλά.
Για περισσότερες πληροφορίες, δείτε στο εγχειρίδιο της Python σχετικά με το csv
module.
Κάνοντας streaming σε μεγάλα αρχεία CSV¶
Όταν έχουμε views τα οποία παράγουν πολύ μεγάλα responses, ίσως θα θέλατε να χρησιμοποιήσετε το StreamingHttpResponse
του Django. Για παράδειγμα, όταν κάνετε streaming ένα αρχείο το οποίο παίρνει αρκετή ώρα για να παραχθεί, μπορείτε να αποφύγετε την διακοπή σύνδεσης του load balancer (Error 522: Connection timed out) που θα προέκυπτε όσο ο server σας παράγει το response.
Σε αυτό το παράδειγμα, χρησιμοποιούμε αμιγώς τους Python generators για να χειριστούμε αποδοτικά την συναρμολόγηση και εκπομπή ενός μεγάλου, σε μέγεθος, αρχείου CSV:
import csv
from django.utils.six.moves import range
from django.http import StreamingHttpResponse
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 some_streaming_csv_view(request):
"""A view that streams a large CSV file."""
# Generate a sequence of rows. The range is based on the maximum number of
# rows that can be handled by a single sheet in most spreadsheet
# applications.
rows = (["Row {}".format(idx), str(idx)] for idx in range(65536))
pseudo_buffer = Echo()
writer = csv.writer(pseudo_buffer)
response = StreamingHttpResponse((writer.writerow(row) for row in rows),
content_type="text/csv")
response['Content-Disposition'] = 'attachment; filename="somefilename.csv"'
return response
Χρησιμοποιώντας το σύστημα template του Django¶
Εναλλακτικά, μπορείτε να χρησιμοποιήσετε το σύστημα template του Django για να δημιουργήσετε CSV αρχεία. Αυτή η μέθοδος είναι χαμηλού επιπέδου (low-level) εν συγκρίσει με τη χρήση του, βολικού, module csv
της Python, αλλά παρουσιάζεται εδώ για λόγους πληρότητας.
Η ιδέα εδώ είναι να περάσετε μια λίστα από items στο template σας και να βάλετε το template να εισάγει τα κόμματα μέσα σε ένα βρόγχο επανάληψης, for
.
Εδώ φαίνεται ένα παράδειγμα, στο οποίο παράγεται το ίδιο αρχείο CSV όπως παραπάνω:
from django.http import HttpResponse
from django.template import loader, Context
def some_view(request):
# Create the HttpResponse object with the appropriate CSV header.
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="somefilename.csv"'
# The data is hard-coded here, but you could load it from a database or
# some other source.
csv_data = (
('First row', 'Foo', 'Bar', 'Baz'),
('Second row', 'A', 'B', 'C', '"Testing"', "Here's a quote"),
)
t = loader.get_template('my_template_name.txt')
c = Context({
'data': csv_data,
})
response.write(t.render(c))
return response
Η μόνη διαφορά αυτού του παραδείγματος με το προηγούμενο είναι ότι αυτό χρησιμοποιεί το template loading αντί του CSV module. Ο υπόλοιπος κώδικας – όπως το content_type='text/csv'
– παραμένει ο ίδιος.
Έπειτα, δημιουργήστε ένα template my_template_name.txt
, με τον template κώδικα, ως εξής:
{% for row in data %}"{{ row.0|addslashes }}", "{{ row.1|addslashes }}", "{{ row.2|addslashes }}", "{{ row.3|addslashes }}", "{{ row.4|addslashes }}"
{% endfor %}
Αυτό το template είναι αρκετά απλό. Απλώς κάνει iterate πάνω στα δοσμένα δεδομένα (data
) και εμφανίζει μια γραμμή CSV για κάθε γραμμή. Χρησιμοποιεί το φίλτρο addslashes
για την επιβεβαίωση μη ύπαρξης προβλημάτων με τυχόν μονά ή διπλά εισαγωγικά.
Άλλες μορφές τύπου κειμένου¶
Όπως είδατε, δεν υπάρχει κάτι συγκεκριμένο με το CSV εδώ – απλώς μια συγκεκριμένη μορφή εξόδου. Μπορείτε να χρησιμοποιήσετε τις ανωτέρω τεχνικές για να εξάγετε οποιαδήποτε μορφή τύπου κειμένου εσείς θέλετε. Μπορείτε, επίσης, να χρησιμοποιήσετε μια παρόμοια τεχνική για να παράγετε αυθαίρετα δυαδικά δεδομένα. Δείτε στο άρθρο Εξάγοντας αρχεία PDF με το Django για ένα παράδειγμα.