【问题标题】:Send a file through Django Class Based Views通过 Django 基于类的视图发送文件
【发布时间】:2013-04-29 19:27:53
【问题描述】:

我们的大部分项目都使用基于类的视图。当我们尝试创建一个 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 等)

【问题讨论】:

  • 你可以创建一个 CSVResponseMixin 类并在render_to_response 方法中包含必要的逻辑来检测它是否应该返回一个 CSV 与它是否应该正常运行

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


【解决方案1】:

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

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

代替编写自定义响应类,开发人员更经常在视图类上或在 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 并委托其他所有内容到响应类。

【讨论】:

  • 自 Django 2.0 起,render_to_response 方法已被弃用。
【解决方案2】:

如何强制响应绕过模板并返回 一个标准的 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

【讨论】:

  • 这是特定于 TemplateView 的。一般有没有办法做到这一点?我需要我的视图能够返回模板化响应(取决于它的要求)。
  • 一般情况如何? View的所有子类?
  • 也许吧。我们实际上正在考虑为此简单地重定向到基于方法的视图 - 基于类的视图似乎太混乱了。这似乎是一个网站的常见要求,我很困惑没有内置的 mixin 可以处理这个问题。也许这将是我的下一个开源项目。哈哈
  • @Rico 查看更新。 TBH,我发现默认的基于类的视图很难自定义,而且它们并没有真正做超出基本 View 类的所有事情,所以我最终创建了我自己的视图层次结构,从 View 继承。跨度>
猜你喜欢
  • 2011-08-13
  • 2011-10-04
  • 2012-02-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-06
  • 2018-08-26
  • 2016-09-03
相关资源
最近更新 更多