Добавлены фильтры для списка клиентов через django-filter
- Создан CustomerFilter с тремя фильтрами: * Есть заметки (has_notes) * Нет телефона (no_phone) * Нет email (no_email) - Обновлен views.py для использования фильтров - Добавлены чекбоксы фильтров в шаблон списка клиентов - Фильтры работают совместно с поиском - Кнопка Очистить отображается при активных фильтрах или поиске
This commit is contained in:
61
myproject/customers/filters.py
Normal file
61
myproject/customers/filters.py
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Фильтры для клиентов с использованием django-filter
|
||||||
|
"""
|
||||||
|
|
||||||
|
import django_filters
|
||||||
|
from django import forms
|
||||||
|
from .models import Customer
|
||||||
|
|
||||||
|
|
||||||
|
class CustomerFilter(django_filters.FilterSet):
|
||||||
|
"""
|
||||||
|
Фильтр для списка клиентов
|
||||||
|
Поддерживает фильтрацию по:
|
||||||
|
- Наличию заметок
|
||||||
|
- Отсутствию телефона
|
||||||
|
- Отсутствию email
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Фильтр: есть заметки
|
||||||
|
has_notes = django_filters.BooleanFilter(
|
||||||
|
method='filter_has_notes',
|
||||||
|
label='Есть заметки',
|
||||||
|
widget=forms.CheckboxInput(attrs={'class': 'form-check-input'})
|
||||||
|
)
|
||||||
|
|
||||||
|
# Фильтр: нет телефона
|
||||||
|
no_phone = django_filters.BooleanFilter(
|
||||||
|
method='filter_no_phone',
|
||||||
|
label='Нет телефона',
|
||||||
|
widget=forms.CheckboxInput(attrs={'class': 'form-check-input'})
|
||||||
|
)
|
||||||
|
|
||||||
|
# Фильтр: нет email
|
||||||
|
no_email = django_filters.BooleanFilter(
|
||||||
|
method='filter_no_email',
|
||||||
|
label='Нет email',
|
||||||
|
widget=forms.CheckboxInput(attrs={'class': 'form-check-input'})
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Customer
|
||||||
|
fields = ['has_notes', 'no_phone', 'no_email']
|
||||||
|
|
||||||
|
def filter_has_notes(self, queryset, name, value):
|
||||||
|
"""Фильтр клиентов с заметками"""
|
||||||
|
if value:
|
||||||
|
return queryset.filter(notes__isnull=False).exclude(notes='')
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
def filter_no_phone(self, queryset, name, value):
|
||||||
|
"""Фильтр клиентов без телефона"""
|
||||||
|
if value:
|
||||||
|
return queryset.filter(phone__isnull=True) | queryset.filter(phone='')
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
def filter_no_email(self, queryset, name, value):
|
||||||
|
"""Фильтр клиентов без email"""
|
||||||
|
if value:
|
||||||
|
return queryset.filter(email__isnull=True) | queryset.filter(email='')
|
||||||
|
return queryset
|
||||||
@@ -27,21 +27,48 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Поиск -->
|
<!-- Поиск и фильтры -->
|
||||||
<div class="card mb-3">
|
<div class="card mb-3">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<form method="get" class="row g-2">
|
<form method="get" class="row g-3">
|
||||||
<div class="col-md-8">
|
<!-- Поиск -->
|
||||||
|
<div class="col-md-6">
|
||||||
<input type="text" class="form-control" name="q" value="{{ query }}"
|
<input type="text" class="form-control" name="q" value="{{ query }}"
|
||||||
placeholder="Поиск по имени, email или телефону..."
|
placeholder="Поиск по имени, email или телефону..."
|
||||||
autofocus>
|
autofocus>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4">
|
|
||||||
<div class="btn-group w-100" role="group">
|
<!-- Фильтры -->
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="d-flex gap-3 align-items-center">
|
||||||
|
<div class="form-check">
|
||||||
|
{{ filter.form.has_notes }}
|
||||||
|
<label class="form-check-label" for="{{ filter.form.has_notes.id_for_label }}">
|
||||||
|
Есть заметки
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check">
|
||||||
|
{{ filter.form.no_phone }}
|
||||||
|
<label class="form-check-label" for="{{ filter.form.no_phone.id_for_label }}">
|
||||||
|
Нет телефона
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check">
|
||||||
|
{{ filter.form.no_email }}
|
||||||
|
<label class="form-check-label" for="{{ filter.form.no_email.id_for_label }}">
|
||||||
|
Нет email
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Кнопки -->
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="btn-group" role="group">
|
||||||
<button type="submit" class="btn btn-primary">
|
<button type="submit" class="btn btn-primary">
|
||||||
<i class="bi bi-search"></i> Поиск
|
<i class="bi bi-search"></i> Поиск / Фильтр
|
||||||
</button>
|
</button>
|
||||||
{% if query %}
|
{% if query or filter.form.has_notes.value or filter.form.no_phone.value or filter.form.no_email.value %}
|
||||||
<a href="{% url 'customers:customer-list' %}" class="btn btn-outline-secondary">
|
<a href="{% url 'customers:customer-list' %}" class="btn btn-outline-secondary">
|
||||||
<i class="bi bi-x-circle"></i> Очистить
|
<i class="bi bi-x-circle"></i> Очистить
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import json
|
|||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from .models import Customer, ContactChannel
|
from .models import Customer, ContactChannel
|
||||||
from .forms import CustomerForm, ContactChannelForm
|
from .forms import CustomerForm, ContactChannelForm
|
||||||
|
from .filters import CustomerFilter
|
||||||
|
|
||||||
|
|
||||||
def normalize_query_phone(q):
|
def normalize_query_phone(q):
|
||||||
@@ -33,6 +34,10 @@ def customer_list(request):
|
|||||||
# Исключаем системного клиента из списка
|
# Исключаем системного клиента из списка
|
||||||
customers = Customer.objects.filter(is_system_customer=False)
|
customers = Customer.objects.filter(is_system_customer=False)
|
||||||
|
|
||||||
|
# Применяем фильтры django-filter
|
||||||
|
customer_filter = CustomerFilter(request.GET, queryset=customers)
|
||||||
|
customers = customer_filter.qs
|
||||||
|
|
||||||
if query:
|
if query:
|
||||||
# Нормализуем номер телефона
|
# Нормализуем номер телефона
|
||||||
phone_normalized = normalize_query_phone(query)
|
phone_normalized = normalize_query_phone(query)
|
||||||
@@ -79,6 +84,7 @@ def customer_list(request):
|
|||||||
'page_obj': page_obj,
|
'page_obj': page_obj,
|
||||||
'query': query,
|
'query': query,
|
||||||
'total_customers': paginator.count, # Используем count из paginator, чтобы избежать дублирования SQL запроса
|
'total_customers': paginator.count, # Используем count из paginator, чтобы избежать дублирования SQL запроса
|
||||||
|
'filter': customer_filter, # Добавляем фильтр в контекст
|
||||||
}
|
}
|
||||||
return render(request, 'customers/customer_list.html', context)
|
return render(request, 'customers/customer_list.html', context)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user