You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django电商网站过滤后商品无法在模板渲染的问题

问题描述

开发电商网站筛选功能时,通过JSON获取筛选参数,在views.py中过滤商品。控制台能正常输出过滤后的商品结果且无报错,但模板无法渲染显示这些商品。

相关代码

views.py

def Products(request):
    data = cartData(request)

    cartItems = data['cartItems']
    order = data['order']
    items = data['items']
    products = Product.objects.all()
    gender = Gender.objects.all()
    categories = Category.objects.all()
    product_types = ProductType.objects.all()
    try:

        data = json.loads(request.body)

        productTId = data['productTId']
        productVId = data['productVId']
        genderId = data['genderId']
        
        genderId = get_object_or_404(Gender, id=genderId)
        productVId = get_object_or_404(Category, id=productVId)
        productTId = get_object_or_404(ProductType, id=productTId)
        
        filtered_products = Product.objects.filter(typeP=productTId).filter(category=productVId).filter(Gender=genderId)
        countFilter=filtered_products.count()
        print(filtered_products)
        print(countFilter)
        return JsonResponse(filtered_products, safe = False)
    except:
        None

    try:
        
        context = {'countFilter':countFilter,'filtered_products':filtered_products,'gender':gender,'categories':categories,'product_types':product_types,'products':products, 'cartItems':cartItems, 'order':order, 'items':items}
    except:
        context = {'gender':gender,'categories':categories,'product_types':product_types,'products':products, 'cartItems':cartItems, 'order':order, 'items':items}
    return render(request, 'product.html', context)

HTML模板

{% for product in filtered_products %}
            <div class="card">
    <div class="card__img">
        <picture><source srcset="{{product.imageURL}}" type="image/webp"><img src="{{product.imageURL}}" alt=""></picture>
    </div>
    <h6 class="card__title">{{product.name}}</h6>
    <div class="card__descr">{{product.description}}...
        <div class="card__more" onclick="More('{{product.id}}')">подробнее...</div>
    </div>
    <div class="card__func">
        <a class="btn-hover card__button-buy update-cart" data-product="{{product.id}}" data-action="add">В КОРЗИНУ</a>
        <div class="card__right">
            <div class="card__price" data-product="{{product.id}}">{{product.price}} р</div>
            <div class="card__changed-price card__changed-price--card">
                <div class="card__changed-btn card__changed-btn--prev card__changed-btn--max increase" data-product="{{product.id}}" data-action="minus"><svg width="8" height="2" viewBox="0 0 8 2" fill="none" xmlns="http://www.w3.org/2000/svg">
                        <path d="M1 1L7 1" stroke="#7ABC51" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
                    </svg>
                </div>
                <p class="card__changed-thing" id="quantity" class="quantity" data-product="{{product.id}}">1</p>
                <div  class="card__changed-btn card__changed-btn--next increase" data-product="{{product.id}}" data-action="plus"><svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
                        <path fill-rule="evenodd" clip-rule="evenodd" d="M3 7C3 7.41421 3.33579 7.75 3.75 7.75C4.16421 7.75 4.5 7.41421 4.5 7V4.75H6.75C7.16421 4.75 7.5 4.41421 7.5 4C7.5 3.58579 7.16421 3.25 6.75 3.25H4.5V1C4.5 0.585786 4.16421 0.25 3.75 0.25C3.33579 0.25 3 0.585786 3 1V3.25H0.75C0.335786 3.25 0 3.58579 0 4C0 4.41421 0.335786 4.75 0.75 4.75H3V7Z" fill="#7ABC51"/>
                    </svg>
                </div>
            </div>
        </div>
    </div>
</div>
{% endfor %}

核心问题分析

  1. 提前终止视图执行:当成功解析JSON参数后,直接返回JsonResponse,视图函数会立即停止,不会执行后续渲染模板的代码。前端拿到JSON数据,但没有对应的JS逻辑更新DOM,所以看不到商品。
  2. 异常处理过于宽泛:except: None会捕获所有异常,包括变量未定义的情况,导致后续try块引用filtered_products和countFilter时触发异常,进入默认context,模板中没有filtered_products变量,循环不执行。
  3. 请求逻辑混淆:如果是AJAX请求,需要前端配合更新页面;如果是普通表单提交,不该用json.loads(request.body)获取参数。

解决方案

方案1:适配AJAX请求(推荐)

如果前端通过AJAX发送筛选请求,需要视图返回结构化JSON,再由JS动态更新页面。

修改后的views.py

def Products(request):
    data = cartData(request)
    cartItems = data['cartItems']
    order = data['order']
    items = data['items']
    products = Product.objects.all()
    gender = Gender.objects.all()
    categories = Category.objects.all()
    product_types = ProductType.objects.all()

    # 识别AJAX请求
    if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
        try:
            data = json.loads(request.body)
            productTId = data['productTId']
            productVId = data['productVId']
            genderId = data['genderId']
            
            gender_obj = get_object_or_404(Gender, id=genderId)
            category_obj = get_object_or_404(Category, id=productVId)
            type_obj = get_object_or_404(ProductType, id=productTId)
            
            filtered_products = Product.objects.filter(typeP=type_obj, category=category_obj, Gender=gender_obj)
            countFilter = filtered_products.count()
            
            # 序列化商品数据为JSON可解析格式
            product_list = list(filtered_products.values('id', 'name', 'description', 'price', 'imageURL'))
            return JsonResponse({
                'countFilter': countFilter,
                'filtered_products': product_list
            })
        except Exception as e:
            print(f"AJAX处理异常: {e}")
            return JsonResponse({'error': str(e)}, status=400)

    # 非AJAX请求,渲染初始页面
    context = {
        'gender': gender,
        'categories': categories,
        'product_types': product_types,
        'products': products,
        'cartItems': cartItems,
        'order': order,
        'items': items
    }
    return render(request, 'product.html', context)

