2011-02-28 37 views
17

Tôi đang cố phát tệp csv dưới dạng tệp tải xuống tệp đính kèm. Các tệp CSV có kích thước 4MB trở lên và tôi cần một cách để người dùng chủ động tải xuống tệp mà không phải chờ tất cả dữ liệu được tạo và cam kết với bộ nhớ trước.Truyền trực tuyến tệp CSV ở Django

Lần đầu tiên tôi sử dụng trình bao bọc tệp của riêng mình dựa trên lớp FileWrapper của Django. Điều đó thất bại. Sau đó, tôi nhìn thấy một phương pháp vào đây để sử dụng một máy phát điện để dòng phản ứng: How to stream an HttpResponse with Django

Khi tôi nâng cao một lỗi trong máy phát điện, tôi có thể thấy rằng tôi đang tạo ra các dữ liệu phù hợp với get_row_data() chức năng, nhưng khi tôi cố gắng trả về phản hồi nó trở lại trống rỗng. Tôi cũng đã vô hiệu hóa Django GZipMiddleware. Có ai biết tôi đang làm gì sai không?

Chỉnh sửa: Vấn đề tôi gặp phải là với ConditionalGetMiddleware. Tôi đã phải thay thế nó, mã trong một câu trả lời dưới đây.

Dưới đây là quan điểm:

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 

Dưới đây là các máy phát điện tôi sử dụng để truyền các phản ứng:

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) 

Và đây là làm thế nào tôi có thể tạo dữ liệu csv hàng:

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() 

Trả lời

30

Dưới đây là một số mã đơn giản sẽ phát trực tuyến CSV; bạn có thể đi từ điều này đến bất cứ điều gì bạn cần làm:

import cStringIO as StringIO 
import csv 

def csv(request): 
    def data(): 
     for i in xrange(10): 
      csvfile = StringIO.StringIO() 
      csvwriter = csv.writer(csvfile) 
      csvwriter.writerow([i,"a","b","c"]) 
      yield csvfile.getvalue() 

    response = HttpResponse(data(), mimetype="text/csv") 
    response["Content-Disposition"] = "attachment; filename=test.csv" 
    return response 

Điều này chỉ đơn giản viết mỗi hàng vào một tệp trong bộ nhớ, đọc hàng và tạo ra nó.

Phiên bản này là hiệu quả hơn để tạo ra dữ liệu số lượng lớn, nhưng hãy chắc chắn để hiểu được ở trên trước khi sử dụng nó:

import cStringIO as StringIO 
import csv 

def csv(request): 
    csvfile = StringIO.StringIO() 
    csvwriter = csv.writer(csvfile) 

    def read_and_flush(): 
     csvfile.seek(0) 
     data = csvfile.read() 
     csvfile.seek(0) 
     csvfile.truncate() 
     return data 

    def data(): 
     for i in xrange(10): 
      csvwriter.writerow([i,"a","b","c"]) 
     data = read_and_flush() 
     yield data 

    response = HttpResponse(data(), mimetype="text/csv") 
    response["Content-Disposition"] = "attachment; filename=test.csv" 
    return response 
+0

Tôi chưa có nhu cầu truyền dữ liệu, nhưng thật tuyệt khi biết nhanh như thế nào để có được thứ gì đó đơn giản và thanh lịch. –

+0

Mặc dù tôi thực sự thích câu trả lời này, nó chỉ ra rằng đây không phải là vấn đề của tôi. Tôi đã sử dụng chính xác mã này chính xác mà bạn đã viết, chỉ để xem liệu nó có tạo ra một phản hồi hay không, nhưng phản hồi trả về là 0 byte. Vì vậy, tôi vẫn bị mắc kẹt với cùng một kết quả. – bfrederix

+0

Mã này hoạt động tốt, vì vậy có điều gì đó không ổn với môi trường của bạn mà bạn sẽ cần khắc phục sự cố. –

2

Vấn đề tôi đã có được với ConditionalGetMiddleware. Tôi thấy django piston đi lên với một middleware thay thế cho các ConditionalGetMiddleware cho phép trực tiếp:

from django.middleware.http import ConditionalGetMiddleware 

def compat_middleware_factory(klass): 
    """ 
    Class wrapper that only executes `process_response` 
    if `streaming` is not set on the `HttpResponse` object. 
    Django has a bad habbit of looking at the content, 
    which will prematurely exhaust the data source if we're 
    using generators or buffers. 
    """ 
    class compatwrapper(klass): 
     def process_response(self, req, resp): 
      if not hasattr(resp, 'streaming'): 
       return klass.process_response(self, req, resp) 
      return resp 
    return compatwrapper 

ConditionalMiddlewareCompatProxy = compat_middleware_factory(ConditionalGetMiddleware) 

Vì vậy, sau đó bạn sẽ thay thế ConditionalGetMiddleware với middleware ConditionalMiddlewareCompatProxy của bạn, và theo quan điểm của bạn (mã vay mượn từ một câu trả lời thông minh cho câu hỏi này):

def csv_view(request): 
    def data(): 
     for i in xrange(10): 
      csvfile = StringIO.StringIO() 
      csvwriter = csv.writer(csvfile) 
      csvwriter.writerow([i,"a","b","c"]) 
      yield csvfile.getvalue() 

    #create the reponse object with a csv mimetype 
    response = HttpResponse(
     data(), 
     mimetype='text/csv', 
     ) 
    #Set the response as an attachment with a filename 
    response['Content-Disposition'] = "attachment; filename=test.csv" 
    response.streaming = True 
    return response 
11

Sự cố phần giữa đã được giải quyết là Django 1.5 và StreamingHttpResponse đã được giới thiệu. Sau đây nên làm:

import cStringIO as StringIO 
import csv 

def csv_view(request): 
    ... 
    # Assume `rows` is an iterator or lists 
    def stream(): 
     buffer_ = StringIO.StringIO() 
     writer = csv.writer(buffer_) 
     for row in rows: 
      writer.writerow(row) 
      buffer_.seek(0) 
      data = buffer_.read() 
      buffer_.seek(0) 
      buffer_.truncate() 
      yield data 
    response = StreamingHttpResponse(
     stream(), content_type='text/csv' 
    ) 
    disposition = "attachment; filename=file.csv" 
    response['Content-Disposition'] = disposition 
    return response 

Có một số tài liệu trên how to output csv from Django nhưng nó không tận dụng lợi thế của StreamingHttpResponse vì vậy tôi đã đi trước và opened a ticket in order to track it.

Các vấn đề liên quan