2013-04-29 96 views
3

我們對大部分項目都使用基於類的視圖。當我們嘗試創建CSV Mixin時,我們遇到了一個問題,它允許用戶將信息從幾乎任何頁面導出爲CSV文件。我們特別的問題涉及CSV文件,但我相信我的問題足以與任何文件類型相關。通過Django Class Based Views發送文件

我們遇到的問題是來自視圖的響應正試圖去模板(比如from django.views.generic import TemplateView)。我們在urls.py文件中指定模板。

url(r'^$', MyClassBasedView.as_view(template_name='my_template.html')) 

如何強制響應繞過模板並返回標準HttpResponse?我猜你需要重寫一個方法,但我不確定哪一個。

有什麼建議嗎?

編輯1:看起來我不清楚我們正在做什麼。我已經呈現了一個頁面(通過基於類的視圖),用戶將看到信息報告。我需要輸入一個按鈕「導出爲CSV」供用戶按下,它將導出他們頁面上的信息並將CSV下載到他們的機器上。

這不是一種將視圖重寫爲基於方法的視圖的選項。我們在處理與基於幾乎所有類視圖類型(的DetailView,ListView中,TemplateView,景觀,RedirectView的,等等)

+1

你可以創建一個CSVResponseMixin類,並且在'render_to_response'方法中它包含檢測它是否應該返回一個CSV與我們應該正常工作的邏輯所需的邏輯 – dm03514 2013-04-29 19:49:29

回答

10

這是一個通用的問題,當你需要爲相同的數據提供不同的響應。您想要插入的點是上下文數據已經解析但尚未構建響應的時間點。

解析爲TemplateResponseMixin的基於類的視圖有幾個屬性和類方法來控制如何構造響應對象。不要被認爲名稱意味着只有HTML響應或需要模板處理的響應只能通過此設計來實現。解決方案的範圍可以從創建基於TemplateResponse類的行爲的自定義可重用響應類,或創建可爲render_to_response方法提供自定義行爲的可重用混合類。

代替編寫自定義響應類,開發人員通常在視圖類中或在mixin中單獨提供自定義的render_to_response方法,因爲它非常簡單直觀地找出發生了什麼。您將嗅探請求數據以查看是否必須構建一些不同類型的響應,如果不是,則只需將其委託給默認實現以呈現模板響應。

這裏有一個這樣的實現可能看起來怎麼樣:

import csv 
from django.http import HttpResponse 
from django.utils.text import slugify 
from django.views.generic import TemplateView 


class CSVResponseMixin(object): 
    """ 
    A generic mixin that constructs a CSV response from the context data if 
    the CSV export option was provided in the request. 
    """ 
    def render_to_response(self, context, **response_kwargs): 
     """ 
     Creates a CSV response if requested, otherwise returns the default 
     template response. 
     """ 
     # Sniff if we need to return a CSV export 
     if 'csv' in self.request.GET.get('export', ''): 
      response = HttpResponse(content_type='text/csv') 
      response['Content-Disposition'] = 'attachment; filename="%s.csv"' % slugify(context['title']) 

      writer = csv.writer(response) 
      # Write the data from the context somehow 
      for item in context['items']: 
       writer.writerow(item) 

      return response 
     # Business as usual otherwise 
     else: 
      return super(CSVResponseMixin, self).render_to_response(context, **response_kwargs): 

在這裏你還可以看到時,可能需要自定義響應類別更精細的設計。雖然這適用於爲自定義響應類型添加臨時支持,但如果您希望支持五種不同的響應類型,則此功能無法很好地擴展。

在這種情況下,你會創建並測試不同的響應班和編寫一個CustomResponsesMixin類會知道所有的響應類和提供自定義render_to_response方法,只有配置self.response_class和代表一切的響應類。

1

你怎麼能強制響應繞過模板,就回到 標準的HttpResponse?

這有點違背了使用TemplateView的觀點。如果你試圖返回的東西不是模板化的迴應,那麼它應該是一個不同的觀點。

但是......

我猜你需要重寫的方法,但我不知道是哪一個。

...如果你喜歡它侵入現有TemplateView,從源代碼注意...

class TemplateView(TemplateResponseMixin, ContextMixin, View): 
    """ 
    A view that renders a template. This view will also pass into the context 
    any keyword arguments passed by the url conf. 
    """ 
    def get(self, request, *args, **kwargs): 
     context = self.get_context_data(**kwargs) 
     return self.render_to_response(context) 

...所以你不得不重寫get()方法,因此在返回您的CSV時不會撥打render_to_response()。例如...

class MyClassBasedView(TemplateView): 
    def get(self, request, *args, **kwargs): 
     if request.GET['csv'].lower() == 'true': 
      # Build custom HTTP response 
      return my_custom_response 
     else: 
      return TemplateView.get(request, *args, **kwargs) 

如果您需要的View所有子類的通用混入,我想你可以做這樣的事情......

class MyMixin(object): 
    def dispatch(self, request, *args, **kwargs): 
     if request.GET['csv'].lower() == 'true': 
      # Build custom HTTP response 
      return my_custom_response 
     else: 
      return super(MyMixin, self).dispatch(request, *args, **kwargs) 

class MyClassBasedView(MyMixin, TemplateView): 
    pass 
+0

這是'TemplateView'特有的。有沒有辦法做到這一般?我需要我的視圖能夠返回模板響應或不響應(取決於它的要求)。 – Rico 2013-04-29 20:18:23

+0

如何一般? View的所有子類? – Aya 2013-04-29 20:20:59

+0

也許吧。實際上我們正在考慮一個簡單的重定向到基於方法的視圖 - 這對於基於類的視圖來說似乎太混亂了。這似乎是一個網站的常見要求,我很困惑,沒有內置的mixin來處理這個問題。也許這將是我的下一個開源項目。哈哈 – Rico 2013-04-29 20:23:38