Я создаю веб-приложение для нашего студенческого паба для управления транзакциями, инвентаризацией, клиентами и т. Д. С помощью Flask и базы данных MySQL.Однако страница индекса, на которой перечисляются до 12 клиентов на картах Bootstrap (с кнопками для оплаты, пополнения счета и т. Д.), Отрисовывает 4 секунды с помощью render_template ().
Это приложение развернуто на дроплете DigitalOcean с 2 виртуальными ЦП и обрабатывает около 50-100 одновременных клиентов в определенный момент времени, используя типичный дуэт Nginx / Gunicorn.Я пытался запустить его на своем локальном компьютере, и у меня были те же результаты: 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)
Iпо времени шаги в index()
: 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>