Потоковая передача файла CSV в Django

Я пытаюсь передать файл csv в потоковом режиме в качестве загрузки вложения. Файлы CSV становятся размером 4 МБ или более, и мне нужен способ, чтобы пользователь мог активно загружать файлы, не дожидаясь, пока все данные будут созданы и сохранены в памяти.

Сначала я использовал свои собственные файловая оболочка на основе класса Django FileWrapper . Это не удалось. Затем я увидел здесь метод использования генератора для потоковой передачи ответа: Как передать HttpResponse с помощью Django

Когда я вызываю ошибку в генераторе, я вижу, что создаю правильные данные с помощью функции get_row_data () , но когда я пытаюсь вернуть ответ он возвращается пустым. Я также отключил Django GZipMiddleware . Кто-нибудь знает, что я делаю не так?

Изменить: Проблема, с которой я столкнулся, была связана с ConditionalGetMiddleware . Мне пришлось заменить его, код находится в ответе ниже.

Вот вид:

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

Вот генератор, который я использую для потоковой передачи ответа:

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)

А вот как я создаю данные строки 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()

18
задан Community 23 May 2017 в 12:25
поделиться