6

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

我们遇到的问题是来自视图的响应正试图转到模板(比如 like 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 等)

4

2 回答 2

16

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

解析为 的基于类的视图TemplateResponseMixin有几个属性和类方法来控制响应对象的构造方式。不要以为这个名字意味着只有 HTML 响应或需要模板处理的响应只能通过这种设计来实现。解决方案的范围可以从创建基于类行为的自定义、可重用响应类TemplateResponse或创建为方法提供自定义行为的可重用 mixin render_to_response

代替编写自定义响应类,开发人员更经常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将其他所有内容配置和委托给响应类。

于 2013-04-29T20:48:05.113 回答
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)

...因此您必须覆盖该方法,以便在返回 CSV 时get()不会调用它。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)

如果你需要一个通用的 mixin 的所有子类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
于 2013-04-29T20:02:24.237 回答