Добавлены фильтры для списка клиентов через django-filter

- Создан CustomerFilter с тремя фильтрами:
  * Есть заметки (has_notes)
  * Нет телефона (no_phone)
  * Нет email (no_email)

- Обновлен views.py для использования фильтров
- Добавлены чекбоксы фильтров в шаблон списка клиентов
- Фильтры работают совместно с поиском
- Кнопка Очистить отображается при активных фильтрах или поиске
This commit is contained in:
2026-01-03 14:50:24 +03:00
parent 63a965ae5c
commit 5ded404346
3 changed files with 101 additions and 7 deletions

View 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

View File

@@ -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>

View File

@@ -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)