Sto tentando di eseguire lo streaming di un file csv come download di allegati. I file CSV hanno dimensioni pari o superiori a 4 MB e ho bisogno di un modo per consentire all'utente di scaricare attivamente i file senza attendere che tutti i dati vengano creati e memorizzati prima sulla memoria.Streaming di un file CSV in Django
Ho usato per la prima volta il mio file wrapper basato sulla classe FileWrapper
di Django. Questo fallì. Poi ho visto un metodo qui per l'utilizzo di un generatore per lo streaming la risposta: How to stream an HttpResponse with Django
Quando alzo un errore all'interno del generatore, posso vedere che sto creando i dati corretti con la funzione get_row_data()
, ma quando provo a restituire la risposta torna vuota. Ho anche disabilitato il Django GZipMiddleware
. Qualcuno sa cosa sto sbagliando?
Modifica: il problema riscontrato riguardava lo ConditionalGetMiddleware
. Ho dovuto sostituirlo, il codice è in una risposta qui sotto.
Qui è la vista:
from django.views.decorators.http import condition
@condition(etag_func=None)
def csv_view(request, app_label, model_name):
""" Based on the filters in the query, return a csv file for the given model """
#Get the model
model = models.get_model(app_label, model_name)
#if there are filters in the query
if request.method == 'GET':
#if the query is not empty
if request.META['QUERY_STRING'] != None:
keyword_arg_dict = {}
for key, value in request.GET.items():
#get the query filters
keyword_arg_dict[str(key)] = str(value)
#generate a list of row objects, based on the filters
objects_list = model.objects.filter(**keyword_arg_dict)
else:
#get all the model's objects
objects_list = model.objects.all()
else:
#get all the model's objects
objects_list = model.objects.all()
#create the reponse object with a csv mimetype
response = HttpResponse(
stream_response_generator(model, objects_list),
mimetype='text/plain',
)
response['Content-Disposition'] = "attachment; filename=foo.csv"
return response
Ecco il generatore che uso per lo streaming la risposta:
def stream_response_generator(model, objects_list):
"""Streaming function to return data iteratively """
for row_item in objects_list:
yield get_row_data(model, row_item)
time.sleep(1)
Ed ecco come creo i dati fila csv:
def get_row_data(model, row):
"""Get a row of csv data from an object"""
#Create a temporary csv handle
csv_handle = cStringIO.StringIO()
#create the csv output object
csv_output = csv.writer(csv_handle)
value_list = []
for field in model._meta.fields:
#if the field is a related field (ForeignKey, ManyToMany, OneToOne)
if isinstance(field, RelatedField):
#get the related model from the field object
related_model = field.rel.to
for key in row.__dict__.keys():
#find the field in the row that matches the related field
if key.startswith(field.name):
#Get the unicode version of the row in the related model, based on the id
try:
entry = related_model.objects.get(
id__exact=int(row.__dict__[key]),
)
except:
pass
else:
value = entry.__unicode__().encode("utf-8")
break
#if it isn't a related field
else:
#get the value of the field
if isinstance(row.__dict__[field.name], basestring):
value = row.__dict__[field.name].encode("utf-8")
else:
value = row.__dict__[field.name]
value_list.append(value)
#add the row of csv values to the csv file
csv_output.writerow(value_list)
#Return the string value of the csv output
return csv_handle.getvalue()
Non ho ancora avuto la necessità di eseguire lo streaming dei dati, ma è bello sapere quanto sia veloce ottenere qualcosa che sia semplice ed elegante. –
Anche se mi piace molto questa risposta, si scopre che questo non è un mio problema. Ho letteralmente usato questo codice esatto che hai scritto, solo per vedere se avrebbe generato una risposta, ma la risposta ritorna come 0 byte. Quindi sono ancora bloccato con lo stesso risultato. – bfrederix
Questo codice funziona bene, quindi c'è qualcosa di sbagliato nel tuo ambiente che dovrai risolvere. –