Устранение дублирования логики поиска между customer_list() и api_search_customers()
Проблема: Было две разных реализации логики поиска: - customer_list() использовала простой icontains везде - api_search_customers() использовала новую smart-логику с determine_search_strategy() Решение: 1. Создана функция build_customer_search_query() которая строит Q-объект на основе стратегии поиска 2. Обновлена customer_list() чтобы использовать: - determine_search_strategy() для определения стратегии - build_customer_search_query() для построения Q-объекта 3. Обновлена api_search_customers() чтобы использовать build_customer_search_query() вместо дублирования логики Результат: ЕДИНАЯ логика поиска везде ✓ Архитектура: 1. normalize_query_phone() — нормализация номеров телефонов 2. determine_search_strategy() — определение стратегии поиска 3. build_customer_search_query() — построение Q-объекта ← NEW 4. customer_list() — используется в веб-интерфейсе списка клиентов 5. api_search_customers() — используется в AJAX для Select2 Все 23 unit-теста проходят успешно ✓ Преимущества: - Единая логика поиска во всем приложении - Легче поддерживать и расширять - Новая функция можно переиспользовать в других местах - Меньше дублирования кода 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -29,13 +29,23 @@ def customer_list(request):
|
|||||||
customers = Customer.objects.all()
|
customers = Customer.objects.all()
|
||||||
|
|
||||||
if query:
|
if query:
|
||||||
# Try to normalize the phone number for searching
|
# Используем ту же логику поиска, что и в AJAX API (api_search_customers)
|
||||||
|
# Это обеспечивает согласованность между веб-интерфейсом и API
|
||||||
|
|
||||||
|
# Нормализуем номер телефона
|
||||||
phone_normalized = normalize_query_phone(query)
|
phone_normalized = normalize_query_phone(query)
|
||||||
customers = customers.filter(
|
|
||||||
Q(name__icontains=query) |
|
# Определяем стратегию поиска
|
||||||
Q(email__icontains=query) |
|
strategy, search_value = determine_search_strategy(query)
|
||||||
Q(phone__icontains=phone_normalized)
|
|
||||||
)
|
# Строим Q-объект для поиска (единая функция)
|
||||||
|
q_objects = build_customer_search_query(query, strategy, search_value)
|
||||||
|
|
||||||
|
# Добавляем поиск по телефону
|
||||||
|
if phone_normalized:
|
||||||
|
q_objects |= Q(phone__icontains=phone_normalized)
|
||||||
|
|
||||||
|
customers = customers.filter(q_objects)
|
||||||
|
|
||||||
customers = customers.order_by('-created_at')
|
customers = customers.order_by('-created_at')
|
||||||
|
|
||||||
@@ -157,6 +167,44 @@ def determine_search_strategy(query):
|
|||||||
return ('name_only', query)
|
return ('name_only', query)
|
||||||
|
|
||||||
|
|
||||||
|
def build_customer_search_query(query, strategy, search_value):
|
||||||
|
"""
|
||||||
|
Строит Q-объект для поиска клиентов на основе стратегии.
|
||||||
|
|
||||||
|
Используется в customer_list() и api_search_customers() для единообразия.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
query: Исходный поисковый запрос (для fallback)
|
||||||
|
strategy: Стратегия поиска (из determine_search_strategy)
|
||||||
|
search_value: Значение для поиска (из determine_search_strategy)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Q-объект для фильтрации Customer.objects
|
||||||
|
"""
|
||||||
|
if strategy == 'name_only':
|
||||||
|
return Q(name__icontains=search_value)
|
||||||
|
|
||||||
|
elif strategy == 'email_prefix':
|
||||||
|
# Query вида "team_x3m@" — ищем email, начинающиеся с "team_x3m"
|
||||||
|
return Q(email__istartswith=search_value)
|
||||||
|
|
||||||
|
elif strategy == 'email_domain':
|
||||||
|
# Query вида "@bk" — ищем все email с доменом "@bk.*"
|
||||||
|
return Q(email__icontains=f'@{search_value}')
|
||||||
|
|
||||||
|
elif strategy == 'email_full':
|
||||||
|
# Query вида "test@bk.ru" — полный поиск по email
|
||||||
|
return Q(email__icontains=search_value)
|
||||||
|
|
||||||
|
elif strategy == 'universal':
|
||||||
|
# Query вида "natul" (3+ символов) — ищем везде
|
||||||
|
return Q(name__icontains=search_value) | Q(email__icontains=search_value)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# На случай неизвестной стратегии (не должно быть)
|
||||||
|
return Q(name__icontains=query)
|
||||||
|
|
||||||
|
|
||||||
@require_http_methods(["GET"])
|
@require_http_methods(["GET"])
|
||||||
def api_search_customers(request):
|
def api_search_customers(request):
|
||||||
"""
|
"""
|
||||||
@@ -203,31 +251,8 @@ def api_search_customers(request):
|
|||||||
# Определяем стратегию поиска на основе содержимого query
|
# Определяем стратегию поиска на основе содержимого query
|
||||||
strategy, search_value = determine_search_strategy(query)
|
strategy, search_value = determine_search_strategy(query)
|
||||||
|
|
||||||
# Строим Q-объект в зависимости от стратегии
|
# Строим Q-объект для поиска (единая функция, используется везде)
|
||||||
if strategy == 'name_only':
|
q_objects = build_customer_search_query(query, strategy, search_value)
|
||||||
# Поиск только по имени (для коротких запросов)
|
|
||||||
q_objects = Q(name__icontains=search_value)
|
|
||||||
|
|
||||||
elif strategy == 'email_prefix':
|
|
||||||
# Query вида "team_x3m@" — ищем email, начинающиеся с "team_x3m"
|
|
||||||
# Это решает проблему: не найдёт "natulj@bk.ru"
|
|
||||||
q_objects = Q(email__istartswith=search_value)
|
|
||||||
|
|
||||||
elif strategy == 'email_domain':
|
|
||||||
# Query вида "@bk" — ищем все email с доменом "@bk.*"
|
|
||||||
q_objects = Q(email__icontains=f'@{search_value}')
|
|
||||||
|
|
||||||
elif strategy == 'email_full':
|
|
||||||
# Query вида "test@bk.ru" — полный поиск по email
|
|
||||||
q_objects = Q(email__icontains=search_value)
|
|
||||||
|
|
||||||
elif strategy == 'universal':
|
|
||||||
# Query вида "natul" (3+ символов) — ищем везде
|
|
||||||
q_objects = Q(name__icontains=search_value) | Q(email__icontains=search_value)
|
|
||||||
|
|
||||||
else:
|
|
||||||
# На случай неизвестной стратегии (не должно быть)
|
|
||||||
q_objects = Q(name__icontains=query)
|
|
||||||
|
|
||||||
# Для телефона ищем по нормализованному номеру и по цифрам
|
# Для телефона ищем по нормализованному номеру и по цифрам
|
||||||
if phone_normalized:
|
if phone_normalized:
|
||||||
|
|||||||
Reference in New Issue
Block a user