【问题标题】:Django Pandas to http response (download file)Django Pandas 到 http 响应(下载文件)
【发布时间】:2016-05-17 23:51:31
【问题描述】:

Python:2.7.11

Django:1.9

熊猫:0.17.1

我应该如何创建一个可能很大的 xlsx 文件下载?我正在从字典列表中创建一个带有熊猫的 xlsx 文件,现在需要为用户提供下载它的可能性。该列表在一个变量中,不允许保存在本地(在服务器上)。

例子:

df = pandas.DataFrame(self.csvdict)
writer = pandas.ExcelWriter('pandas_simple.xlsx', engine='xlsxwriter')
df.to_excel(writer, sheet_name='Sheet1')
writer.save()

此示例将创建文件并将其保存在执行脚本所在的位置。我需要将其创建为 http 响应,以便用户获得下载提示。

我发现了一些关于为 xlsxwriter 执行此操作的帖子,但不是为 pandas 执行此操作的帖子。我也认为我应该为此使用“StreamingHttpResponse”而不是“HttpResponse”。

【问题讨论】:

  • 如果文件非常大,您可以考虑使用 CSV 而不是 xlsx。我注意到 Pandas 使用 CSV 比使用 Excel 更快。至少在我的电脑上。

标签: python django pandas


【解决方案1】:

我将详细说明@jmcnamara 所写的内容。这适用于最新版本的 Excel、Pandas 和 Django。导入语句将位于您的 views.py 的顶部,其余代码可能位于视图中:

import pandas as pd
from django.http import HttpResponse
try:
    from io import BytesIO as IO # for modern python
except ImportError:
    from io import StringIO as IO # for legacy python

# this is my output data a list of lists
output = some_function()
df_output = pd.DataFrame(output)

# my "Excel" file, which is an in-memory output file (buffer) 
# for the new workbook
excel_file = IO()

xlwriter = pd.ExcelWriter(excel_file, engine='xlsxwriter')

df_output.to_excel(xlwriter, 'sheetname')

xlwriter.save()
xlwriter.close()

# important step, rewind the buffer or when it is read() you'll get nothing
# but an error message when you try to open your zero length file in Excel
excel_file.seek(0)

# set the mime type so that the browser knows what to do with the file
response = HttpResponse(excel_file.read(), content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')

# set the file name in the Content-Disposition header
response['Content-Disposition'] = 'attachment; filename=myfile.xlsx'

return response

【讨论】:

  • 一个小修正:对于旧版python,它必须是:from io import StringIO as IO
【解决方案2】:

Jmcnamara 正在为您指明正确的方向。翻译成您正在寻找以下代码的问题:

sio = StringIO()
PandasDataFrame = pandas.DataFrame(self.csvdict)
PandasWriter = pandas.ExcelWriter(sio, engine='xlsxwriter')
PandasDataFrame.to_excel(PandasWriter, sheet_name=sheetname)
PandasWriter.save()

sio.seek(0)
workbook = sio.getvalue()

response = StreamingHttpResponse(workbook, content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
response['Content-Disposition'] = 'attachment; filename=%s' % filename

请注意,您将数据保存到 StringIO 变量而不是文件位置。这样可以防止在生成响应之前保存文件。

【讨论】:

  • 这在 Python 3 中如何工作?我收到此错误消息:“需要字符串参数,得到 'bytes'”
  • @Johan 使用 BytesIO 而不是 StringIO。
【解决方案3】:

使用 Pandas 0.17+,您可以使用 StringIO/BytesIO 对象作为pd.ExcelWriter 的文件句柄。例如:

import pandas as pd
import StringIO

output = StringIO.StringIO()

# Use the StringIO object as the filehandle.
writer = pd.ExcelWriter(output, engine='xlsxwriter')

# Write the data frame to the StringIO object.
pd.DataFrame().to_excel(writer, sheet_name='Sheet1')
writer.save()
xlsx_data = output.getvalue()

print len(xlsx_data)

之后遵循 XlsxWriter Python 2/3 HTTP examples

对于旧版本的 Pandas,您可以使用 workaround

【讨论】:

  • 您的示例运行良好。我得到了一个空的 xlsx 文件。我现在的问题是,除了我的字典列表,我怎么能使用它?在我的示例中是 'self.csvdict' 字典列表。
【解决方案4】:

只是想分享一个基于类的视图方法,使用上面答案中的元素。只需覆盖 Django Viewget 方法即可。我的模型有一个 JSON 字段,其中包含使用 to_json 方法将数据帧转储到 JSON 的结果。

Python 版本是 3.6 和 Django 1.11。

# models.py
from django.db import models
from django.contrib.postgres.fields import JSONField

class myModel(models.Model):
    json_field = JSONField(verbose_name="JSON data")

# views.py
import pandas as pd
from io import BytesIO as IO
from django.http import HttpResponse
from django.views import View

from .models import myModel

class ExcelFileDownloadView(View):
    """
    Allows the user to download records in an Excel file
    """

    def get(self, request, *args, **kwargs):

        obj = myModel.objects.get(pk=self.kwargs['pk'])
        excel_file = IO()
        xlwriter = pd.ExcelWriter(excel_file, engine='xlsxwriter')
        pd.read_json(obj.json_field).to_excel(xlwriter, "Summary")
        xlwriter.save()
        xlwriter.close()

        excel_file.seek(0)

        response = HttpResponse(excel_file.read(),
                                content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')

        response['Content-Disposition'] = 'attachment; filename="excel_file.xlsx"'
        return response

# urls.py
from django.conf.urls import url
from .views import ExcelFileDownloadView

urlpatterns = [
    url(r'^mymodel/(?P<pk>\d+)/download/$', ExcelFileDownloadView.as_view(), name="excel-download"),]

【讨论】:

    【解决方案5】:

    可能有点跑题了,但值得指出的是,to_csv 方法通常比to_excel 快​​,因为 excel 包含工作表的格式信息。如果您只有数据而没有格式信息,请考虑to_csv。 Microsoft Excel 可以毫无问题地查看和编辑 csv 文件。

    使用to_csv 的一个好处是to_csv 函数可以将任何类似文件的对象作为第一个参数,而不仅仅是文件名字符串。由于 Django 响应对象是类似文件的,to_csv 函数可以直接写入它。您的视图函数中的一些代码如下所示:

    df = <your dataframe to be downloaded>
    response = HttpResponse(content_type='text/csv')
    response['Content-Disposition'] = 'attachment; filename=<default filename you wanted to give to the downloaded file>'
    df.to_csv(response, index=False)
    return response
    

    参考:

    1. https://gist.github.com/jonperron/733c3ead188f72f0a8a6f39e3d89295d
    2. https://docs.djangoproject.com/en/2.1/howto/outputting-csv/

    【讨论】:

    • 嗨...有没有办法为这个响应添加上下文?例如,除了下载之外,我还想将 {'number_of_records' : 10} 返回到 html 页面
    【解决方案6】:

    您混合了两个应该分开的要求:

    1. 使用 python 或 pandas 创建一个 .xlsx 文件——看起来你在这方面做得很好。

    2. 提供可下载文件 (django);见this postmaybe this one

    【讨论】:

    • 感谢您的帖子。我已经找到了第一个,但它似乎对我不起作用。第二个使概念更清晰,但仍然没有用。如果我尝试将 xlsx 创建和 http 响应结合起来,唯一发生的事情是在我的项目的根目录中创建了一个新的 xlsx。我没有收到任何下载提示。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-19
    • 1970-01-01
    • 1970-01-01
    • 2022-06-25
    • 1970-01-01
    相关资源
    最近更新 更多