news 2026/5/23 23:28:00

Django 从 0 到 1 打造完整电商平台:商品列表页实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Django 从 0 到 1 打造完整电商平台:商品列表页实现

IT策士 10余年一线大厂经验,专注 IT 思维、架构、职场进阶。我会在公众号、今日头条持续发布最新文章,助你少走弯路。


上一篇我们搞定了商品分类树和 SPU 详情页的规格切换,商品模块的骨架已经撑起来了。但一个商城不能只靠详情页活着——用户得有地方“逛”,能看到一页一页的商品,像刷货架一样。今天我们就来实现电商的商品列表页,并且加入经典的分页功能。

需求很明确:所有上架的商品(SKU)以网格形式展示,用户可以通过分类链接进入某个分类下的商品列表,每页展示固定数量,底部有分页导航。全程使用 Django 内置的Paginator,无需第三方库,简单可靠。


一、需求分析

商品列表页的核心功能:

  1. 默认展示所有上架 SKU,按创建时间倒序排列。

  2. 支持按分类筛选:通过 URL 查询参数?category_id=1过滤该分类及其子分类下的商品。

  3. 分页展示:每页 12 个商品,底部生成 Bootstrap 风格的分页导航。

  4. 商品卡片:显示主图(或占位图)、名称、价格、销量等信息,点击跳转到 SPU 详情页。

  5. 排序暂时留到第 15 篇,这里保持简单。


二、视图实现

我们将使用 Django 的ListView来快速构建,但为了更灵活地处理分类筛选(包含子分类),还是采用函数视图 +Paginator手写。

编辑apps/products/views.py,在已有内容基础上追加:

from django.core.paginatorimportPaginator, EmptyPage, PageNotAnInteger from .modelsimportSKU, Category def sku_list(request):# 获取所有上架 SKU,预加载关联数据,减少数据库查询skus=SKU.objects.filter(is_active=True).select_related('spu__category').prefetch_related('images').order_by('-create_time')# 分类筛选category_id=request.GET.get('category_id')ifcategory_id: try: category=Category.objects.get(pk=category_id,is_active=True)# 获取该分类及其所有子分类的 ID 列表category_ids=[category.id]# 简单处理两级分类:查找子分类children=Category.objects.filter(parent=category,is_active=True)category_ids.extend(children.values_list('id',flat=True))# 过滤出属于这些分类的 SPU 的 SKUskus=skus.filter(spu__category_id__in=category_ids)except Category.DoesNotExist:# 分类不存在时返回空列表skus=skus.none()# 分页:每页 12 个paginator=Paginator(skus,12)page_number=request.GET.get('page',1)try: page_obj=paginator.page(page_number)except PageNotAnInteger: page_obj=paginator.page(1)except EmptyPage: page_obj=paginator.page(paginator.num_pages)# 传递当前分类信息供模板使用current_category=Noneifcategory_id: try: current_category=Category.objects.get(pk=category_id)except Category.DoesNotExist: passreturnrender(request,'products/sku_list.html',{'page_obj':page_obj,'current_category':current_category,})

代码要点:

  • select_related('spu__category')连表查询 SPU 和分类,避免循环查询。

  • prefetch_related('images')预取图片,供模板取主图用。

  • 分类筛选时,先取出当前分类及其子分类的 ID,再通过spu__category_id__in过滤。

  • Paginatorpage()方法会自动处理页码越界,我们捕获异常并返回首页或末页。


三、URL 配置

apps/products/urls.py中添加路由:

