Django+Ajax实现商品过滤时出现Field 'id'类型不匹配错误求助
问题场景
开发商品筛选功能,引入Ajax实现多类别卡片筛选,原本功能正常,添加数据处理和JsonResponse后,点击筛选复选框触发错误:ValueError: Field 'id' expected a number but got 'filter_data'。
错误原因
Django的URL路由匹配是从上到下依次匹配,你的urls.py中,path('<str:id>', views.product_view, name="product_view")这个路由定义在filter_data路由的前面。当Ajax请求/filter_data/时,Django会先匹配到<str:id>这个路由,把字符串filter_data作为id参数传递给product_view视图。而视图中用这个字符串去查询Product的id(整数类型字段),就会抛出类型不匹配的错误。
修复步骤
1. 调整URL路由顺序
把filter_data的路由移到<str:id>路由的前面,确保Django先匹配到明确的筛选路由,不会被通用的id路由捕获。
修改后的urls.py:
from django.urls import path, include from django.contrib.auth.views import PasswordChangeView from . import views from .forms import PasswordUpdateForm urlpatterns = [ path('', views.store, name="store"), path('cart/', views.cart, name="cart"), path('checkout/', views.checkout, name="checkout"), path('login/', views.loginPage, name="login"), path('logout/', views.logoutUser, name="logout"), path('registration/', views.registrationPage, name="registration"), path('profile/', views.profile, name="profile"), path('change_password/', PasswordChangeView.as_view(template_name='store/change_password.html', success_url='profile', form_class=PasswordUpdateForm ), name="change_password"), path('change_password/profile/', views.profile, name="profile"), path('update_item/', views.updateItem, name="update_item"), path('update_wish_item/', views.updateWishItem, name="update_wish_item"), path('process_order/', views.processOrder, name="process_order"), # 把filter_data路由移到前面 path('filter_data/', views.filter_data, name="filter_data"), path('wishlist/', views.wishlist, name="wishlist"), path('search/', views.search, name="search"), # 通用id路由放在最后 path('<str:id>', views.product_view, name="product_view"), path('search/<str:id>',views.product_view, name="product_view"), ]
2. 优化product_view的路由参数(可选但推荐)
因为Product的id是整数类型,把路由参数改成<int:id>,这样只有数字字符串会被匹配,避免类似的字符串参数错误:
修改路由:
path('<int:id>', views.product_view, name="product_view"), path('search/<int:id>',views.product_view, name="product_view"),
同时修改views.py中的product_view函数参数类型:
def product_view(request, id: int): product = Product.objects.filter(id=id).first() # 其余代码不变
3. 完善filter_data视图的筛选逻辑
当前filter_data只是返回固定字符串,需要实现实际的筛选逻辑,根据Ajax传递的筛选参数过滤商品:
修改views.py中的filter_data:
from django.shortcuts import render from django.http import JsonResponse def filter_data(request): # 获取Ajax传递的筛选参数 filters = request.GET.dict() # 构建查询条件 query = Product.objects.all() # 处理颜色筛选 if 'color' in filters and filters['color']: query = query.filter(color_id__in=filters['color'].split(',')) # 处理尺寸筛选 if 'size' in filters and filters['size']: query = query.filter(size_id__in=filters['size'].split(',')) # 处理品牌筛选 if 'brand' in filters and filters['brand']: query = query.filter(brand_id__in=filters['brand'].split(',')) # 处理性别筛选 if 'gender' in filters and filters['gender']: query = query.filter(gender__in=filters['gender'].split(',')) # 把查询结果渲染成HTML片段(需要创建对应的模板片段) html = render(request, 'store/product_card.html', {'products': query}).content.decode('utf-8') return JsonResponse({'data': html})
需要创建store/product_card.html模板,用来渲染单个商品卡片,示例:
{% for product in products %} <div class="product-card"> <img src="{{ product.imageURL }}" alt="{{ product.name }}"> <h3>{{ product.name }}</h3> <p>价格:{{ product.price }}</p> <a href="{{ product.id }}">查看详情</a> </div> {% endfor %}
验证修复
修改完成后,重启Django服务,点击筛选复选框,Ajax请求会正确路由到filter_data视图,返回筛选后的商品HTML片段,替换页面中的#filteredProducts内容,不会再触发id类型错误。
内容的提问来源于stack exchange,提问作者Poor Cake

