通过基于Django类的视图发送文件

时间:2013-04-29 19:27:53

标签: python django download django-class-based-views

我们对大多数项目使用基于类的视图。当我们尝试创建一个CSV Mixin时,我们遇到了一个问题,它允许用户将来自几乎任何页面的信息导出为CSV文件。我们的特殊问题涉及CSV文件,但我相信我的问题通用性足以与任何文件类型相关。

我们遇到的问题是视图的响应是试图转到模板(比如from django.views.generic import TemplateView)。我们在urls.py文件中指定模板。

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

如何强制响应绕过模板并返回标准HttpResponse?我猜你需要覆盖一个方法,但我不确定是哪一个。

有什么建议吗?

EDIT1:我似乎不清楚我们要做什么。我已经渲染了一个页面(通过基于类的视图),用户将看到信息报告。我需要输入一个“导出到CSV”按钮供用户按,它将在页面上导出信息并将CSV下载到他们的机器上。

不能将视图重写为基于方法的视图。我们处理几乎所有基于类的视图类型(DetailView,ListView,TemplateView,View,RedirectView等)

2 个答案:

答案 0 :(得分:13)

当您需要为相同数据提供不同的响应时,这是一个常见问题。您想要插入的点是当上下文数据已经解决但尚未构建响应时。

解析为TemplateResponseMixin的基于类的视图具有多个属性和类方法,用于控制响应对象的构造方式。不要认为名称暗示只有HTML响应或那些需要模板处理的人才能通过这种设计来促进。解决方案包括创建基于TemplateResponse类行为的自定义可重用响应类,或创建可为render_to_response方法提供自定义行为的可重用mixin。

除了编写自定义响应类之外,开发人员更经常在视图类上提供自定义render_to_response方法,或者在mixin中单独提供,因为它非常简单明了地计算出正在发生的事情。您将嗅探请求数据以查看是否必须构造某种不同类型的响应,如果不是,您只需委托默认实现来呈现模板响应。

以下是一个这样的实现可能如下所示:

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类,该类将了解所有响应类并提供仅配置{{1}的自定义render_to_response方法并将其他所有内容委托给响应类。

答案 1 :(得分: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的所有子类的通用mixin,我想你可以做类似......

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