From 77ebb0e3a872d7bddde6639221add8d06a7e981e Mon Sep 17 00:00:00 2001 From: Kirill Date: Thu, 23 Apr 2026 16:53:38 +0300 Subject: [PATCH] fix(api): fix err with filters --- api/product.py | 49 ++++++++++------- db/handlers/product_handler.py | 98 +++++++++++++--------------------- 2 files changed, 67 insertions(+), 80 deletions(-) diff --git a/api/product.py b/api/product.py index 49277b7..8df58f8 100644 --- a/api/product.py +++ b/api/product.py @@ -2,9 +2,19 @@ from flask import jsonify, request from db.handlers.product_handler import get_products_with_filters, get_product_by_id from flask import Blueprint -# Создаем blueprint для API api_product = Blueprint('api_product', __name__, url_prefix='/products') +def clean_filter_value(value): + """Очищает значение фильтра от мусора. Возвращает очищенное значение или None, если оно пустое.""" + if value is None: + return None + if isinstance(value, str): + cleaned = value.strip().rstrip('/') + if not cleaned: + return None + return cleaned + return value + @api_product.route('/', methods=['GET'], strict_slashes=False) def get_products(): try: @@ -20,14 +30,14 @@ def get_products(): if limit < 1 or limit > 100: limit = 9 - # Словарь для маппинга внешних ключей → внутренние имена в БД + # ✅ ИСПРАВЛЕННЫЙ МАППИНГ filter_mapping = { 'attr_1': 'diameter', 'attr_2': 'length', - 'attr_3': 'wall_thickness', + 'attr_3': 'wall_thickness', # (опционально) тоже толщина 'attr_4': 'color', - 'attr_5': 'application_type', - 'attr_6': 'stiffness_class', + 'attr_5': 'stiffness_class', # SN2, SN4 + 'attr_6': 'wall_thickness', # <- теперь attr_6 ищет толщину стенки 'attr_7': 'angle', 'attr_8': 'fitting_type', } @@ -39,14 +49,24 @@ def get_products(): if key in excluded_params: continue - # Преобразуем ключ, если он есть в маппинге, иначе оставляем как есть real_key = filter_mapping.get(key, key) - # Разделяем значения по запятой if ',' in value: - filters[real_key] = value.split(',') + parts = value.split(',') + cleaned_parts = [] + for part in parts: + cleaned = clean_filter_value(part) + if cleaned is not None: + cleaned_parts.append(cleaned) + if cleaned_parts: + filters[real_key] = cleaned_parts else: - filters[real_key] = value + cleaned = clean_filter_value(value) + if cleaned is not None: + filters[real_key] = cleaned + + # Отладочный вывод (убедитесь, что здесь видно правильный ключ) + print("=== FINAL FILTERS ===", filters) result = get_products_with_filters( category_id=category_id, @@ -60,12 +80,9 @@ def get_products(): except Exception as e: return jsonify({"error": str(e)}), 500 + @api_product.route('/', methods=['GET'], strict_slashes=False) def get_product(product_id): - """ - Получение товара по ID - GET /api/products/{id} - """ try: product = get_product_by_id(product_id) if product: @@ -74,17 +91,13 @@ def get_product(product_id): except Exception as e: return jsonify({"error": str(e)}), 500 + @api_product.route('/fetchById', methods=['GET'], strict_slashes=False) def get_product_by_id_param(): - """ - Альтернативный маршрут для получения товара через query параметр - GET /api/products/fetchById?id=101 - """ try: product_id = request.args.get('id', type=int) if not product_id: return jsonify({"error": "id parameter is required"}), 400 - product = get_product_by_id(product_id) if product: return jsonify(product), 200 diff --git a/db/handlers/product_handler.py b/db/handlers/product_handler.py index 5258084..03aee71 100644 --- a/db/handlers/product_handler.py +++ b/db/handlers/product_handler.py @@ -10,12 +10,17 @@ def get_products_with_filters( filters: Dict[str, Any] = None, db_name: str = 'pipe_store.db' ) -> Dict[str, Any]: + """ + Получение товаров с фильтрацией и пагинацией. + Все фильтры (включая диаметр, длину, цвет и т.д.) ищутся в таблице product_filters. + """ + print(f"Received filters: {filters}") conn = sqlite3.connect(db_name) conn.row_factory = sqlite3.Row cursor = conn.cursor() - + try: - # Рекурсивный CTE для получения всех подкатегорий + # 1. Получаем все ID категорий (включая подкатегории) hierarchy_query = """ WITH RECURSIVE category_hierarchy AS ( SELECT id FROM categories WHERE id = ? @@ -27,7 +32,7 @@ def get_products_with_filters( """ cursor.execute(hierarchy_query, (category_id,)) category_ids = [row['id'] for row in cursor.fetchall()] - + if not category_ids: return {"items": [], "total": 0, "page": page, "limit": limit, "total_pages": 0} @@ -35,71 +40,43 @@ def get_products_with_filters( base_where = f"WHERE p.category_id IN ({placeholders_cats})" params = list(category_ids) - # Обработка фильтров + # 2. Обработка всех фильтров через product_filters if filters: - column_map = { - 'diameter': 'p.diameter', - 'length': 'p.length', - 'wall_thickness': 'p.wall_thickness', - 'color': 'p.color', - 'application_type': 'p.application_type', - 'stiffness_class': 'p.stiffness_class' - } - - def cast_value(col_name, val): - if col_name in ('diameter', 'length'): - try: - return int(val) - except: - return val - if col_name == 'wall_thickness': - try: - return float(val) - except: - return val - return val - for key, value in filters.items(): if not value: continue - if key in column_map: - col = column_map[key] - if isinstance(value, list): - casted = [cast_value(key, v) for v in value] - v_ph = ','.join(['?' for _ in casted]) - base_where += f" AND {col} IN ({v_ph})" - params.extend(casted) - else: - casted = cast_value(key, value) - base_where += f" AND {col} = ?" - params.append(casted) + # Подзапрос для проверки наличия фильтра у товара + if isinstance(value, list): + # Множественный фильтр (например, несколько диаметров) + placeholders = ','.join(['?' for _ in value]) + base_where += f""" + AND p.id IN ( + SELECT product_id FROM product_filters + WHERE filter_key = ? AND value IN ({placeholders}) + ) + """ + params.append(key) + params.extend([str(v) for v in value]) else: - # Фильтры из product_filters - if isinstance(value, list): - v_ph = ','.join(['?' for _ in value]) - base_where += f""" - AND p.id IN ( - SELECT product_id FROM product_filters - WHERE filter_key = ? AND value IN ({v_ph}) - )""" - params.append(key) - params.extend([str(v) for v in value]) - else: - base_where += f""" - AND p.id IN ( - SELECT product_id FROM product_filters - WHERE filter_key = ? AND value = ? - )""" - params.append(key) - params.append(str(value)) + # Одиночный фильтр + base_where += f""" + AND p.id IN ( + SELECT product_id FROM product_filters + WHERE filter_key = ? AND value = ? + ) + """ + params.append(key) + params.append(str(value)) - # Подсчёт общего количества + # 3. Подсчёт общего количества count_query = f"SELECT COUNT(*) as total FROM products p {base_where}" cursor.execute(count_query, params) + print("SQL QUERY:", count_query) + print("PARAMS:", params) total = cursor.fetchone()['total'] - # Пагинация + # 4. Пагинация offset = (page - 1) * limit query = f""" SELECT p.* FROM products p @@ -110,6 +87,7 @@ def get_products_with_filters( cursor.execute(query, params + [limit, offset]) rows = cursor.fetchall() + # 5. Формируем ответ items = [] for row in rows: items.append({ @@ -228,15 +206,12 @@ def get_product_images(product_id: int, main_image: Optional[str], conn: sqlite3 def check_stock(product_id: int, conn: sqlite3.Connection) -> bool: """Проверить наличие товара на складе""" # Временная логика - все товары в наличии - # В будущем можно добавить таблицу stock return True def get_pack_amount(product_id: int, diameter: int = None, conn: sqlite3.Connection = None) -> str: """ Получить количество в упаковке - В реальном проекте должно быть в базе данных """ - # Временная логика на основе диаметра if diameter: if diameter == 110: return "6-10" @@ -272,7 +247,7 @@ def generate_full_description(row: sqlite3.Row) -> str: def get_product_list(page=1, limit=100, category_id=None): """ - Получить список товаров с пагинацией. + Получить список товаров с пагинацией (без фильтров). Возвращает список словарей с полями id, full_name, description. """ conn = None @@ -281,7 +256,6 @@ def get_product_list(page=1, limit=100, category_id=None): conn = sqlite3.connect('pipe_store.db') conn.row_factory = sqlite3.Row - offset = (page - 1) * limit rows = get_products_by_page(conn, page, limit, category_id) products = []