feat: add pagination and color filter to catalog view and templates
All checks were successful
Build and Push Docker Image / build (push) Successful in 24s
All checks were successful
Build and Push Docker Image / build (push) Successful in 24s
- Implemented pagination for catalog items with Django's Paginator - Updated `item-list.html` to include pagination controls - Added color filtering logic to catalog view with support for `Color` model - Enhanced `side-bar.html` with a dynamic color dropdown filter - Adjusted filtering URL logic to accommodate color filters and pagination parameters - Prefetched `colors` in item queries to optimize performance
This commit is contained in:
@@ -2,11 +2,12 @@ from dataclasses import dataclass
|
||||
from decimal import Decimal, InvalidOperation
|
||||
from urllib.parse import urlencode
|
||||
|
||||
from django.core.paginator import Paginator
|
||||
from django.http import HttpRequest, HttpResponse, Http404
|
||||
from django.shortcuts import redirect, render
|
||||
|
||||
from favorites.services import get_user_favorite_item_ids
|
||||
from items.models import Category, Item, Subcategory
|
||||
from items.models import Category, Color, Item, Subcategory
|
||||
from orders.services import get_user_cart_item_ids
|
||||
|
||||
|
||||
@@ -45,6 +46,7 @@ def _build_catalog_url(
|
||||
max_price: str = "",
|
||||
sale: bool = False,
|
||||
new: bool = False,
|
||||
color: str = "",
|
||||
) -> str:
|
||||
base_url = "/catalog/"
|
||||
if category_slug:
|
||||
@@ -63,6 +65,8 @@ def _build_catalog_url(
|
||||
query_params["sale"] = "1"
|
||||
if new:
|
||||
query_params["new"] = "1"
|
||||
if color:
|
||||
query_params["color"] = color
|
||||
|
||||
if not query_params:
|
||||
return base_url
|
||||
@@ -100,6 +104,7 @@ def index(request: HttpRequest, category: str | None = None) -> HttpResponse:
|
||||
selected_scope = request.GET.get('category', '').strip()
|
||||
filter_sale = request.GET.get('sale') == '1'
|
||||
filter_new = request.GET.get('new') == '1'
|
||||
selected_color_id_raw = request.GET.get('color', '').strip()
|
||||
|
||||
if category is None and selected_scope:
|
||||
category_slug, selected_subcategory_slug = _resolve_catalog_scope(selected_scope)
|
||||
@@ -113,12 +118,13 @@ def index(request: HttpRequest, category: str | None = None) -> HttpResponse:
|
||||
max_price=max_price_raw,
|
||||
sale=filter_sale,
|
||||
new=filter_new,
|
||||
color=selected_color_id_raw,
|
||||
)
|
||||
)
|
||||
|
||||
items = (
|
||||
Item.objects.select_related('category', 'subcategory')
|
||||
.prefetch_related('images')
|
||||
.prefetch_related('images', 'colors')
|
||||
.order_by('category__name', 'name')
|
||||
)
|
||||
|
||||
@@ -159,6 +165,16 @@ def index(request: HttpRequest, category: str | None = None) -> HttpResponse:
|
||||
if filter_new:
|
||||
items = [item for item in items if item.is_new]
|
||||
|
||||
color_item_ids = {c.id for item in items for c in item.colors.all()}
|
||||
available_colors = list(Color.objects.filter(id__in=color_item_ids))
|
||||
|
||||
if selected_color_id_raw:
|
||||
try:
|
||||
selected_color_id_int = int(selected_color_id_raw)
|
||||
items = [item for item in items if any(c.id == selected_color_id_int for c in item.colors.all())]
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
item_prices = [item.display_price for item in items]
|
||||
slider_min_bound = min(item_prices, default=Decimal('0'))
|
||||
slider_max_bound = max(item_prices, default=slider_min_bound)
|
||||
@@ -176,6 +192,13 @@ def index(request: HttpRequest, category: str | None = None) -> HttpResponse:
|
||||
slider_selected_min = min(max(slider_selected_min, slider_min_bound), slider_max_bound)
|
||||
slider_selected_max = min(max(slider_selected_max, slider_min_bound), slider_max_bound)
|
||||
|
||||
page_number = request.GET.get('page', 1)
|
||||
paginator = Paginator(items, 24)
|
||||
page_obj = paginator.get_page(page_number)
|
||||
|
||||
pagination_params = {k: v for k, v in request.GET.items() if k != 'page'}
|
||||
pagination_query_string = urlencode(pagination_params)
|
||||
|
||||
categories = list(Category.objects.order_by('name'))
|
||||
all_items_url = _build_catalog_url(
|
||||
search_query=search_query,
|
||||
@@ -183,6 +206,7 @@ def index(request: HttpRequest, category: str | None = None) -> HttpResponse:
|
||||
max_price=max_price_raw,
|
||||
sale=filter_sale,
|
||||
new=filter_new,
|
||||
color=selected_color_id_raw,
|
||||
)
|
||||
for catalog_category in categories:
|
||||
catalog_category.catalog_url = _build_catalog_url(
|
||||
@@ -192,6 +216,7 @@ def index(request: HttpRequest, category: str | None = None) -> HttpResponse:
|
||||
max_price=max_price_raw,
|
||||
sale=filter_sale,
|
||||
new=filter_new,
|
||||
color=selected_color_id_raw,
|
||||
)
|
||||
|
||||
subcategories = list(subcategories)
|
||||
@@ -204,6 +229,7 @@ def index(request: HttpRequest, category: str | None = None) -> HttpResponse:
|
||||
max_price=max_price_raw,
|
||||
sale=filter_sale,
|
||||
new=filter_new,
|
||||
color=selected_color_id_raw,
|
||||
)
|
||||
for catalog_subcategory in subcategories:
|
||||
catalog_subcategory.catalog_url = _build_catalog_url(
|
||||
@@ -214,6 +240,7 @@ def index(request: HttpRequest, category: str | None = None) -> HttpResponse:
|
||||
max_price=max_price_raw,
|
||||
sale=filter_sale,
|
||||
new=filter_new,
|
||||
color=selected_color_id_raw,
|
||||
)
|
||||
|
||||
price_filter_action = _build_catalog_url(selected_category.id)
|
||||
@@ -235,6 +262,11 @@ def index(request: HttpRequest, category: str | None = None) -> HttpResponse:
|
||||
'categories': categories,
|
||||
'subcategories': subcategories,
|
||||
'items': items,
|
||||
'page_obj': page_obj,
|
||||
'paginator': paginator,
|
||||
'pagination_query_string': pagination_query_string,
|
||||
'available_colors': available_colors,
|
||||
'selected_color_id': selected_color_id_raw,
|
||||
'selected_category': selected_category,
|
||||
'selected_subcategory': selected_subcategory,
|
||||
'all_items_url': all_items_url,
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
<main class="item-list">
|
||||
{% include "_fragments/item-grid.html" with items=items empty_message="В этой категории пока нет товаров." %}
|
||||
{% include "_fragments/item-grid.html" with items=page_obj empty_message="В этой категории пока нет товаров." %}
|
||||
|
||||
{% if paginator.num_pages > 1 %}
|
||||
<nav class="catalog-pagination">
|
||||
{% if page_obj.has_previous %}
|
||||
<a href="?{{ pagination_query_string }}&page={{ page_obj.previous_page_number }}" class="catalog-pagination__btn">«</a>
|
||||
{% endif %}
|
||||
{% for i in paginator.page_range %}
|
||||
<a href="?{{ pagination_query_string }}&page={{ i }}"
|
||||
class="catalog-pagination__btn {% if i == page_obj.number %}active{% endif %}">{{ i }}</a>
|
||||
{% endfor %}
|
||||
{% if page_obj.has_next %}
|
||||
<a href="?{{ pagination_query_string }}&page={{ page_obj.next_page_number }}" class="catalog-pagination__btn">»</a>
|
||||
{% endif %}
|
||||
</nav>
|
||||
{% endif %}
|
||||
</main>
|
||||
|
||||
@@ -85,13 +85,19 @@
|
||||
<button type="submit" class="catalog-filter-submit">Применить</button>
|
||||
</section>
|
||||
|
||||
{% if available_colors %}
|
||||
<section class="a-section color-section">
|
||||
<h2>Цвет</h2>
|
||||
<select name="color" onchange="this.form.submit()">
|
||||
<option value="">Любой</option>
|
||||
{% for color in available_colors %}
|
||||
<option value="{{ color.id }}" {% if selected_color_id == color.id|stringformat:"i" %}selected{% endif %}>
|
||||
{{ color.name }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</section>
|
||||
|
||||
<section class="a-section material-section">
|
||||
<h2>Материал</h2>
|
||||
</section>
|
||||
{% endif %}
|
||||
</form>
|
||||
|
||||
<section class="a-section promotions-section">
|
||||
|
||||
Reference in New Issue
Block a user