# Products API 文档 ## 概述 Products API 提供轻量级的产品查询接口,专门为前端下拉框和自动完成功能设计。 **特点**: - 只返回 `id` 和 `name` 字段,减少数据传输 - 支持分页(LimitOffset) - 支持按名称模糊搜索 - 需要 JWT 认证 **基础路径**: `/api/v1/products/quick/` --- ## 认证 所有接口都需要 JWT 认证。在请求头中添加: ``` Authorization: Bearer ``` --- ## 接口详情 ### 获取产品列表 **请求** ``` GET /api/v1/products/quick/ ``` **查询参数** | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | limit | integer | 否 | 返回结果数量,不提供则返回所有 | | offset | integer | 否 | 偏移量,默认 0 | | search | string | 否 | 按产品名称模糊搜索(不区分大小写) | **请求示例** ```bash # 获取所有产品 curl -X GET "http://localhost:8000/api/v1/products/quick/" \ -H "Authorization: Bearer YOUR_TOKEN" # 分页获取(前10条) curl -X GET "http://localhost:8000/api/v1/products/quick/?limit=10&offset=0" \ -H "Authorization: Bearer YOUR_TOKEN" # 按名称搜索 curl -X GET "http://localhost:8000/api/v1/products/quick/?search=布料" \ -H "Authorization: Bearer YOUR_TOKEN" # 搜索 + 分页 curl -X GET "http://localhost:8000/api/v1/products/quick/?search=布料&limit=5&offset=0" \ -H "Authorization: Bearer YOUR_TOKEN" ``` **成功响应 - 不分页** (200 OK) ```json { "count": 5, "results": [ { "id": 1, "name": "纯棉布料" }, { "id": 2, "name": "涤纶布料" }, { "id": 3, "name": "丝绸布料" }, { "id": 4, "name": "麻布" }, { "id": 5, "name": "牛仔布" } ] } ``` **成功响应 - 带分页** (200 OK) ```json { "count": 50, "next": "http://localhost:8000/api/v1/products/quick/?limit=10&offset=10", "previous": null, "results": [ { "id": 1, "name": "纯棉布料" }, { "id": 2, "name": "涤纶布料" } ] } ``` **错误响应** | 状态码 | 说明 | |--------|------| | 401 Unauthorized | 未认证或 Token 无效 | | 403 Forbidden | 无权限访问 | ```json { "detail": "Authentication credentials were not provided." } ``` --- ## 使用场景 ### 1. 前端下拉框 ```javascript // React 示例 import { useState, useEffect } from 'react'; function ProductSelect() { const [products, setProducts] = useState([]); const [loading, setLoading] = useState(false); useEffect(() => { const fetchProducts = async () => { setLoading(true); const response = await fetch('/api/v1/products/quick/?limit=100', { headers: { 'Authorization': `Bearer ${token}` } }); const data = await response.json(); setProducts(data.results); setLoading(false); }; fetchProducts(); }, []); return ( ); } ``` ### 2. 自动完成(Autocomplete) ```javascript // Vue 3 + Element Plus 示例 ``` ### 3. 搜索框实时搜索 ```javascript // Vanilla JavaScript 示例 const searchInput = document.getElementById('product-search'); const resultsContainer = document.getElementById('search-results'); let debounceTimer; searchInput.addEventListener('input', (e) => { clearTimeout(debounceTimer); debounceTimer = setTimeout(async () => { const searchText = e.target.value.trim(); if (searchText.length < 2) { resultsContainer.innerHTML = ''; return; } const response = await fetch( `/api/v1/products/quick/?search=${encodeURIComponent(searchText)}&limit=10`, { headers: { 'Authorization': `Bearer ${token}` } } ); const data = await response.json(); // 渲染搜索结果 resultsContainer.innerHTML = data.results.map(product => `
${product.name}
`).join(''); }, 300); // 300ms 防抖 }); ``` --- ## Python 使用示例 ### 基础查询 ```python import requests BASE_URL = "http://localhost:8000" TOKEN = "your_jwt_token" headers = {"Authorization": f"Bearer {TOKEN}"} # 获取所有产品 response = requests.get(f"{BASE_URL}/api/v1/products/quick/", headers=headers) products = response.json() print(f"共有 {products['count']} 个产品:") for product in products['results']: print(f" - {product['id']}: {product['name']}") ``` ### 分页查询 ```python def get_all_products(token): """获取所有产品(自动处理分页)""" BASE_URL = "http://localhost:8000" headers = {"Authorization": f"Bearer {token}"} all_products = [] offset = 0 limit = 50 while True: response = requests.get( f"{BASE_URL}/api/v1/products/quick/?limit={limit}&offset={offset}", headers=headers ) data = response.json() all_products.extend(data['results']) if data['next'] is None: break offset += limit return all_products # 使用 products = get_all_products(token) print(f"总共获取了 {len(products)} 个产品") ``` ### 搜索查询 ```python def search_products(token, query, limit=10): """搜索产品""" BASE_URL = "http://localhost:8000" headers = {"Authorization": f"Bearer {token}"} response = requests.get( f"{BASE_URL}/api/v1/products/quick/?search={query}&limit={limit}", headers=headers ) return response.json()['results'] # 使用 results = search_products(token, "布料", limit=5) for product in results: print(f"{product['id']}: {product['name']}") ``` --- ## 性能优化建议 ### 前端缓存 ```javascript // 使用本地缓存避免重复请求 class ProductCache { constructor() { this.cache = new Map(); this.cacheTimeout = 5 * 60 * 1000; // 5分钟 } async getProducts(search = '', limit = 100) { const cacheKey = `${search}:${limit}`; const cached = this.cache.get(cacheKey); if (cached && Date.now() - cached.timestamp < this.cacheTimeout) { return cached.data; } const url = search ? `/api/v1/products/quick/?search=${encodeURIComponent(search)}&limit=${limit}` : `/api/v1/products/quick/?limit=${limit}`; const response = await fetch(url, { headers: { 'Authorization': `Bearer ${token}` } }); const data = await response.json(); this.cache.set(cacheKey, { data: data.results, timestamp: Date.now() }); return data.results; } clear() { this.cache.clear(); } } const productCache = new ProductCache(); ``` ### 防抖搜索 ```javascript // 防抖函数避免频繁请求 function debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } const searchProducts = debounce(async (query) => { const response = await fetch( `/api/v1/products/quick/?search=${encodeURIComponent(query)}&limit=10`, { headers: { 'Authorization': `Bearer ${token}` } } ); const data = await response.json(); updateSearchResults(data.results); }, 300); ``` --- ## 常见问题 ### Q: 为什么只返回 id 和 name? **A**: 这是一个轻量级接口,专门为下拉框和自动完成设计。只返回必要的字段可以: - 减少数据传输量 - 提高响应速度 - 降低服务器负载 如果需要完整的产品信息,请使用完整的产品 API。 ### Q: 搜索是否区分大小写? **A**: 不区分。搜索使用 `icontains` 查询,对大小写不敏感。 ### Q: 最多能返回多少条数据? **A**: 建议使用 `limit` 参数限制返回数量。对于下拉框,建议 `limit=100`;对于自动完成,建议 `limit=10`。 ### Q: 如何实现"加载更多"功能? **A**: 使用 `offset` 参数进行分页。每次增加 `offset` 的值来获取下一页数据。 ```javascript let offset = 0; const limit = 20; async function loadMore() { const response = await fetch( `/api/v1/products/quick/?limit=${limit}&offset=${offset}`, { headers: { 'Authorization': `Bearer ${token}` } } ); const data = await response.json(); appendProducts(data.results); offset += limit; // 检查是否还有更多数据 return data.next !== null; } ``` --- ## 测试 完整的测试套件位于 `api_v1/views/products/test_products_api.py`。 运行测试: ```bash # 运行所有产品 API 测试 python manage.py test api_v1.views.products # 运行特定测试 python manage.py test api_v1.views.products.test_products_api.ProductQuickAPITestCase.test_search_products_by_name ``` 测试覆盖: - ✅ 基础列表查询(带/不带分页) - ✅ 名称模糊搜索 - ✅ 搜索 + 分页组合 - ✅ 大小写不敏感搜索 - ✅ 空结果处理 - ✅ 认证检查 - ✅ 响应格式验证 - ✅ 边界情况测试 --- ## 更新日志 ### 2025-11-18 - 初始版本发布 - 支持产品列表查询(仅 id 和 name) - 支持分页(LimitOffset) - 支持按名称模糊搜索 - 完整的测试覆盖 --- ## 相关链接 - [Basic Info Models](../../basic_info/models.py) - Product 模型定义 - [API 路由配置](../../urls.py) - [测试文件](./test_products_api.py)