Bootstrap аккордеон с Django: Как загрузить данные только для секции открытого аккордеона?
Я пытаюсь сделать веб-страницу, которая будет отображать рецепты в формате загрузочного аккордеона, вот так ( см. Здесь). Вот как я делаю это сейчас:
<div class="panel-group" id="accordion">
{% for recipe in recipes %}
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">
<a data-toggle="collapse" data-parent="#accordion" href="#collapse{{ forloop.counter }}">
{{ recipe }}
</a>
</h4>
</div>
<div id="collapse{{ forloop.counter }}" class="panel-collapse collapse">
<div class="panel-body">
<table class="table table-hover">
{% for ingredient in foodtype|ingredients_in_recipe:recipe %}
<tr>
<td>
{{ ingredient.ingredient_name }}
</td>
<td>
{{ ingredient.ingredient_quantity }}
</td>
</tr>
{% endfor %}
<p>{{ recipe.details }}</p>
</table>
</div>
</div>
</div>
{% endfor %}
</div>
Я сделал собственный шаблонный тег для этого примерно так:
@register.filter
def ingredients_in_recipe(foodtype, recipe):
return foodtype.ingredient_set.filter(recipe=recipe).order_by("ingredient_name")
Проблема в том, что у меня более 200 рецептов, и загрузка всех этих данных происходит слишком медленно. В идеале функция тега шаблона ингредиенты_ин_рецепта должна вызываться только тогда, когда пользователь нажимает на рецепт. Однако, насколько я понимаю, это невозможно, потому что Django запускает все это, а затем отправляет визуализированный HTML-код пользователю.
Могу ли я как-нибудь обойти эту проблему, сохранив стиль аккордеона, как на картинке?
Заранее спасибо Макс
РЕДАКТИРОВАТЬ: Вот мой взгляд, а также
def detail(request, foodtype_id):
foodtype = get_object_or_404(foodtype, id=foodtype_id)
recipe = foodtype.recipe_set.values_list('recipe').order_by('recipe').distinct()
context = {
'foodtype': foodtype,
'recipe': recipe,
}
return render(request, 'main/detail.html', context)
2 ответа
Всегда лучше делать эту логику до того, как она попадет в шаблон. Что, если вы установите порядок на ингредиенты, чтобы вам не пришлось заказывать их в шаблоне? Это работает и улучшает производительность?
class Ingredient(models.Model):
...
class Meta:
ordering = ['ingredient_name']
<div class="panel-group" id="accordion">
{% for recipe in recipes %}
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">
<a data-toggle="collapse" data-parent="#accordion" href="#collapse{{ forloop.counter }}">
{{ recipe }}
</a>
</h4>
</div>
<div id="collapse{{ forloop.counter }}" class="panel-collapse collapse">
<div class="panel-body">
<table class="table table-hover">
{% for ingredient in recipe.ingredient_set.all %}
<tr>
<td>
{{ ingredient.ingredient_name }}
</td>
<td>
{{ ingredient.ingredient_quantity }}
</td>
</tr>
{% endfor %}
<p>{{ recipe.details }}</p>
</table>
</div>
</div>
</div>
{% endfor %}
</div>
Попробуйте этот вариант с помощью Alpine.js, надеюсь, кому-то он будет полезен. Сначала в ваш «_base.html» вы должны вставить строку скрипта Alpine.js.
<section>
<div class="panel-group" id="accordion" x-data="{openedIndex: null}">
{% for recipe in recipes %}
<div class="card-header" style="cursor: pointer;"
@click="openedIndex = {{ forloop.counter }}">
<h5 class="mb-0">{{ recipe }}</h5>
</div>
<div class="card-body" x-show="openedIndex === {{ forloop.counter }}">
{{ recipe.details }}
</div>
{% endfor %}
</div>
</section>