前端添加JS逻辑

在模板中加入以下代码,处理AJAX响应并更新DOM:

// 绑定筛选表单提交事件
document.getElementById('filter-form').addEventListener('submit', function(e) {
    e.preventDefault();
    const productTId = document.getElementById('product-type').value;
    const productVId = document.getElementById('category').value;
    const genderId = document.getElementById('gender').value;
    
    fetch('/products/', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'X-CSRFToken': getCookie('csrftoken')
        },
        body: JSON.stringify({
            productTId: productTId,
            productVId: productVId,
            genderId: genderId
        })
    })
    .then(response => response.json())
    .then(data => {
        const productContainer = document.getElementById('product-container');
        productContainer.innerHTML = '';
        // 动态生成商品卡片
        data.filtered_products.forEach(product => {
            const card = `
                <div class="card">
                    <div class="card__img">
                        <picture><source srcset="${product.imageURL}" type="image/webp"><img src="${product.imageURL}" alt=""></picture>
                    </div>
                    <h6 class="card__title">${product.name}</h6>
                    <div class="card__descr">${product.description}...
                        <div class="card__more" onclick="More('${product.id}')">подробнее...</div>
                    </div>
                    <div class="card__func">
                        <a class="btn-hover card__button-buy update-cart" data-product="${product.id}" data-action="add">В КОРЗИНУ</a>
                        <div class="card__right">
                            <div class="card__price" data-product="${product.id}">${product.price} р</div>
                            <div class="card__changed-price card__changed-price--card">
                                <div class="card__changed-btn card__changed-btn--prev card__changed-btn--max increase" data-product="${product.id}" data-action="minus">
                                    <svg width="8" height="2" viewBox="0 0 8 2" fill="none" xmlns="http://www.w3.org/2000/svg">
                                        <path d="M1 1L7 1" stroke="#7ABC51" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
                                    </svg>
                                </div>
                                <p class="card__changed-thing quantity" data-product="${product.id}">1</p>
                                <div class="card__changed-btn card__changed-btn--next increase" data-product="${product.id}" data-action="plus">
                                    <svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
                                        <path fill-rule="evenodd" clip-rule="evenodd" d="M3 7C3 7.41421 3.33579 7.75 3.75 7.75C4.16421 7.75 4.5 7.41421 4.5 7V4.75H6.75C7.16421 4.75 7.5 4.41421 7.5 4C7.5 3.58579 7.16421 3.25 6.75 3.25H4.5V1C4.5 0.585786 4.16421 0.25 3.75 0.25C3.33579 0.25 3 0.585786 3 1V3.25H0.75C0.335786 3.25 0 3.58579 0 4C0 4.41421 0.335786 4.75 0.75 4.75H3V7Z" fill="#7ABC51"/>
                                    </svg>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            `;
            productContainer.innerHTML += card;
        });
    })
    .catch(error => console.error('筛选请求失败:', error));
});

// 获取CSRF令牌辅助函数
function getCookie(name) {
    let cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        const cookies = document.cookie.split(';');
        for (let i = 0; i < cookies.length; i++) {
            const cookie = cookies[i].trim();
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}

方案2:普通表单提交(页面刷新)

如果不需要AJAX,改用普通表单提交参数,视图直接渲染过滤后的页面。

修改后的views.py

def Products(request):
    data = cartData(request)
    cartItems = data['cartItems']
    order = data['order']
    items = data['items']
    products = Product.objects.all()
    gender = Gender.objects.all()
    categories = Category.objects.all()
    product_types = ProductType.objects.all()
    
    filtered_products = products
    countFilter = 0

    # 从GET参数中获取筛选条件
    productTId = request.GET.get('productTId')
    productVId = request.GET.get('productVId')
    genderId = request.GET.get('genderId')
    
    if productTId and productVId and genderId:
        try:
            gender_obj = get_object_or_404(Gender, id=genderId)
            category_obj = get_object_or_404(Category, id=productVId)
            type_obj = get_object_or_404(ProductType, id=productTId)
            
            filtered_products = Product.objects.filter(typeP=type_obj, category=category_obj, Gender=gender_obj)
            countFilter = filtered_products.count()
        except Exception as e:
            print(f"筛选异常: {e}")

    context = {
        'countFilter': countFilter,
        'filtered_products': filtered_products,
        'gender': gender,
        'categories': categories,
        'product_types': product_types,
        'products': products,
        'cartItems': cartItems,
        'order': order,
        'items': items
    }
    return render(request, 'product.html', context)

修改前端表单

<form method="get" action="/products/">
    <select name="productTId" id="product-type">
        {% for type in product_types %}
            <option value="{{ type.id }}">{{ type.name }}</option>
        {% endfor %}
    </select>
    <select name="productVId" id="category">
        {% for cat in categories %}
            <option value="{{ cat.id }}">{{ cat.name }}</option>
        {% endfor %}
    </select>
    <select name="genderId" id="gender">
        {% for g in gender %}
            <option value="{{ g.id }}">{{ g.name }}</option>
        {% endfor %}
    </select>
    <button type="submit">筛选</button>
</form>

<div id="product-container">
    {% for product in filtered_products %}
        <!-- 商品卡片内容保持不变 -->
        <div class="card">...</div>
    {% endfor %}
</div>

内容的提问来源于stack exchange,提问作者Behzod Urinov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 23:01:12