使用 groupby 和 itemgetter() 将 Python 3.x 代码转换为 jinja2 模板

Converting Python 3.x code into jinja2 template using groupby and itemgetter()

我已经对名为 sorted_cities=[亚特兰大、柏林、伯尔尼、卡尔加里] 的城市列表进行了排序,我正在按 alpha 顺序排序并希望在网络上显示并能够点击城市获取详细信息以及 select 城市以通过电子邮件接收通知。我希望网络上的输出如下所示:

A

 Atlanta

B  
 Berlin

 Bern

C

 Calgary

我在 Python 3.x 中有以下代码可以正常工作:

sorted_names=sorted(names,key=str.lower)

    print(sorted_names)

   for letter, words in groupby(sorted_names,key=itemgetter(0)):

       print(letter)
       for word in words:

           print(word)

我在 Jinja2 中创建了这段代码,但它不起作用:

{% block content %}
<div>
    <form action="cities" method="post">
    {% for group in sorted_names|groupby("letter") %}
        <li><b>{{ group.grouper }} </b></li>
        {% for word in group.list %}
            <input type="checkbox" name="city" value="{{ word }}"> <a href="/{{ word }}">{{ word }}</a><br>
        {% endfor %}
        {% endfor %}

    <input type="submit" value="Submit">
    </form>
</div>
{% endblock %}

这是一个快速测试,可获得您正在寻找的基本功能。我放弃了 @1844144 的评论。但基本上只是将 groupby 元素附加到 python 端的数组并将其传递给 jinja。然后在 jinja 我用 jinja 过滤器做了一个不太聪明的条件事情 (Length of string in Jinja/Flask)

app.py:

from flask import Flask, render_template
from itertools import groupby
from operator import itemgetter

app = Flask(__name__)

@app.route('/')
def main():
    names = ['Atlanta','Calgary','Berlin','Austin']
    sorted_names = sorted(names, key=str.lower)
    grouped = []
    for letter, words in groupby(sorted_names,key=itemgetter(0)):
        grouped.append(letter)
        for word in words:
            grouped.append(word)
    return render_template('index.html', grouped=grouped)

if __name__ == '__main__':
    app.run(debug=True)

templates/index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
{% for name in grouped %}
    {% if name|length == 1 %}
        {{name}}<br>
    {% else %}
        <a href="/"> {{name}} </a><br>
    {%endif%}
{% endfor %}
</body>
</html>

浏览器中的效果如下:

jinja2 中的默认过滤器 groupby 需要该属性来对您的项目进行分组。但是,您提供了 'letter' 属性,但 jinja2 无法理解它,因为您的 names 列表没有这样的属性。

groupby(value, attribute)

Group a sequence of objects by a common attribute.


要重用您的 python 代码,您可以通过 [template_filter] 向 jinja2 注册一个过滤器(http://flask.pocoo.org/docs/dev/api/#flask.Flt 现在可以使用点 notatask.template_filter)。所以,你可以这样试试:

from itertools import groupby
from operator import itemgetter

from flask import Flask, render_template


app = Flask(__name__)


@app.template_filter()
def groupby_letter(iterable):
    return groupby(sorted(iterable), key=itemgetter(0))


@app.route('/')
def index():
    names = ['Atlanta','Calgary','Berlin','Austin']
    return render_template('index.html', names=names)


if __name__ == '__main__':
    app.run(debug=True)

并像这样在模板中使用过滤器:

{% for key, group in names|groupby_letter %}
    <li><b>{{ key }}</b></li>
    {% for word in group %}
        <p>{{ word }}</p>
    {% endfor %}
{% endfor %}

另一种方法是,如果您仍想使用 groupby 过滤器,则需要对 names 做一些事情。我们需要构建一个列表字典,每个字典包含一个 'first_letter' 键,我们将在使用 groupby 过滤器时将其用作组属性。

from flask import Flask, render_template


app = Flask(__name__)


@app.route('/')
def index():
    names = ['Atlanta','Calgary','Berlin','Austin']
    name_dicts = [{'first_letter': name[0], 'name': name} for name in names]
    return render_template('index.html', name_dicts=name_dicts)


if __name__ == '__main__':
    app.run(debug=True)

并在您的模板中使用 groupby 过滤器,如下所示:

{% for group in name_dicts|groupby('first_letter') %}
    <li><b>{{ group.grouper }}</b></li>
    {% for item in group.list %}
        <p>{{ item.name }}</p>
    {% endfor %}
{% endfor %}