Django/Jinja2:通过模板语法生成CSV数据

0 投票
2 回答
2206 浏览
提问于 2025-04-17 16:41

我想用一种类似模板的语法来生成CSV文件(或者其他合适的导出格式)所需的数据,这样我就可以访问我的Django对象的字段。例如,输出字段的规范可以是:

employee.id
employee.manager.first_name
employee.user.last_login
employee.contact.phone_number

在这里,字段的指定方式和我在Django或Jinja2模板中使用的语法是一样的。

我还希望能够像在Django/Jinja模板中那样使用过滤器:

employee.user.last_login|date:"c"

给定这个规范和一个对象列表,我想从我的对象中生成数据行。然后我可以根据这些数据生成CSV、XML等格式。

我开始写一些代码来实现这个功能,但很快就变得复杂了。我在想,可能可以利用Django/Jinja2模板引擎中现有的一些功能。

有没有什么方法可以让我通过Django/Jinja2语法来访问我的对象呢?

2 个回答

0

Django文档是一个很棒的资源 - http://docs.python.org/2/library/csv.html

你只需要了解在Python中用点号来访问对象属性的写法。

1

首先,使用除了 csv 模块以外的任何方法来生成 CSV 数据,可能不会让你交到很多朋友;因为这个模块才是最合适的工具。

用这个模块生成输出的最 方便 的方法是使用 writer.writerows(),它需要一个嵌套列表(或者说是一个序列的迭代器)。生成器是表达这个的好方法:

def csv_spec(queryset):
    for employee in queryset:
        yield (
            employee.id,
            employee.manager.first_name,
            employee.user.last_login,
            employee.contact.phone_number)

w = csv.writer(outfile)
w.writerows(csv_spec(something))

补充一下:你是说你想要一个类似模板的东西,原因和我们使用模板一样,但它生成的是 CSV 而不是文本。你可以让 jinja 解析和处理它在占位符中的表达式,而不需要完整的模板。

假设我们有一些数据:

>>> from collections import namedtuple
>>> from datetime import datetime
>>> Employee = namedtuple('Employee', 'id manager user contact')
>>> Person = namedtuple('Person', 'first_name last_name')
>>> Account = namedtuple('Account', 'username password last_login')
>>> Contact = namedtuple('Contact', 'phone_number email')
>>> data = [
...     Employee(0, Person('Alice', 'Brown'),
...              Account('abrown', 'secret', datetime(2013, 3, 3)),
...              Contact('5551234', 'abrown@example.com')),
...     Employee(1, Person('Bob', 'Jones'),
...              Account('bjones', 'safe', datetime(2013, 3, 3)),
...              Contact('5555678', 'bjones@example.com')),
...     Employee(2, Person('Carol', 'Smith'),
...              Account('csmith', 'hidden', datetime(2013, 3, 3)),
...              Contact('5559012', 'csmith@example.com'))]

还有一个模板(你也可以很容易地从文件中读取这个模板)

>>> import StringIO
>>> template = StringIO.StringIO('''employee.id
... employee.manager.first_name
... employee.user.last_login
... employee.contact.phone_number''')

要使用表达式编译器,你需要一个环境;不需要指定任何选项:

>>> import jinja2
>>> env = jinja2.Environment()
>>> template_filter = map(env.compile_expression, template)

正如我提到的,使用 csv 模块:

>>> import csv
>>> import sys
>>> writer = csv.writer(sys.stdout)

最后,利用编译好的表达式,我们可以逐行处理数据,对每一行应用每个表达式

>>> writer.writerows([[field(employee=row) for field in template_filter]
...                   for row in data])
0,Alice,2013-03-03 00:00:00,5551234
1,Bob,2013-03-03 00:00:00,5555678
2,Carol,2013-03-03 00:00:00,5559012

撰写回答