Проект - площадка для объявлений. В ленте должны отображаются объявления с каруселью фотографий, но карусель корректно срабатывает только 1 раз
Первое объявление выводит фотографии корректно, дальше фотографии не выводятся вообще
views.py Тут список photos наполняться корректно, там есть фотографии всех объявлений
def index(request):
media = Media.objects.all()
photos = []
ads, cats, _ = get_data_from_db()
for ad in ads:
for photo in media:
if photo.ad.id == ad.id:
photos.append(photo)
page_obj = get_pagination(request, ads)
context = {
'title': 'All ads:',
'cats': cats,
'page_obj': page_obj,
'photos': photos,
}
return render(request, 'main/index.html', context=context)
часть index.html отвечающая за вывод обьявления (в base.html подключен js для работы карусели)
<div class="album py-5 bg-light">
<div class="container">
<div class="row row-cols-1 row-cols-sm-2 row-cols-md-3 g-3">
{% if page_obj %}
{% for el in page_obj %}
<div class="col">
<a href="{{ el.get_absolute_url }}" class="text-dark text-decoration-none">
<div class="card shadow-sm">
<div class="row justify-content-center py-2">
<div>
<div id="post_{{ el.id }}" class="carousel slide" data-bs-ride="carousel" data-bs-interval="false">
<div class="carousel-indicators">
{% for media in photos %}
{% if media.ad.id == el.id %}
<button type="button" data-bs-target="#post_{{ el.id }}" data-bs-slide-to="{{ forloop.counter0 }}"
{% if forloop.first %} class="active" aria-current="true"{% endif %} aria-label="Slide {{ forloop.counter }}">
</button>
{% endif %}
{% endfor %}
</div>
<div class="carousel-inner">
{% for media in photos %}
{% if media.ad.id == el.id %}
<div class="carousel-item {% if forloop.first %}active{% endif %}" align="center">
<img src="http://127.0.0.1:8080{{ media.media.url }} " height="325" width="400" border="8" alt="{{el.title}}">
</div>
{% endif %}
{% endfor %}
</div>
<button class="carousel-control-prev" type="button" data-bs-target="#post_{{ el.id }}" data-bs-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="visually-hidden">Previous</span>
</button>
<button class="carousel-control-next" type="button" data-bs-target="#post_{{ el.id }}" data-bs-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="visually-hidden">Next</span>
</button>
</div>
</div>
</div>
<div class="card-body">
<h3>{{ el.title }}</h3>
<h5>{{ el.price }} $</h5>
<p>{{ el.ad|linebreaks|truncatewords:4 }}</p>
<div class="d-flex justify-content-between align-items-center">
<small class="text-muted">Date: {{ el.time_create|date:"d-m-Y H:i" }}</small>
{% for cat in cats %}
{% if cat.id == el.cat.id %}
<small class="text-muted"><a href="{{ cat.get_absolute_url }}" class="text-dark">Category: {{ el.cat }}</a></small>
{% endif %}
{% endfor %}
</div>
</div>
</div>
</a>
</div>
{% endfor %}
{% else %}
<p>There is no ads!</p>
{% endif %}
</div>
</div>
</div>
{% if page_obj.has_other_pages %}
<span class="step-links">
<ul class="nav justify-content-center pb-3 mb-3">
{% if page_obj.has_previous %}
<a class="page-link text-muted" href="?page={{ page_obj.previous_page_number }}"><</a>
{% endif %}
{% for p in page_obj.paginator.page_range %}
{% if page_obj.number == p %}
<li class="page-item active">
<a class="page-link" href="?page={{ p }}">{{ p }}</a>
</li>
{% elif p >= page_obj.number|add:-2 and p <= page_obj.number|add:2 %}
<li class="page-item">
<a class="page-link text-muted" href="?page={{ p }}">{{ p }}</a>
</li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<a class="page-link text-muted" href="?page={{ page_obj.next_page_number }}">></a>
{% endif %}
</ul>
</span>
{% endif %}
Это вариант исправления в темплейте:
В диве carousel-indicators вы запускаете два цикла по одним и тем же данным, но они не обязательно синхронны между собой, измените это формирование цикла, и проверьте рендер темплэйта.
Например:
<div id="post_{{ el.id }}" class="carousel slide" data-bs-ride="carousel" data-bs-interval="false">
{% for media in photos %}
{% if media.ad.id == el.id %}
<div class="carousel-indicators">
<button type="button" data-bs-target="#post_{{ el.id }}" data-bs-slide-to="{{ forloop.counter0 }}"
{% if forloop.first %} class="active" aria-current="true"{% endif %} aria-label="Slide {{ forloop.counter }}">
</button>
</div>
<div class="carousel-inner">
<div class="carousel-item {% if forloop.first %}active{% endif %}" align="center">
<img src="http://127.0.0.1:8080{{ media.media.url }} " height="325" width="400" border="8" alt="{{el.title}}">
</div>
</div>
{% endif %}
{% endfor %}
<button class="carousel-control-prev" type="button" data-bs-target="#post_{{ el.id }}" data-bs-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="visually-hidden">Previous</span>
</button>
<button class="carousel-control-next" type="button" data-bs-target="#post_{{ el.id }}" data-bs-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="visually-hidden">Next</span>
</button>
</div>
Но лучше тщательнее готовить queryset, что бы как можно меньше делать проверок в темплэйте.
Пример views.py:
from dataclasses import dataclass
# Здесь делаю подготовку данных используя - dataclass
# это позволит мне ускорить рендер страницы и меньше данных
# перебирать в темплэйте
@dataclass
class AdCtx:
id: int
photos: list
def index(request):
media = Media.objects.all()
# тут соберу объекты для контекста
ads_ctx = list()
ads, cats, _ = get_data_from_db()
for ad in ads:
ad_photos = []
for photo in media:
if photo.ad.id == ad.id:
# каждый набор фото принадлежит только одному ad.id
ad_photos.append(photo)
# тут наполняю датакласс
ad_ctx = AdCtx(ad.id, ad_photos)
# тут дсе датаклассы собираю в list
ads_ctx.append(ad_ctx)
# В пагинатор отправляю данные собранные на датаклассе
page_obj = get_pagination(request, ads_ctx)
# В контекст идет комплект данных: список ads разделенный
# на объект ad.id и набор его photo
# Это позволяет в темплэйте не делать сравнений и проверок
context = {
'title': 'All ads:',
'cats': cats,
'page_obj': page_obj,
}
return render(request, 'main/index.html', context=context)
Пример templates:
<div class="album py-5 bg-light">
<div class="container">
<div class="row row-cols-1 row-cols-sm-2 row-cols-md-3 g-3">
{% if page_obj|length > 0 %}
{% for el in page_obj %}
<div class="col">
<a href="{{ el.get_absolute_url }}" class="text-dark text-decoration-none">
<div class="card shadow-sm">
<div class="row justify-content-center py-2">
<div>
<div id="post_{{ el.id }}" class="carousel slide" data-bs-ride="carousel" data-bs-interval="false">
{% for media in el.photos %}
<div class="carousel-indicators">
<button type="button" data-bs-target="#post_{{ el.id }}" data-bs-slide-to="{{ forloop.counter0 }}"
{% if forloop.first %} class="active" aria-current="true"{% endif %} aria-label="Slide {{ forloop.counter }}">
</button>
</div>
<div class="carousel-inner">
<div class="carousel-item {% if forloop.first %}active{% endif %}" align="center">
<img src="http://127.0.0.1:8080{{ media.media.url }} " height="325" width="400" border="8" alt="{{el.title}}">
</div>
</div>
{% endfor %}
<button class="carousel-control-prev" type="button" data-bs-target="#post_{{ el.id }}" data-bs-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="visually-hidden">Previous</span>
</button>
<button class="carousel-control-next" type="button" data-bs-target="#post_{{ el.id }}" data-bs-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="visually-hidden">Next</span>
</button>
</div>
</div>
</div>
<div class="card-body">
<h3>{{ el.title }}</h3>
<h5>{{ el.price }} $</h5>
<p>{{ el.ad|linebreaks|truncatewords:4 }}</p>
<div class="d-flex justify-content-between align-items-center">
<small class="text-muted">Date: {{ el.time_create|date:"d-m-Y H:i" }}</small>
{% for cat in cats %}
{% if cat.id == el.cat.id %}
<small class="text-muted"><a href="{{ cat.get_absolute_url }}" class="text-dark">Category: {{ el.cat }}</a></small>
{% endif %}
{% endfor %}
</div>
</div>
</div>
</a>
</div>
{% endfor %}
{% else %}
<p>There is no ads!</p>
{% endif %}
</div>
</div>
</div>
Конечно этот код надо проверять на живых данных, я не проверял все выводы данных в темплэйте :)