【发布时间】:2018-12-21 10:20:13
【问题描述】:
我正在为我们的学生酒吧创建一个网络应用程序,以使用 Flask 和 MySQL 数据库来管理交易、库存、客户等。然而,在 Bootstrap 卡片中列出了多达 12 个客户的索引页面(带有支付按钮、充值帐户等),使用 render_template() 渲染需要 4 秒。
此应用部署在具有 2 个 vCPU 的 DigitalOcean droplet 上,并使用典型的 Nginx/Gunicorn duo 在给定时间处理大约 50-100 个并发客户端。我尝试在本地计算机上运行它并得到相同的结果:render_template() 需要很长时间才能呈现页面。
这是模板中需要 4 秒才能渲染的部分:
<div class="container">
<div class="row">
{% for user in users.items %}
<div class="col-lg-3 col-md-4 col-sm-6 col-6">
<div class="card mb-4 shadow {% if not user.deposit %}text-secondary border-secondary{% elif user.balance <= 0 %}text-danger border-danger{% elif user.balance <= 5 %}text-warning border-warning{% else %}text-primary border-primary{% endif %}">
<a href="{{ url_for('main.user', username=user.username) }}">
<img class="card-img-top img-fluid" src="{{ user.avatar() }}" alt="{{ user.username }}">
</a>
<div class="card-body">
<h5 class="card-title text-nowrap user-card-title">
{% if user.nickname %}
"{{ user.nickname }}"<br>{{ user.last_name|upper }}
{% else %}
{{ user.first_name }}<br>{{ user.last_name|upper }}
{% endif %}
</h5>
</div>
<div class="card-footer">
{% if user.deposit %}
<div class="btn-toolbar justify-content-between" role="toolbar" aria-label="Pay and quick access item">
<div class="btn-group" role="group" aria-label="Pay">
{% include '_pay.html.j2' %}
</div>
<div class="btn-group" role="group" aria-label="Quick access item">
{% include '_quick_access_item.html.j2' %}
</div>
</div>
{% else %}
<div class="btn-toolbar justify-content-between" role="toolbar" aria-label="Deposit">
<div class="btn-group" role="group" aria-label="Deposit">
{% include '_deposit.html.j2' %}
</div>
</div>
{% endif %}
</div>
</div>
</div>
{% endfor %}
</div>
</div>
“快速访问项目”、“充值”和“存款”是简单的下拉按钮,“支付”是一个可滚动的下拉菜单,最多可以列出 50 种产品。这个索引页面是分页的,用户是一个 Flask 分页对象,每页最多 12 个用户,总共 100 个用户。
这是索引页面的路由函数:
@bp.route('/', methods=['GET'])
@bp.route('/index', methods=['GET'])
@login_required
def index():
""" View index page. For bartenders, it's the customers page and for clients,
it redirects to the profile. """
if not current_user.is_bartender:
return redirect(url_for('main.user', username=current_user.username))
# Get arguments
page = request.args.get('page', 1, type=int)
sort = request.args.get('sort', 'asc', type=str)
grad_class = request.args.get('grad_class', str(current_app.config['CURRENT_GRAD_CLASS']), type=int)
# Get graduating classes
grad_classes_query = db.session.query(User.grad_class.distinct().label('grad_class'))
grad_classes = [row.grad_class for row in grad_classes_query.all()]
# Get inventory
inventory = Item.query.order_by(Item.name.asc()).all()
# Get favorite items
favorite_inventory = Item.query.filter_by(is_favorite=True).order_by(Item.name.asc()).all()
# Get quick access item
quick_access_item = Item.query.filter_by(id=current_app.config['QUICK_ACCESS_ITEM_ID']).first()
# Sort users alphabetically
if sort == 'asc':
users = User.query.filter_by(grad_class=grad_class).order_by(User.last_name.asc()).paginate(page,
current_app.config['USERS_PER_PAGE'], True)
else:
users = User.query.filter_by(grad_class=grad_class).order_by(User.last_name.desc()).paginate(page,
current_app.config['USERS_PER_PAGE'], True)
return render_template('index.html.j2', title='Checkout',
users=users, sort=sort, inventory=inventory,
favorite_inventory=favorite_inventory,
quick_access_item=quick_access_item,
grad_class=grad_class, grad_classes=grad_classes)
我对@987654323@ 中的步骤进行了计时:render_template() 耗时约 4 秒,其余的视图函数耗时约 15 毫秒。
我在这里做错了什么?我的模板太复杂了吗?作为一个网络爱好者,我不知道 jinja2 模板可以承受多少滥用。我考虑过将模板预渲染到静态文件中,但是如果 Nginx 提供静态文件,我如何确保安全(只有调酒师帐户可以访问此页面)?
编辑:这是包含在索引模板中的“支付”下拉模板。他是渲染时间过长的罪魁祸首。
<div class="btn-group pay-btn" role="group">
<button class="user-card-btn btn {% if user.balance <= 0 %}btn-danger{% elif user.balance <= 5 %}btn-warning{% else %}btn-primary{% endif %} dropdown-toggle{% if user.balance <= 0 or not user.deposit %} disabled{% endif %}" type="button" id="dropdownPay" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="icon" data-feather="shopping-cart"></span><span class="text">Pay</span>
</button>
<div class="dropdown-menu scrollable-menu" aria-labelledby="dropdownPay">
{% if favorite_inventory|length > 1 %}
<h6 class="dropdown-header">Favorites</h6>
{% for item in favorite_inventory %}
<a class="dropdown-item{% if (not user.deposit) or (user.can_buy(item) != True) or (item.is_quantifiable and item.quantity <= 0) %} disabled{% endif %}" href="{{ url_for('main.pay', username=user.username, item_name=item.name) }}">
{% if (not user.deposit) or (user.can_buy(item) != True) or (item.is_quantifiable and item.quantity <= 0) %}
<strike>
{% endif %}
{{ item.name }} ({{ item.price }}€)
{% if (not user.deposit) or (user.can_buy(item) != True) or (item.is_quantifiable and item.quantity <= 0) %}
</strike>
{% endif %}
</a>
{% endfor %}
<div class="dropdown-divider"></div>
{% endif %}
<h6 class="dropdown-header">Products</h6>
{% for item in inventory %}
{% if item not in favorite_inventory %}
<a class="dropdown-item{% if (not user.deposit) or (user.can_buy(item) != True) or (item.is_quantifiable and item.quantity <= 0) %} disabled{% endif %}" href="{{ url_for('main.pay', username=user.username, item_name=item.name) }}">
{% if (not user.deposit) or (user.can_buy(item) != True) or (item.is_quantifiable and item.quantity <= 0) %}
<strike>
{% endif %}
{{ item.name }} ({{ item.price }}€)
{% if (not user.deposit) or (user.can_buy(item) != True) or (item.is_quantifiable and item.quantity <= 0) %}
</strike>
{% endif %}
</a>
{% endif %}
{% endfor %}
</div>
</div>
【问题讨论】:
-
您确定时间消耗在渲染而不是python的视图函数中?
index()中的步骤你计时了吗? -
@roganjosh:确实,我忘了提到我对
index()中的步骤进行了计时:render_template()函数执行大约需要 4 秒,而视图函数的其余部分需要 15 毫秒,我会在我的问题中添加它! -
我可以看到一些可以从 jinja 中提取出来的逻辑,例如
{% if user.nickname %}检查但模板中的任何内容对我来说都是 4 秒值得。我实际上不知道如何分析 jinja2 渲染时间,我想知道是否有一种方法可以获得类似于 Flask 调试分析器的逐行细分(据我所知,它只详细查看 Python 代码) . -
我的猜测是
{% include '_quick_access_item.html.j2' %}等人正在消耗您的时间。以及其中的一些东西。我在此模板中看不到任何内容。 -
@roganjosh:你是对的!由于它们只是按钮,我什至没有想到它们是问题所在。 “支付”下拉菜单及其所有项目是罪魁祸首。现在我需要寻找一种方法来先渲染它,然后将它包含在 index.html 中。有没有比这个更好的方法来列出产品(该列表在某种程度上取决于用户,因为有些是每个用户禁用的),还是我应该使用 Javascript 来做到这一点?
标签: python html templates flask jinja2