Django新手求助:如何用.filter按分类展示商品?
Django按分类展示商品实现方案(带详细解释)
原代码存在的核心问题
- Product模型中外键字段名
Category不符合Python命名规范(应小写),易导致查询逻辑出错 - Category和Product的slug字段使用
CharField不够严谨,缺乏URL友好性与唯一性约束 - 缺失
categoryslug视图实现,这是按分类过滤商品的核心逻辑 - 模型中
__str__方法缩进错误,未包含在类内部
一、修正后的Models代码
from django.db import models class Category(models.Model): name = models.CharField(max_length=200) # 使用SlugField存储URL友好字符串,unique=True避免分类slug重复 slug = models.SlugField(max_length=200, unique=True) def __str__(self): return self.name class Product(models.Model): # 外键字段名改为小写category,符合PEP8规范,related_name方便反向查询商品 category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='products') name = models.CharField(max_length=200, null=False, blank=False) slug = models.SlugField(max_length=200, unique=True, null=False, blank=False) description = models.TextField(max_length=350, null=False, blank=False) # 指定图片存储路径,避免媒体文件混乱 image = models.ImageField(null=False, blank=False, upload_to='product_images/') quantity = models.IntegerField(null=False, blank=False) def __str__(self): return self.name
关键解释:
SlugField:自动去除特殊字符、转为小写,确保URL路径合法;unique=True避免不同分类/商品出现相同slug导致的URL冲突related_name='products':允许通过category.products.all()直接获取该分类下的所有商品,简化查询逻辑upload_to:将商品图片统一存储到media/product_images/目录,需配合Django媒体文件配置使用
二、修正后的URLs代码
from django.urls import path from . import views urlpatterns = [ path('', views.home, name="home"), # 路径改为语义化的product/,便于理解维护 path('product/<str:slug>/', views.product_detail, name="product_detail"), path('categories/', views.category_list, name="category_list"), path('category/<str:slug>/', views.category_products, name="category_products"), ]
关键解释:
- 重命名视图与URL名称,语义更清晰:比如
fd改为product_detail,categoryslug改为category_products - URL末尾添加
/:符合Django规范,避免出现category/slug和category/slug/两个重复路径
三、修正后的Views代码
from django.shortcuts import render, get_object_or_404 from .models import Product, Category def home(request): context = { 'products': Product.objects.all(), } return render(request, 'pages/home.html', context) def product_detail(request, slug): # 用get_object_or_404替代get(),商品不存在时返回404页面,避免报错 product = get_object_or_404(Product, slug=slug) context = { 'product': product, } return render(request, 'pages/product.html', context) def category_list(request): context = { 'categories': Category.objects.all(), } return render(request, 'pages/category.html', context) def category_products(request, slug): # 获取指定slug的分类,不存在则返回404 category = get_object_or_404(Category, slug=slug) # 两种方式过滤该分类下的商品,任选其一即可 # 方式1:正向过滤 products = Product.objects.filter(category=category) # 方式2:利用related_name反向查询(更简洁) # products = category.products.all() context = { 'category': category, 'products': products, } return render(request, 'pages/category_products.html', context)
关键解释:
get_object_or_404:比objects.get()更健壮,查询不到对象时自动返回404错误,避免抛出DoesNotExist异常category_products视图核心逻辑:通过分类slug定位分类对象,再用filter筛选该分类下的所有商品,两种查询方式均有效
四、模板示例(category_products.html)
<h1>{{ category.name }}</h1> <div class="product-list"> {% for product in products %} <div class="product-item"> <img src="{{ product.image.url }}" alt="{{ product.name }}"> <h3><a href="{% url 'product_detail' product.slug %}">{{ product.name }}</a></h3> <p>{{ product.description }}</p> <p>库存:{{ product.quantity }}</p> </div> {% empty %} <p>该分类下暂无商品</p> {% endfor %} </div>
关键解释:
- 用
{{ category.name }}展示当前分类名称 {% for %}循环遍历分类商品,点击商品名称可跳转到详情页{% empty %}处理分类无商品的边界情况,给出友好提示
后续必要操作
- 数据库迁移:修改模型后执行以下命令更新数据库:
python manage.py makemigrations python manage.py migrate
- 媒体文件配置:在项目
settings.py中添加:
MEDIA_URL = '/media/' MEDIA_ROOT = BASE_DIR / 'media'
并在项目根URL中添加媒体文件路由:
from django.conf import settings from django.conf.urls.static import static urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
内容的提问来源于stack exchange,提问作者dadi game
相关产品推荐
相关产品推荐

