新手求教:如何在Django中实现品牌筛选与价格升降排序?
嘿,作为Django新手能想到要做筛选排序功能,已经迈出了很棒的一步!我来一步步教你实现你要的品牌/厂商筛选+价格排序,顺便给你讲讲Django里处理这类需求的通用思路~
我们从视图层和模板层两部分来写,假设你的商品模型是Product,包含brand_name(品牌/厂商)、price(价格)这些字段。
1. 视图层处理逻辑
在你的app的views.py里写列表视图,核心是通过request.GET获取用户的筛选和排序参数,然后链式操作QuerySet:
from django.shortcuts import render from .models import Product def product_list(request): # 先拿到所有商品的基础查询集(Django的QuerySet是惰性的,不会立刻查数据库) products = Product.objects.all() # 1. 处理品牌/厂商筛选 selected_brand = request.GET.get('brand') if selected_brand: # 过滤出对应品牌的商品,这里的字段名要和你模型里的一致 products = products.filter(brand_name=selected_brand) # 2. 处理价格排序 selected_sort = request.GET.get('sort', 'price_low') # 默认按价格从低到高 # 定义排序规则的白名单,防止用户传入非法参数导致错误 sort_rules = { 'price_low': 'price', # 正序:价格从低到高 'price_high': '-price' # 倒序:价格从高到低 } # 只处理白名单内的排序选项 if selected_sort in sort_rules: products = products.order_by(sort_rules[selected_sort]) # 准备模板需要的数据:所有可选的品牌(去重)、选中的筛选/排序参数 all_brands = Product.objects.values_list('brand_name', flat=True).distinct() context = { 'products': products, 'all_brands': all_brands, 'selected_brand': selected_brand, 'selected_sort': selected_sort } return render(request, 'product_list.html', context)
2. 模板层渲染界面
在模板product_list.html里,我们要做一个筛选表单+排序选项,同时保留用户已经选中的参数(比如选了品牌后排序,不会丢失品牌筛选条件):
<!-- 筛选和排序表单 --> <form method="get"> <!-- 品牌下拉框 --> <div> <label>选择品牌:</label> <select name="brand" onchange="this.form.submit()"> <option value="">全部品牌</option> {% for brand in all_brands %} <option value="{{ brand }}" {% if brand == selected_brand %}selected{% endif %}> {{ brand }} </option> {% endfor %} </select> </div> <!-- 价格排序下拉框 --> <div> <label>价格排序:</label> <select name="sort" onchange="this.form.submit()"> <option value="price_low" {% if selected_sort == 'price_low' %}selected{% endif %}> 从低到高 </option> <option value="price_high" {% if selected_sort == 'price_high' %}selected{% endif %}> 从高到低 </option> </select> </div> </form> <!-- 商品列表展示 --> <div class="product-list"> {% for product in products %} <div class="product-item"> <h3>{{ product.name }}</h3> <p>品牌:{{ product.brand_name }}</p> <p>价格:¥{{ product.price }}</p> </div> {% empty %} <p>没有找到符合条件的商品哦~</p> {% endfor %} </div>
上面是针对你需求的具体实现,接下来讲几个通用思路,以后遇到类似需求都能用:
1. 利用QuerySet的链式调用
Django的QuerySet支持链式调用filter()、exclude()、order_by()等方法,而且是惰性执行的——直到你真正需要数据(比如模板里遍历)的时候才会去查数据库,所以不用担心多次调用会多查库,性能很友好。
2. 封装可复用的逻辑(Mixin模式)
如果你的项目里多个页面都需要筛选排序,可以写一个Mixin类,把通用逻辑抽出来,让视图继承它:
class SortFilterMixin: # 子类需要重写的配置 filter_fields = [] # 需要支持筛选的字段,比如['brand_name', 'category'] sort_mapping = {} # 排序规则映射,比如{'price_low': 'price', 'price_high': '-price'} default_sort = '' # 默认排序选项 def get_queryset(self): queryset = super().get_queryset() # 处理筛选 for field in self.filter_fields: value = self.request.GET.get(field) if value: queryset = queryset.filter(**{field: value}) # 处理排序 sort_option = self.request.GET.get('sort', self.default_sort) if sort_option in self.sort_mapping: queryset = queryset.order_by(self.sort_mapping[sort_option]) return queryset def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) # 把选中的筛选/排序参数传到模板 for field in self.filter_fields: context[f'selected_{field}'] = self.request.GET.get(field) context['selected_sort'] = self.request.GET.get('sort', self.default_sort) return context
然后你的视图就可以这样写,非常简洁:
from django.views.generic import ListView from .models import Product from .mixins import SortFilterMixin class ProductListView(SortFilterMixin, ListView): model = Product template_name = 'product_list.html' filter_fields = ['brand_name'] sort_mapping = {'price_low': 'price', 'price_high': '-price'} default_sort = 'price_low'
3. 永远做参数合法性校验
绝对不要直接把用户传入的参数放到filter()或order_by()里,比如不要写products.order_by(request.GET.get('sort'))——如果用户传入恶意参数,可能会导致SQL注入或者程序报错。一定要像前面那样用白名单映射,只处理你预先定义好的参数。
4. 复杂筛选用表单类验证
如果筛选条件比较复杂(比如价格范围、多条件组合),可以用Django的Form类来处理参数验证,这样更规范,也更容易处理错误提示:
from django import forms class ProductFilterForm(forms.Form): brand = forms.CharField(required=False) min_price = forms.DecimalField(required=False, min_value=0) max_price = forms.DecimalField(required=False, min_value=0) sort = forms.ChoiceField( choices=[('price_low', '从低到高'), ('price_high', '从高到低')], required=False, initial='price_low' )
然后在视图里用这个表单:
def product_list(request): form = ProductFilterForm(request.GET) products = Product.objects.all() if form.is_valid(): cleaned_data = form.cleaned_data # 处理品牌筛选 if cleaned_data['brand']: products = products.filter(brand_name=cleaned_data['brand']) # 处理价格范围 if cleaned_data['min_price']: products = products.filter(price__gte=cleaned_data['min_price']) if cleaned_data['max_price']: products = products.filter(price__lte=cleaned_data['max_price']) # 处理排序 sort_option = cleaned_data['sort'] or 'price_low' sort_rules = {'price_low': 'price', 'price_high': '-price'} products = products.order_by(sort_rules[sort_option]) context = {'products': products, 'form': form} return render(request, 'product_list.html', context)
模板里直接渲染表单就行,Django会自动保留用户的选中值:
<form method="get"> {{ form.as_p }} <button type="submit">筛选</button> </form>
5. 配合分页使用
如果你的商品数据很多,记得加上分页功能,Django的Paginator可以轻松实现,而且筛选排序后的分页也完全兼容,只需要在视图里把products换成分页后的对象就行。
内容的提问来源于stack exchange,提问作者Dennis Gathagu

