2013-04-29 36 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