feat(products): улучшить интерфейс массовой синхронизации с Recommerce

- Добавить секцию маркетинговых флагов в модалку синхронизации
- Добавить кнопки "Выбрать все" для групп полей
- Улучшить UX отображения списка товаров

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-13 13:21:08 +03:00
parent 3cffa9b05d
commit 36090382c1
5 changed files with 269 additions and 95 deletions

View File

@@ -12,7 +12,7 @@
// DOM elements // DOM elements
const selectAllCheckbox = document.getElementById('select-all-checkbox'); const selectAllCheckbox = document.getElementById('select-all-checkbox');
const batchActionsBtn = document.getElementById('batch-actions-btn'); const batchActionsBtn = document.getElementById('batch-actions-btn');
const batchActionsDropdown = document.getElementById('batch-actions-dropdown'); const batchActionsWrapper = document.getElementById('batch-actions-wrapper');
let selectionCountSpan = document.getElementById('selection-count'); let selectionCountSpan = document.getElementById('selection-count');
/** /**
@@ -139,15 +139,21 @@
if (batchActionsBtn) { if (batchActionsBtn) {
batchActionsBtn.disabled = !shouldEnable; batchActionsBtn.disabled = !shouldEnable;
// Управляем подсказкой через data-атрибут
if (batchActionsWrapper) {
if (shouldEnable) {
batchActionsWrapper.removeAttribute('data-hint');
} else {
batchActionsWrapper.setAttribute('data-hint', 'true');
}
}
// Если в кнопке нет span#selection-count, значит текст был изменён - восстанавливаем // Если в кнопке нет span#selection-count, значит текст был изменён - восстанавливаем
if (!batchActionsBtn.querySelector('#selection-count')) { if (!batchActionsBtn.querySelector('#selection-count')) {
batchActionsBtn.innerHTML = `<i class="bi bi-gear-fill"></i> Действия над выбранными (<span id="selection-count">${count}</span>)`; batchActionsBtn.innerHTML = `<i class="bi bi-gear-fill"></i> Действия над выбранными (<span id="selection-count">${count}</span>)`;
selectionCountSpan = document.getElementById('selection-count'); selectionCountSpan = document.getElementById('selection-count');
} }
} }
if (batchActionsDropdown) {
batchActionsDropdown.disabled = !shouldEnable;
}
// Показываем/скрываем dropdown для выбора всех // Показываем/скрываем dropdown для выбора всех
const selectAllDropdown = document.getElementById('select-all-dropdown-group'); const selectAllDropdown = document.getElementById('select-all-dropdown-group');

View File

@@ -62,6 +62,11 @@ document.addEventListener('DOMContentLoaded', function() {
if (document.getElementById('syncContent').checked) options.fields.push('content'); if (document.getElementById('syncContent').checked) options.fields.push('content');
if (document.getElementById('syncImages').checked) options.fields.push('images'); if (document.getElementById('syncImages').checked) options.fields.push('images');
// Маркетинговые флаги
if (document.getElementById('syncIsNew')?.checked) options.fields.push('is_new');
if (document.getElementById('syncIsPopular')?.checked) options.fields.push('is_popular');
if (document.getElementById('syncIsSpecial')?.checked) options.fields.push('is_special');
// Блокируем кнопку // Блокируем кнопку
startBtn.disabled = true; startBtn.disabled = true;
const originalText = startBtn.innerHTML; const originalText = startBtn.innerHTML;
@@ -125,4 +130,24 @@ document.addEventListener('DOMContentLoaded', function() {
} }
return cookieValue; return cookieValue;
} }
// Кнопки "Выбрать все" для групп полей
document.getElementById('toggleBasicFields')?.addEventListener('click', function() {
const check = !this.dataset.allSelected || this.dataset.allSelected === 'false';
this.dataset.allSelected = check;
this.textContent = check ? 'Снять все' : 'Выбрать все';
document.getElementById('syncPrice').checked = check;
document.getElementById('syncStock').checked = check;
document.getElementById('syncContent').checked = check;
document.getElementById('syncImages').checked = check;
});
document.getElementById('toggleMarketingFields')?.addEventListener('click', function() {
const check = !this.dataset.allSelected || this.dataset.allSelected === 'false';
this.dataset.allSelected = check;
this.textContent = check ? 'Снять все' : 'Выбрать все';
document.getElementById('syncIsNew').checked = check;
document.getElementById('syncIsPopular').checked = check;
document.getElementById('syncIsSpecial').checked = check;
});
}); });

View File

@@ -1,6 +1,6 @@
<!-- Модальное окно для синхронизации с Recommerce --> <!-- Модальное окно для синхронизации с Recommerce -->
<div class="modal fade" id="recommerceSyncModal" tabindex="-1" aria-labelledby="recommerceSyncModalLabel" aria-hidden="true"> <div class="modal fade" id="recommerceSyncModal" tabindex="-1" aria-labelledby="recommerceSyncModalLabel" aria-hidden="true">
<div class="modal-dialog"> <div class="modal-dialog modal-lg">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title" id="recommerceSyncModalLabel"> <h5 class="modal-title" id="recommerceSyncModalLabel">
@@ -9,52 +9,99 @@
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Закрыть"></button> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Закрыть"></button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div class="alert alert-info mb-3"> <div class="alert alert-info mb-4">
<i class="bi bi-info-circle"></i> <strong>Выбрано товаров:</strong> <span id="recommerceSyncCount">0</span> <i class="bi bi-info-circle"></i> <strong>Выбрано товаров:</strong> <span id="recommerceSyncCount">0</span>
</div> </div>
<div class="mb-3"> <!-- Основные данные -->
<label class="form-label fw-bold">Что обновлять?</label> <div class="card mb-3">
<div class="card-header py-2 d-flex justify-content-between align-items-center">
<strong><i class="bi bi-box-seam"></i> Основные данные</strong>
<button type="button" class="btn btn-sm btn-link p-0" id="toggleBasicFields">Выбрать все</button>
</div>
<div class="card-body">
<div class="row g-2">
<div class="col-6">
<div class="form-check"> <div class="form-check">
<input class="form-check-input" type="checkbox" value="price" id="syncPrice" checked> <input class="form-check-input" type="checkbox" value="price" id="syncPrice" checked>
<label class="form-check-label" for="syncPrice"> <label class="form-check-label" for="syncPrice">
Цены <i class="bi bi-tag"></i> Цены
</label> </label>
</div> </div>
</div>
<div class="col-6">
<div class="form-check"> <div class="form-check">
<input class="form-check-input" type="checkbox" value="count" id="syncStock" checked> <input class="form-check-input" type="checkbox" value="count" id="syncStock">
<label class="form-check-label" for="syncStock"> <label class="form-check-label" for="syncStock">
Остатки <i class="bi bi-stack"></i> Остатки
</label> </label>
</div> </div>
</div>
<div class="col-6">
<div class="form-check"> <div class="form-check">
<input class="form-check-input" type="checkbox" value="content" id="syncContent"> <input class="form-check-input" type="checkbox" value="content" id="syncContent">
<label class="form-check-label" for="syncContent"> <label class="form-check-label" for="syncContent">
Название и описание <i class="bi bi-card-text"></i> Название и описание
</label> </label>
</div> </div>
</div>
<div class="col-6">
<div class="form-check"> <div class="form-check">
<input class="form-check-input" type="checkbox" value="images" id="syncImages"> <input class="form-check-input" type="checkbox" value="images" id="syncImages">
<label class="form-check-label" for="syncImages"> <label class="form-check-label" for="syncImages">
Изображения <i class="bi bi-images"></i> Изображения
</label> </label>
</div> </div>
</div> </div>
</div>
</div>
</div>
<hr> <!-- Маркетинговые флаги -->
<div class="card mb-3">
<div class="card-header py-2 d-flex justify-content-between align-items-center">
<strong><i class="bi bi-megaphone"></i> Маркетинговые флаги</strong>
<button type="button" class="btn btn-sm btn-link p-0" id="toggleMarketingFields">Выбрать все</button>
</div>
<div class="card-body">
<div class="row g-2">
<div class="col-6 col-md-4">
<div class="form-check">
<input class="form-check-input" type="checkbox" value="is_new" id="syncIsNew">
<label class="form-check-label" for="syncIsNew">
<span class="badge bg-warning text-dark me-1"><i class="bi bi-stars"></i></span> Новинки
</label>
</div>
</div>
<div class="col-6 col-md-4">
<div class="form-check">
<input class="form-check-input" type="checkbox" value="is_popular" id="syncIsPopular">
<label class="form-check-label" for="syncIsPopular">
<span class="badge bg-danger me-1"><i class="bi bi-fire"></i></span> Популярные
</label>
</div>
</div>
<div class="col-6 col-md-4">
<div class="form-check">
<input class="form-check-input" type="checkbox" value="is_special" id="syncIsSpecial">
<label class="form-check-label" for="syncIsSpecial">
<span class="badge bg-success me-1"><i class="bi bi-percent"></i></span> Спецпредложения
</label>
<div class="form-text text-muted small">Также авто при скидке</div>
</div>
</div>
</div>
</div>
</div>
<div class="mb-3"> <!-- Дополнительно -->
<div class="form-check form-switch"> <div class="card">
<div class="card-body py-2">
<div class="form-check form-switch mb-0">
<input class="form-check-input" type="checkbox" id="syncCreateNew"> <input class="form-check-input" type="checkbox" id="syncCreateNew">
<label class="form-check-label" for="syncCreateNew"> <label class="form-check-label" for="syncCreateNew">
Создавать товары, если не найдены <strong>Создавать товары, если не найдены в Recommerce</strong>
</label> </label>
<div class="form-text text-muted">
Если товар отсутствует в Recommerce, он будет создан (требуется полное заполнение).
</div> </div>
</div> </div>
</div> </div>
@@ -62,7 +109,7 @@
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Отмена</button> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Отмена</button>
<button type="button" class="btn btn-primary" id="startRecommerceSyncBtn"> <button type="button" class="btn btn-primary" id="startRecommerceSyncBtn">
<i class="bi bi-play-fill"></i> Запустить <i class="bi bi-play-fill"></i> Запустить синхронизацию
</button> </button>
</div> </div>
</div> </div>

View File

@@ -37,30 +37,30 @@
<hr class="my-3"> <hr class="my-3">
<form method="get" id="filterForm"> <form method="get" id="filterForm">
<div class="row g-3"> <div class="row g-2">
<!-- Поиск --> <!-- Поиск -->
<div class="col-12 col-md-4"> <div class="col-12 col-md-4">
<label for="search" class="form-label"><i class="bi bi-search"></i> Поиск</label> <label for="search" class="form-label small mb-1">Поиск</label>
<input type="text" class="form-control" id="search" name="search" <input type="text" class="form-control form-control-sm" id="search" name="search"
placeholder="Название, артикул, описание..." placeholder="Название, артикул..."
value="{{ filters.current.search|default:'' }}"> value="{{ filters.current.search|default:'' }}">
</div> </div>
<!-- Тип --> <!-- Тип -->
<div class="col-12 col-md-2"> <div class="col-6 col-md-1">
<label for="type" class="form-label"><i class="bi bi-box"></i> Тип</label> <label for="type" class="form-label small mb-1">Тип</label>
<select class="form-select" id="type" name="type"> <select class="form-select form-select-sm" id="type" name="type">
<option value="all" {% if filters.current.type == 'all' %}selected{% endif %}>Все</option> <option value="all" {% if filters.current.type == 'all' %}selected{% endif %}>Все</option>
<option value="products" {% if filters.current.type == 'products' %}selected{% endif %}>Только товары</option> <option value="products" {% if filters.current.type == 'products' %}selected{% endif %}>Товары</option>
<option value="kits" {% if filters.current.type == 'kits' %}selected{% endif %}>Только комплекты</option> <option value="kits" {% if filters.current.type == 'kits' %}selected{% endif %}>Комплекты</option>
</select> </select>
</div> </div>
<!-- Категория --> <!-- Категория -->
<div class="col-12 col-md-3"> <div class="col-6 col-md-2">
<label for="category" class="form-label"><i class="bi bi-bookmark"></i> Категория</label> <label for="category" class="form-label small mb-1">Категория</label>
<select class="form-select" id="category" name="category"> <select class="form-select form-select-sm" id="category" name="category">
<option value="">Все категории</option> <option value="">Все</option>
{% for category in filters.categories %} {% for category in filters.categories %}
<option value="{{ category.id }}" {% if filters.current.category == category.id|stringformat:"s" %}selected{% endif %}> <option value="{{ category.id }}" {% if filters.current.category == category.id|stringformat:"s" %}selected{% endif %}>
{{ category.name }} {{ category.name }}
@@ -70,10 +70,10 @@
</div> </div>
<!-- Статус --> <!-- Статус -->
<div class="col-12 col-md-3"> <div class="col-6 col-md-1">
<label for="status" class="form-label"><i class="bi bi-toggle-on"></i> Статус</label> <label for="status" class="form-label small mb-1">Статус</label>
<select class="form-select" id="status" name="status"> <select class="form-select form-select-sm" id="status" name="status">
<option value="">Все статусы</option> <option value="">Все</option>
{% for status_value, status_name in item_statuses %} {% for status_value, status_name in item_statuses %}
<option value="{{ status_value }}" {% if filters.current.status == status_value %}selected{% endif %}> <option value="{{ status_value }}" {% if filters.current.status == status_value %}selected{% endif %}>
{{ status_name }} {{ status_name }}
@@ -81,52 +81,101 @@
{% endfor %} {% endfor %}
</select> </select>
</div> </div>
<!-- Со скидкой -->
<div class="col-6 col-md-1">
<label class="form-label small mb-1">Скидка</label>
<select class="form-select form-select-sm" id="has_discount" name="has_discount">
<option value=""></option>
<option value="1" {% if filters.current.has_discount == '1' %}selected{% endif %}>Да</option>
</select>
</div> </div>
<div class="row g-3 mt-2"> <!-- Маркетинг -->
<!-- В наличии (только для товаров) -->
<div class="col-12 col-md-3"> <div class="col-12 col-md-3">
<label for="in_stock" class="form-label"><i class="bi bi-check-circle"></i> В наличии</label> <label class="form-label small mb-1">Маркетинг</label>
<select class="form-select" id="in_stock" name="in_stock"> <div class="btn-group w-100 d-flex" role="group" style="flex-wrap: wrap;">
<option value="">Все</option> <input type="checkbox" class="btn-check" id="is_new" name="is_new" value="1"
{% if filters.current.is_new == '1' %}checked{% endif %}
onchange="document.getElementById('filterForm').submit()">
<label class="btn btn-outline-warning btn-sm flex-fill" for="is_new" title="Новинки"
style="white-space: normal; text-align: center; font-size: 0.75rem;">
<i class="bi bi-stars d-block"></i> Новинки
</label>
<input type="checkbox" class="btn-check" id="is_popular" name="is_popular" value="1"
{% if filters.current.is_popular == '1' %}checked{% endif %}
onchange="document.getElementById('filterForm').submit()">
<label class="btn btn-outline-danger btn-sm flex-fill" for="is_popular" title="Популярные"
style="white-space: normal; text-align: center; font-size: 0.75rem;">
<i class="bi bi-fire d-block"></i> Популярные
</label>
<input type="checkbox" class="btn-check" id="is_special" name="is_special" value="1"
{% if filters.current.is_special == '1' %}checked{% endif %}
onchange="document.getElementById('filterForm').submit()">
<label class="btn btn-outline-success btn-sm flex-fill" for="is_special" title="Акции"
style="white-space: normal; text-align: center; font-size: 0.75rem;">
<i class="bi bi-percent d-block"></i> Акции
</label>
</div>
</div>
</div>
<div class="row g-2">
<!-- Цена -->
<div class="col-12 col-md-6">
<label class="form-label small mb-1">Цена</label>
<div class="input-group input-group-sm">
<input type="number" class="form-control" id="price_from" name="price_from" placeholder="От"
value="{{ filters.current.price_from|default:'' }}" min="0" step="0.01">
<span class="input-group-text"></span>
<input type="number" class="form-control" id="price_to" name="price_to" placeholder="До"
value="{{ filters.current.price_to|default:'' }}" min="0" step="0.01">
</div>
</div>
<!-- В наличии -->
<div class="col-4 col-md-1">
<label for="in_stock" class="form-label small mb-1">Наличие</label>
<select class="form-select form-select-sm" id="in_stock" name="in_stock">
<option value=""></option>
<option value="1" {% if filters.current.in_stock == '1' %}selected{% endif %}>Да</option> <option value="1" {% if filters.current.in_stock == '1' %}selected{% endif %}>Да</option>
<option value="0" {% if filters.current.in_stock == '0' %}selected{% endif %}>Нет</option> <option value="0" {% if filters.current.in_stock == '0' %}selected{% endif %}>Нет</option>
</select> </select>
</div> </div>
<!-- Теги --> <!-- Теги -->
<div class="col-12 col-md-3"> <div class="col-4 col-md-2">
<label for="tags" class="form-label"><i class="bi bi-tags"></i> Теги</label> <label for="tags" class="form-label small mb-1">Теги</label>
<select class="form-select" id="tags" name="tags" multiple size="1"> <select class="form-select form-select-sm" id="tags" name="tags" multiple size="1">
{% for tag in filters.tags %} {% for tag in filters.tags %}
<option value="{{ tag.id }}" {% if tag.id in filters.current.tags %}selected{% endif %}> <option value="{{ tag.id }}" {% if tag.id in filters.current.tags %}selected{% endif %}>
{{ tag.name }} {{ tag.name }}
</option> </option>
{% endfor %} {% endfor %}
</select> </select>
{% if filters.current.tags %}
<small class="text-muted">Выбрано: {{ filters.current.tags|length }}</small>
{% endif %}
</div> </div>
<!-- Показывать по --> <!-- per_page -->
<div class="col-12 col-md-2"> <div class="col-4 col-md-1">
<label for="per_page" class="form-label"><i class="bi bi-list-ol"></i> Показывать по:</label> <label for="per_page" class="form-label small mb-1">На стр.</label>
<select class="form-select" id="per_page" name="per_page" onchange="this.form.submit()"> <select class="form-select form-select-sm" id="per_page" name="per_page" onchange="this.form.submit()">
<option value="20" {% if filters.current.per_page == '20' %}selected{% endif %}>20</option> <option value="20" {% if filters.current.per_page == '20' %}selected{% endif %}>20</option>
<option value="50" {% if filters.current.per_page == '50' %}selected{% endif %}>50</option> <option value="50" {% if filters.current.per_page == '50' %}selected{% endif %}>50</option>
<option value="100" {% if filters.current.per_page == '100' %}selected{% endif %}>100</option> <option value="100" {% if filters.current.per_page == '100' %}selected{% endif %}>100</option>
</select> </select>
</div> </div>
<div class="col-12 col-md-4"> <!-- Кнопки -->
<label class="form-label d-none d-md-block">&nbsp;</label> <div class="col-12 col-md-2">
<div class="d-flex gap-2"> <label class="form-label small mb-1 d-none d-md-block">&nbsp;</label>
<button type="submit" class="btn btn-primary"> <div class="d-flex gap-1">
<i class="bi bi-check-circle"></i> Применить <button type="submit" class="btn btn-primary btn-sm flex-fill">
OK
</button> </button>
<a href="{% url 'products:products-list' %}" class="btn btn-outline-secondary"> <a href="{% url 'products:products-list' %}" class="btn btn-outline-secondary btn-sm">
<i class="bi bi-x-circle"></i> Сбросить X
</a> </a>
</div> </div>
</div> </div>
@@ -175,15 +224,13 @@
</ul> </ul>
</div> </div>
</div> </div>
<div class="btn-group"> <div class="dropdown d-flex align-items-center gap-2" id="batch-actions-wrapper" style="position: relative;">
<button type="button" class="btn btn-outline-secondary" id="batch-actions-btn" disabled> <button type="button" class="btn btn-light dropdown-toggle border" id="batch-actions-btn" disabled
data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-gear-fill"></i> Действия над выбранными (<span id="selection-count">0</span>) <i class="bi bi-gear-fill"></i> Действия над выбранными (<span id="selection-count">0</span>)
</button> </button>
<button type="button" class="btn btn-outline-secondary dropdown-toggle dropdown-toggle-split" <span class="badge bg-secondary" id="batch-actions-hint" style="position: absolute; top: 100%; left: 0; margin-top: 4px; z-index: 10; white-space: nowrap; display: none;">Сначала выберите товары</span>
id="batch-actions-dropdown" data-bs-toggle="dropdown" aria-expanded="false" disabled> <ul class="dropdown-menu dropdown-menu-end" aria-labelledby="batch-actions-btn">
<span class="visually-hidden">Открыть меню</span>
</button>
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="batch-actions-dropdown">
<li> <li>
<a class="dropdown-item" href="#" id="bulk-category-action"> <a class="dropdown-item" href="#" id="bulk-category-action">
<i class="bi bi-bookmark-fill"></i> Изменить категории <i class="bi bi-bookmark-fill"></i> Изменить категории
@@ -495,11 +542,18 @@
</div> </div>
</div> </div>
</div> </div>
<style>
#batch-actions-wrapper[data-hint="true"]:hover #batch-actions-hint {
display: inline-block !important;
}
</style>
{% endblock %} {% endblock %}
{% block extra_js %} {% block extra_js %}
{% load static %} {% load static %}
<script src="{% static 'products/js/batch-selection.js' %}?v=1.2"></script> <script src="{% static 'products/js/batch-selection.js' %}?v=1.5"></script>
<script src="{% static 'products/js/bulk-category-modal.js' %}?v=1.6"></script> <script src="{% static 'products/js/bulk-category-modal.js' %}?v=1.6"></script>
<script src="{% static 'products/js/recommerce-sync.js' %}?v=1.1"></script> <script src="{% static 'products/js/recommerce-sync.js' %}?v=1.1"></script>
{% endblock %} {% endblock %}

View File

@@ -343,6 +343,42 @@ class CombinedProductListView(LoginRequiredMixin, ManagerOwnerRequiredMixin, Lis
products = products.filter(tags__id__in=tags).distinct() products = products.filter(tags__id__in=tags).distinct()
kits = kits.filter(tags__id__in=tags).distinct() kits = kits.filter(tags__id__in=tags).distinct()
# Фильтр по цене
price_from = self.request.GET.get('price_from')
price_to = self.request.GET.get('price_to')
if price_from:
products = products.filter(
Q(sale_price__gte=price_from) | Q(price__gte=price_from)
).distinct()
kits = kits.filter(
Q(sale_price__gte=price_from) | Q(price__gte=price_from)
).distinct()
if price_to:
products = products.filter(
Q(sale_price__lte=price_to) | Q(price__lte=price_to)
).distinct()
kits = kits.filter(
Q(sale_price__lte=price_to) | Q(price__lte=price_to)
).distinct()
# Маркетинговые фильтры (OR логика — объединяются через Q)
marketing_filters = Q()
if self.request.GET.get('is_new') == '1':
marketing_filters |= Q(is_new=True)
if self.request.GET.get('is_popular') == '1':
marketing_filters |= Q(is_popular=True)
if self.request.GET.get('is_special') == '1':
marketing_filters |= Q(is_special=True)
if marketing_filters:
products = products.filter(marketing_filters)
kits = kits.filter(marketing_filters)
# Фильтр по скидке
has_discount = self.request.GET.get('has_discount')
if has_discount == '1':
products = products.filter(sale_price__gt=0)
kits = kits.filter(sale_price__gt=0)
# Применяем фильтр по типу # Применяем фильтр по типу
products_list = [] products_list = []
kits_list = [] kits_list = []
@@ -387,6 +423,12 @@ class CombinedProductListView(LoginRequiredMixin, ManagerOwnerRequiredMixin, Lis
'in_stock': self.request.GET.get('in_stock', ''), 'in_stock': self.request.GET.get('in_stock', ''),
'tags': [int(tag) for tag in self.request.GET.getlist('tags') if tag.isdigit()], 'tags': [int(tag) for tag in self.request.GET.getlist('tags') if tag.isdigit()],
'per_page': self.request.GET.get('per_page', '20'), 'per_page': self.request.GET.get('per_page', '20'),
'price_from': self.request.GET.get('price_from', ''),
'price_to': self.request.GET.get('price_to', ''),
'is_new': self.request.GET.get('is_new', ''),
'is_popular': self.request.GET.get('is_popular', ''),
'is_special': self.request.GET.get('is_special', ''),
'has_discount': self.request.GET.get('has_discount', ''),
} }
} }