通常当我们谈论开发Web站点时,我们正在讨论生产HTML。 当然,Web上的HTML比HTML要多得多; 我们使用Web以各种格式分发数据:RSS,
PDF,图像等等。
到目前为止,我们一直关注HTML制作的常见案例,但在本章中,我们将绕道看看如何使用Django制作其他类型的内容。 Django具有方便的内置工具,您可以使用这些工具生成一些常见的非HTML内容:
- 逗号分隔(CSV)文件,用于导入电子表格应用程序。
- PDF文件。
- RSS / Atom联合供稿。
- Sitemaps(最初由Google开发的XML格式,它为搜索引擎提供提示)。
稍后我们将研究这些工具,但首先我们将介绍基本原则。
基础知识:视图和MIME类型
回想第2章,一个视图函数只是一个Python函数,它接受一个Web请求并返回一个Web响应。 此响应可以是网页,重定向,404错误,XML文档或图像的HTML内容,或者任何其他内容。 更正式的说,一个Django视图函数必须:
- 接受一个HttpRequest实例作为它的第一个参数;
- 返回一个HttpResponse实例。
从视图中返回非HTML内容的关键在于HttpResponse类,特别是content_type参数。 默认情况下,Django将content_type设置为“text / html”。 但是,您可以将content_type设置为由IANA管理的任何官方Internet媒体类型(MIME类型)。
通过调整MIME类型,我们可以向浏览器表明我们已经返回了不同格式的响应。 例如,让我们看看一个返回一个PNG图像的视图。 为了简单起见,
我们只是从磁盘读取文件:
from django.http import HttpResponse
def my_image(request):
image_data = open("/path/to/my/image.png", "rb").read()
return HttpResponse(image_data, content_type="image/png")
而已! 如果将open()调用中的图像路径替换为实际图像的路径,则可以使用此非常简单的视图来提供图像,并且浏览器将正确显示它。
需要牢记的另一件重要事情是HttpResponse对象实现了Python的标准文件类对象API。 这意味着您可以在任何地方使用HttpResponse实例Python(或第三方库)需要一个文件。 有关如何工作的示例,我们来看看如何使用Django生成CSV。
生成CSV
Python附带一个CSV库csv。 在Django中使用它的关键在于csv模块的CSV创建功能作用于类文件对象,而Django的HttpResponse对象则是文件类对象。 这是一个例子:
import csv
from django.http import HttpResponse
def some_view(request):
# Create the HttpResponse object with the appropriate CSV header.
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment;
filename="somefilename.csv"'
writer = csv.writer(response)
writer.writerow(['First row', 'Foo', 'Bar', 'Baz'])
writer.writerow(['Second row', 'A', 'B', 'C', '"Testing"'])
return response
代码和评论应该是不言自明的,但有几件事情值得一提:
-
响应获取特殊的MIME类型text / csv。 这告诉浏览器文档是一个CSV文件,而不是HTML文件。 如果你不这样做,浏览器可能会将输出解释为HTML,这会在浏览器窗口中产生丑陋,可怕的gobbledygook。
-
该响应会获得一个额外的Content-Disposition标头,其中包含CSV文件的名称。 这个文件名是任意的; 随心所欲地称呼它。 它将被浏览器在另存为中使用
对话等 -
连接到CSV生成API非常简单:只需将响应作为第一个参数传递给csv.writer即可。 csv.writer函数需要一个类似文件的对象,并且HttpResponse对象适合该账单。
-
对于CSV文件中的每一行,调用writer.writerow,将它传递给一个可迭代对象,如列表或元组。
-
CSV模块负责为您提供引用,因此您不必担心引号或逗号在字符串中转义。 只要通过writerow()你的原始字符串,它会做正确的事情。
流式传输大型CSV文件
处理生成非常大的响应的视图时,您可能需要考虑使用Django的StreamingHttpResponse。 例如,通过流式传输需要很长时间才能生成的文件,可以避免负载均衡器丢弃可能在服务器生成响应时超时的连接。 在这个例子中,我们充分利用Python生成器来有效地处理大型CSV文件的汇编和传输:
import csv
from django.utils.six.moves import range
from django.http import StreamingHttpResponse
class Echo(object):
"""An object that implements just the write method of the file-like
interface.
"""
def write(self, value):
"""Write the value by returning it, instead of storing in a buffer."""
return value
def some_streaming_csv_view(request):
"""A view that streams a large CSV file."""
# Generate a sequence of rows. The range is based on the maximum number of
# rows that can be handled by a single sheet in most spreadsheet
# applications.
rows = (["Row {}".format(idx), str(idx)] for idx in range(65536))
pseudo_buffer = Echo()
writer = csv.writer(pseudo_buffer)
response = StreamingHttpResponse((writer.writerow(row)
for row in rows), content_type="text/csv")
response['Content-Disposition'] = 'attachment;
filename="somefilename.csv"'
return response
使用模板系统
或者,您可以使用Django模板系统来生成CSV。 这比使用方便的Python csv模块要低一些,但是为了完整性,这里给出了解决方案。 这里的想法是将项目列表传递给您的模板,并使模板在for循环中输出逗号。 这里有一个例子,它生成与上面相同的CSV文件:
from django.http import HttpResponse
from django.template import loader, Context
def some_view(request):
# Create the HttpResponse object with the appropriate CSV header.
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment;
filename="somefilename.csv"'
# The data is hard-coded here, but you could load it
# from a database or some other source.
csv_data = (
('First row', 'Foo', 'Bar', 'Baz'),
('Second row', 'A', 'B', 'C', '"Testing"', "Here's a quote"),
)
t = loader.get_template('my_template_name.txt')
c = Context({'data': csv_data,})
response.write(t.render(c))
return response The only difference between this example and the previous example\
is that this one uses template loading instead of the CSV module. The rest of the co\
de - such as the `content_type='text/csv'` - is the same. Then, create the template `\
my_template_name.txt`, with this template code:
{% for row in data %}
"{{ row.0|addslashes }}",
"{{ row.1|addslashes }}",
"{{ row.2|addslashes }}",
"{{ row.3|addslashes }}",
"{{ row.4|addslashes }}"
{% endfor %}
这个模板很基础。 它只是遍历给定的数据,并为每一行显示一行CSV。 它使用addslashes模板过滤器来确保引号没有任何问题。
其他基于文本的格式
请注意,这里没有特定的CSV,只是特定的输出格式。 您可以使用这些技术中的任何一种来输出您可以梦寐以求的任何基于文本的格式。 您也可以使用类似的技术来生成任意的二进制数据; 例如,生成PDF。
网友评论