urlpatterns=[path('categories/', views.category_tree,name='category_tree'), path('spu/<int:spu_id>/', views.spu_detail,name='spu_detail'), path('list/', views.sku_list,name='sku_list'),# 新增]

这样商品列表页的 URL 就是/products/list/,带分类参数则是/products/list/?category_id=2


四、模板设计

创建apps/products/templates/products/sku_list.html

{% extends'base.html'%}{% load static %}{% block title %}{%ifcurrent_category %}{{current_category.name}}- 商品列表{%else%}全部商品 - Django 商城{% endif %}{% endblock %}{% block content %}<divclass="d-flex justify-content-between align-items-center mb-4"><h3>{%ifcurrent_category %}📦{{current_category.name}}{%else%}🛍️ 全部商品{% endif %}</h3><spanclass="text-muted">{{page_obj.paginator.count}}件商品</span></div><!-- 分类导航(可复用上一篇的分类树链接) --><divclass="mb-3"><ahref="{% url 'products:sku_list' %}"class="btn btn-sm {% if not current_category %}btn-primary{% else %}btn-outline-primary{% endif %}">全部</a>{%forcatintop_categories %}<ahref="{% url 'products:sku_list' %}?category_id={{ cat.id }}"class="btn btn-sm {% if current_category.id == cat.id %}btn-primary{% else %}btn-outline-primary{% endif %}">{{cat.name}}</a>{% endfor %}</div><!-- 商品网格 --><divclass="row">{%forskuinpage_obj %}<divclass="col-md-3 col-sm-6 mb-4"><divclass="card h-100 shadow-sm"><ahref="{% url 'products:spu_detail' sku.spu.id %}">{% with sku.images.all|first as main_image %}{%ifmain_image %}<imgsrc="{{ main_image.image.url }}"class="card-img-top"alt="{{ sku.name }}"style="height:200px; object-fit:cover;">{%else%}<imgsrc="{% static 'images/placeholder.png' %}"class="card-img-top"alt="暂无图片"style="height:200px; object-fit:cover;">{% endif %}{% endwith %}</a><divclass="card-body d-flex flex-column"><h6class="card-title"><ahref="{% url 'products:spu_detail' sku.spu.id %}"class="text-decoration-none text-dark">{{sku.name}}</a></h6><divclass="mt-auto"><spanclass="fs-5 text-danger fw-bold">¥{{sku.price}}</span><spanclass="text-muted small ms-2">已售{{sku.sales}}</span></div></div></div></div>{% empty %}<divclass="col-12"><divclass="alert alert-info">该分类下暂无商品。</div></div>{% endfor %}</div><!-- 分页导航 -->{%ifpage_obj.has_other_pages %}<nav aria-label="商品列表分页"><ulclass="pagination justify-content-center"><!-- 上一页 -->{%ifpage_obj.has_previous %}<liclass="page-item"><aclass="page-link"href="?page={{ page_obj.previous_page_number }}{% if current_category %}&category_id={{ current_category.id }}{% endif %}">« 上一页</a></li>{%else%}<liclass="page-item disabled"><spanclass="page-link">« 上一页</span></li>{% endif %}<!-- 页码 -->{%fornuminpage_obj.paginator.page_range %}{%ifnum==page_obj.number %}<liclass="page-item active"><spanclass="page-link">{{num}}</span></li>{%elifnum>page_obj.number|add:'-3'and num<page_obj.number|add:'3'%}<liclass="page-item"><aclass="page-link"href="?page={{ num }}{% if current_category %}&category_id={{ current_category.id }}{% endif %}">{{num}}</a></li>{% endif %}{% endfor %}<!-- 下一页 -->{%ifpage_obj.has_next %}<liclass="page-item"><aclass="page-link"href="?page={{ page_obj.next_page_number }}{% if current_category %}&category_id={{ current_category.id }}{% endif %}">下一页 »</a></li>{%else%}<liclass="page-item disabled"><spanclass="page-link">下一页 »</span></li>{% endif %}</ul></nav>{% endif %}{% endblock %}

模板说明:

  • 在顶部我们使用了一个分类快捷导航条,但这需要传入top_categories变量。所以需要修改视图,把顶级分类也传给模板。

更新视图:在sku_list中添加:

top_categories=Category.objects.filter(parent__isnull=True,is_active=True).order_by('sort')

然后在return render的 context 里加上'top_categories': top_categories

这样就可以在列表页快速切换分类。


五、视图最终版本(补充 top_categories)

def sku_list(request):# ... 之前的 skus 查询和分类筛选 ...# 顶级分类,供分类导航使用top_categories=Category.objects.filter(parent__isnull=True,is_active=True).order_by('sort')# ... 分页逻辑 ...returnrender(request,'products/sku_list.html',{'page_obj':page_obj,'current_category':current_category,'top_categories':top_categories,})

六、添加更多测试数据

为了让分页效果明显,我们需要超过 12 个 SKU。可以在 Admin 中多添加几个,或者编写一个临时命令来快速填充。

apps/products/management/commands/init_product_data.py中可以追加一些商品,但更简单的是直接在dbshell中插入几条记录,不过我们保持规范,可以再创建一个命令create_test_skus.py来批量生成。这里提供快捷方式:

在项目根目录执行python manage.py shell

from products.modelsimportSPU, SKU, Category# 假设已有电子产品分类下的手机子分类phone_cat=Category.objects.get(name='手机')spu_iphone=SPU.objects.get(name='iPhone 15')spu_samsung=SPU.objects.create(name='Samsung Galaxy S24',brand='Samsung',desc='三星旗舰手机',category=phone_cat)# 为三星创建几个 SKUSKU.objects.create(spu=spu_samsung,name='Samsung Galaxy S24 256GB 黑色',specs={'颜色':'黑色','存储':'256GB'},price=6999,stock=60,is_active=True)SKU.objects.create(spu=spu_samsung,name='Samsung Galaxy S24 512GB 黑色',specs={'颜色':'黑色','存储':'512GB'},price=7999,stock=30,is_active=True)# 为 iPhone 再多加几个颜色SKU.objects.create(spu=spu_iphone,name='iPhone 15 128GB 星光色',specs={'颜色':'星光色','存储':'128GB'},price=5999,stock=80,is_active=True)SKU.objects.create(spu=spu_iphone,name='iPhone 15 256GB 星光色',specs={'颜色':'星光色','存储':'256GB'},price=6999,stock=40,is_active=True)

现在数据足够了。如果使用之前的初始化命令,可能只有 5 个 SKU,加上手动添加的,总数超过 12,分页效果就出来了。


七、测试流程与输出

启动服务器:

python manage.py runserver
7.1 全部商品列表

访问http://127.0.0.1:8000/products/list/

页面展示:

  • 顶部显示“🛍️ 全部商品”,共 N 件。

  • 分类快捷按钮:全部(高亮)、电子产品、服装。

  • 商品网格,每行 4 个卡片,显示图片、名称、价格、销量。

  • 底部有分页导航:« 上一页 1 [2] 下一页 »

终端输出:

[23/May/202610:15:00]"GET /products/list/ HTTP/1.1"2009654
7.2 按分类筛选

点击“电子产品”按钮,或访问http://127.0.0.1:8000/products/list/?category_id=1(假设电子产品分类 ID=1)。

页面只显示 iPhone 和 Samsung 的 SKU,分类按钮“电子产品”高亮。

终端输出:

[23/May/202610:16:12]"GET /products/list/?category_id=1 HTTP/1.1"2007632
7.3 分页测试

如果当前商品数量超过 12,底部出现分页。点击“下一页”,URL 变为?page=2(若带有 category_id 会保留)。商品卡片内容变化,且分页导航当前页码高亮。

终端输出(点击下一页):

[23/May/202610:17:45]"GET /products/list/?page=2 HTTP/1.1"2009654
7.4 空分类

访问一个不存在的分类 ID,如/products/list/?category_id=999,页面显示“该分类下暂无商品。”,总数 0。

终端输出:

[23/May/202610:18:33]"GET /products/list/?category_id=999 HTTP/1.1"2004512

八、SQL 查询分析(简要)

Django 的connection.queries可以在开发环境查看 SQL。但此处不展开,只需要知道通过select_relatedprefetch_related我们的查询次数控制在 3~5 次内(分类、SKU 列表、图片),没有 N+1 问题。后续性能优化篇会详解。


九、总结与下集预告

今天我们让商品“上架”到了用户面前,实现了:

  • 商品列表页视图,支持按分类筛选(包含子分类);

  • 利用 Django 内置分页器Paginator实现高效分页;

  • 响应式商品卡片布局,展示主图、价格、销量;

  • 分类快捷导航,方便用户浏览。

现在,用户可以浏览全部商品,也能按分类查看,还能跳转到详情页查看规格。但详情页的图片展示还很简陋,下一站我们就来解决这个问题。第 13 篇,我将带你完善商品详情页与图片展示,包括多图切换、主图高亮、缩略图导航等,让详情页真正“活”起来。保持节奏,明天见!

想了解更多还可以去公众号、今日头条搜索「IT策士」,一起升级 IT 思维 !


本文为《Django 从 0 到 1 打造完整电商平台》系列第 12 篇,作者:IT策士。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/23 23:21:51

IDA32与pwntools协同调试栈溢出实战指南

1. 这不是“黑客电影”&#xff0c;而是我调试第7个CTF栈溢出题时的真实桌面你打开IDA32&#xff0c;看到一串密密麻麻的汇编指令&#xff0c;main函数里有个gets()调用像颗定时炸弹——它不检查输入长度&#xff0c;而你手边的pwntools脚本刚跑出[x] Starting local process .…

作者头像 李华
网站建设 2026/5/23 23:15:59

销售预测实战:从数据清洗到业务落地的端到端方法论

1. 为什么销售预测不是“算命”&#xff0c;而是企业运转的中枢神经我做零售行业数据建模整整十二年&#xff0c;从给县城连锁超市搭第一个Excel预测模板&#xff0c;到后来带团队为全国性快消品牌构建千万级SKU的滚动预测系统&#xff0c;踩过的坑比走过的路还多。很多人一听到…

作者头像 李华
网站建设 2026/5/23 23:14:00

大模型规模信仰的科学反思:数据、架构与训练策略的结构性失衡

1. 项目概述&#xff1a;一场被高估的“规模信仰”实验你最近肯定刷到过那条新闻——微软和OpenAI联手砸下1000亿美元&#xff0c;要建一台叫“Stargate”的超级计算机。不是实验室里的概念验证&#xff0c;不是小规模试点&#xff0c;是实打实按“百亿美金”这个量级来规划的基…

作者头像 李华
网站建设 2026/5/23 23:13:03

CrewAI 实战评测 角色分工能提升多少吞吐和稳定性

CrewAI 实战评测:角色分工能提升多少吞吐和稳定性 本文基于 15 年软件架构经验 + 3 个月多 Agent 落地实践,通过 3 类典型场景、1200 次对照实验,量化拆解角色分工式多 Agent 架构的真实收益与适用边界,所有代码、数据均可复现。 一、问题背景与核心概念 1.1 问题背景:单…

作者头像 李华
网站建设 2026/5/23 23:10:09

从零手写神经网络:NumPy实现两层MLP与反向传播详解

1. 项目概述&#xff1a;这不是“又一个”神经网络教程&#xff0c;而是一次手把手拆解真实NN构建过程的实战复盘“NN#8 — Neural Networks Decoded (Build your first NN in Python)”这个标题里藏着三个关键信号&#xff1a;NN#8说明它属于一个有延续性的系列&#xff0c;不…

作者头像 李华