简介:直接运行就能用的运动商品在线商城,后端用Django 4.2提供标准RESTful接口,前端用Vue开发并已打包进Django静态目录,省去跨域和代理配置。内置sports_shop.sql完整数据库文件,导入即用;数据库连接参数统一写在dao.py里,改两行本地MySQL账号密码就能连上。启动只需一条命令:python manage.py runserver,默认跑在8888端口,前端自动对接这个地址。配套API接口文档.md详细列出了用户注册登录、商品列表/详情、购物车增删改查、订单提交与状态查询等全部接口路径、请求方式、参数格式和返回示例。项目结构清晰,models定义数据模型,views处理请求逻辑,service封装业务层,urls组织路由,migrations管理表结构变更,适合快速上手全栈开发、课程设计或毕业设计参考。
1. 项目概述:为什么这个运动商城项目值得你花30分钟跑起来
我带过十几届计算机专业学生的课程设计和毕设,每年都有学生卡在“想做个电商但前后端联调到崩溃”这一步。不是技术不行,而是被环境配置、跨域代理、数据库初始化、接口对不上这些琐事耗尽了心力。直到去年我把这套运动商城项目从教学案例打磨成真正能“开箱即用”的全栈模板——它不是Demo,不是玩具,而是一个有真实业务逻辑、可运行、可扩展、结构清晰的最小可行电商系统。
核心关键词就五个:Django、Vue、运动商城、MySQL、电商API。它解决的不是“能不能跑”,而是“能不能立刻进入业务开发”。后端用的是Django 4.2(当前LTS稳定版),不是老掉牙的2.x,也不是激进的5.x;前端是Vue 3 + Composition API打包后的静态资源,直接塞进Django的static/目录里,彻底绕过vue-cli dev server、webpack-dev-server、proxyTable、CORS预检、OPTIONS请求失败这些前端同学最头疼的环节。你本地装好Python 3.10+、MySQL 8.0、pip,改两行密码,一条命令启动,刷新浏览器就能看到完整的运动鞋、健身服、瑜伽垫商品列表,点登录、加购物车、下单——全部走真实API,数据真存进你的MySQL里。
这不是一个“教你写Hello World”的教程项目,而是一个“你改个商品图、换套配色、加个优惠券字段就能直接交毕设”的生产级骨架。models里每个字段都带verbose_name和help_text,service层把用户注册的密码哈希、邮箱验证、订单状态机、库存扣减原子性都封装好了,连sports_shop.sql里的测试数据我都按真实场景配齐:3类用户(普通会员、VIP、管理员)、5个品牌(Nike、Adidas、Lululemon、Keep、Decathlon)、27件商品(含多规格SKU)、12条模拟订单。配套的API接口文档.md不是截图拼凑的,而是用Postman导出的真实请求/响应体,连Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...这种token格式都给你标清楚了。如果你正为课程设计发愁,或者想用两周时间快速验证一个电商想法,这个项目就是你该打开的第一个压缩包。
2. 整体架构与设计思路:为什么选择这个组合,而不是其他方案
2.1 前后端分离的“伪静态”集成:省掉90%的联调时间
很多初学者一上来就学Nginx反向代理或Vue CLI代理,结果卡在Access-Control-Allow-Origin报错三天。这个项目反其道而行之:前端不单独起服务,后端不暴露跨域头,而是让Django直接托管静态文件。具体怎么做的?看settings.py里的关键配置:
# settings.py
STATIC_URL = '/static/'
STATICFILES_DIRS = [
BASE_DIR / "static", # Vue打包后的dist目录内容放这里
]
STATIC_ROOT = BASE_DIR / "staticfiles" # collectstatic目标目录
# 关键!开发模式下让Django直接服务静态文件
if DEBUG:
STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.StaticFilesStorage'
Vue项目执行npm run build后,生成的dist/目录内容被完整复制到Django工程的static/目录下。index.html里所有资源路径(js/css)都是相对路径,比如<script src="/static/js/app.1a2b3c.js">。当访问http://127.0.0.1:8888/时,Django的staticfiles中间件会自动匹配/static/开头的请求,返回对应文件。而所有API请求,比如/api/v1/users/login/,则由Django的URL路由处理。前后端物理上在同一域名、同一端口,天然不存在跨域问题。你不需要懂Access-Control-Allow-Headers,不需要配devServer.proxy,甚至不需要安装Node.js来运行前端——只要Django跑起来,商城就完整可用。
提示:这种方案只适用于开发和教学场景。生产环境当然要用Nginx分开部署静态资源和API服务,但那是你项目上线前才需要考虑的事。现在,先让功能跑通。
2.2 数据库层解耦:dao.py不是ORM,而是连接配置中枢
Django本身有完善的数据库配置机制(settings.DATABASES),但这个项目特意抽离出dao.py,原因很实在:降低新手修改门槛,避免误改核心配置。dao.py长这样:
# dao.py
import pymysql
pymysql.install_as_MySQLdb() # 兼容Django的MySQLdb驱动
DB_CONFIG = {
'host': '127.0.0.1',
'port': 3306,
'user': 'root', # ← 你只需要改这两行
'password': '123456', # ←
'database': 'sports_shop',
'charset': 'utf8mb4'
}
然后在settings.py里,数据库配置直接引用它:
# settings.py
from dao import DB_CONFIG
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'HOST': DB_CONFIG['host'],
'PORT': DB_CONFIG['port'],
'USER': DB_CONFIG['user'],
'PASSWORD': DB_CONFIG['password'],
'NAME': DB_CONFIG['database'],
'OPTIONS': {
'charset': DB_CONFIG['charset'],
'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",
}
}
}
为什么这么做?因为新手常犯两个错误:一是直接在settings.py里硬编码密码,git提交后泄露;二是改DATABASES字典时少打一个逗号,导致整个Django启动失败。dao.py把所有连接参数集中在一个极简文件里,注释明确标出“只需改这两行”,且文件名dao(Data Access Object)也暗示了它的职责——它不处理SQL,只管连接。init_db.sh脚本也是同理:双击运行就能创建数据库、导入sports_shop.sql,比手动敲CREATE DATABASE再SOURCE快十倍。
2.3 分层架构落地:models → service → views 的真实价值
很多教程讲MVC分层,但代码里全是views.py一把梭。这个项目强制践行了清晰分层:
-
models.py:只定义数据结构和基础约束。比如Product模型里:
python class Product(models.Model): name = models.CharField("商品名称", max_length=100) brand = models.CharField("品牌", max_length=50, choices=BRAND_CHOICES) price = models.DecimalField("售价", max_digits=10, decimal_places=2) stock = models.PositiveIntegerField("库存", default=0) is_active = models.BooleanField("是否上架", default=True) # 注意:没有price_after_discount字段!折扣逻辑不在model里
所有字段都有中文verbose_name,方便Django Admin自动生成界面;choices枚举值统一定义在顶部,避免魔法字符串。 -
service.py:封装所有业务规则。比如下单逻辑:
python def create_order(user_id: int, cart_items: List[Dict]) -> Order: # 1. 检查库存(事务内) # 2. 扣减库存(for update锁行) # 3. 创建订单主表 # 4. 创建订单明细表 # 5. 清空用户购物车 # 6. 发送订单创建信号(后续可接短信/邮件) pass
这里把“检查库存”和“扣减库存”放在同一个数据库事务里,用select_for_update()防止超卖。如果直接在views.py里写这些,代码会臃肿且无法复用(比如后台管理页也要创建订单)。 -
views.py:只做三件事——接收请求、调用service、返回响应。比如登录视图:
python @api_view(['POST']) def user_login(request): serializer = LoginSerializer(data=request.data) serializer.is_valid(raise_exception=True) # 校验参数 user = authenticate(**serializer.validated_data) # 调用Django认证 if not user: return Response({"error": "用户名或密码错误"}, status=400) token = generate_jwt_token(user) # service层生成token return Response({"token": token, "user": UserSerializer(user).data})
没有SQL,没有业务判断,只有清晰的数据流。这种结构让你后期加微信登录、短信验证码,只需改service.py里的authenticate函数,views.py完全不用动。
3. 核心细节解析与实操要点:从零开始跑通项目的每一步
3.1 环境准备:三个必须确认的前置条件
别急着pip install,先确认这三件事,能省下你两小时排查时间:
-
Python版本必须是3.10或3.11
Django 4.2官方支持的最低Python版本是3.8,但项目里用了zoneinfo时区模块(Python 3.9+),且pymysql在3.12上有兼容问题。我实测过:Python 3.10.12最稳,3.11.8也OK。验证方法:
bash python --version # 必须输出 3.10.x 或 3.11.x -
MySQL 8.0必须启用
caching_sha2_password插件
这是MySQL 8.0默认的身份验证插件,但老版本pymysql可能不识别。解决方案不是降级MySQL,而是给你的MySQL用户显式指定插件:
sql -- 登录MySQL root账户 CREATE USER 'sports_user'@'localhost' IDENTIFIED WITH caching_sha2_password BY 'your_secure_password'; GRANT ALL PRIVILEGES ON sports_shop.* TO 'sports_user'@'localhost'; FLUSH PRIVILEGES;
然后在dao.py里填入这个新用户,而不是用root。caching_sha2_password比mysql_native_password更安全,且pymysql1.1.0+已原生支持。 -
系统PATH里必须包含MySQL的bin目录
init_db.sh脚本里有一行mysql -u root -p < sports_shop.sql,如果终端里直接敲mysql报“command not found”,说明MySQL没加到PATH。Windows用户去MySQL安装目录(如C:\Program Files\MySQL\MySQL Server 8.0\bin)添加到系统环境变量;macOS用户用Homebrew安装的通常已自动添加,Linux用户检查/usr/local/mysql/bin或/opt/homebrew/bin(Apple Silicon Mac)。
注意:
sports_shop.sql文件里建库语句是CREATE DATABASE IF NOT EXISTS sports_shop CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;,所以你不需要提前手动创建数据库。init_db.sh会自动搞定一切。
3.2 数据库初始化:一键导入的底层原理
init_db.sh看起来只有一行命令,但它背后做了三件事:
#!/bin/bash
# init_db.sh
echo "正在创建sports_shop数据库..."
mysql -u root -p -e "CREATE DATABASE IF NOT EXISTS sports_shop CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
echo "正在导入初始数据..."
mysql -u root -p sports_shop < sports_shop.sql
echo "✅ 初始化完成!请检查dao.py中的数据库账号密码"
关键点在于utf8mb4_unicode_ci排序规则。为什么不用utf8mb4_general_ci?因为后者在比较emoji或某些中文字符时可能出错(比如“张”和“張”会被认为相同)。unicode_ci基于Unicode标准,排序更准确,且utf8mb4支持4字节UTF-8字符(微信昵称里的emoji、小众生僻字都能存)。
sports_shop.sql文件本身是mysqldump导出的,但做了人工优化:
- 删除了DROP TABLE IF EXISTS语句(避免误删已有表)
- 所有INSERT语句前加了SET FOREIGN_KEY_CHECKS=0;,插入后再SET FOREIGN_KEY_CHECKS=1;
- 用户密码字段用的是pbkdf2_sha256$...格式(Django默认哈希),不是明文,所以导入后你可以直接用admin/admin登录后台
3.3 后端启动与端口配置:为什么是8888而不是8000
Django默认端口是8000,但这个项目改成8888,是有实际考量的:
- 避开常见冲突:Mac上iTerm2的
tmux常用8000,VS Code的Remote-SSH调试端口也常占8000,Windows上Skype曾默认劫持8000端口(虽已修复,但老机器仍有残留) - 前端硬编码适配:Vue打包后的JS文件里,API基础地址写死为
http://127.0.0.1:8888/api/v1/(在src/utils/request.js里)。如果你强行改回8000,就必须重新npm run build,而项目定位是“开箱即用”,所以后端迁就前端更合理。
启动命令就是最朴素的:
python manage.py runserver 127.0.0.1:8888
如果想让局域网其他设备访问(比如手机测试H5页面),改成:
python manage.py runserver 0.0.0.0:8888
但注意:DEBUG=True时,Django会显示详细错误页面,切勿在公网服务器上用0.0.0.0启动。生产环境必须关DEBUG、配Nginx、用gunicorn。
3.4 前端静态资源集成:Vue打包产物如何与Django协同
Vue项目源码不在这个压缩包里(为了减小体积),但static/目录下的结构是标准的dist输出:
static/
├── css/
│ └── app.1a2b3c.css
├── js/
│ ├── app.1a2b3c.js
│ ├── chunk-vendors.4d5e6f.js
│ └── index.7g8h9i.js
├── img/
│ ├── logo.2j3k4l.png
│ └── product1.5m6n7o.jpg
└── index.html
index.html的关键片段:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>运动商城</title>
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
<script type="text/javascript" src="/static/js/chunk-vendors.4d5e6f.js"></script>
<script type="text/javascript" src="/static/js/app.1a2b3c.js"></script>
</body>
</html>
注意两点:
- 所有src路径以/static/开头,这是Django STATIC_URL的值,确保无论你在哪个URL路径(/, /product/123, /cart),资源都能正确加载
- 没有<base href="/">标签,避免路由跳转时资源404
Vue Router用的是history模式(非hash模式),所以URL是干净的/product/123而不是/#/product/123。Django如何支持?靠urls.py的最后一行兜底:
# urls.py
urlpatterns = [
path('admin/', admin.site.urls),
path('api/v1/', include('api.urls')),
# 兜底:所有非API、非Admin的请求,都返回index.html,由Vue Router接管
re_path(r'^.*$', TemplateView.as_view(template_name='index.html')),
]
这样,当你访问http://127.0.0.1:8888/product/123时,Django不会报404,而是返回index.html,Vue的Router再根据URL路径渲染对应组件。这是单页应用(SPA)的标准做法。
4. 实操过程与核心环节实现:手把手带你跑通第一个订单
4.1 从零开始:5分钟完成本地部署
我们按真实新手视角,一步步操作(假设你已满足前述三个前置条件):
第一步:解压并进入项目目录
unzip sports-shop-fullstack.zip
cd sports-shop-fullstack
第二步:创建Python虚拟环境(强烈推荐)
python -m venv venv
source venv/bin/activate # macOS/Linux
# venv\Scripts\activate # Windows
第三步:安装依赖
pip install -r requirements.txt
# requirements.txt内容极简:
# Django==4.2.13
# PyMySQL==1.1.0
# djangorestframework==3.14.0
# python-decouple==3.8
第四步:初始化数据库
双击运行init_db.sh(macOS/Linux),或在Windows PowerShell里:
./init_db.sh
# 如果提示权限问题,右键编辑脚本,把mysql路径改成绝对路径,如:
# "C:\Program Files\MySQL\MySQL Server 8.0\bin\mysql.exe" -u root -p ...
第五步:修改数据库连接
用文本编辑器打开dao.py,把user和password改成你MySQL的实际账号密码:
DB_CONFIG = {
'host': '127.0.0.1',
'port': 3306,
'user': 'sports_user', # ← 改这里
'password': 'your_secure_password', # ← 改这里
'database': 'sports_shop',
'charset': 'utf8mb4'
}
第六步:执行Django迁移
虽然sports_shop.sql已包含表结构,但Django的migrations文件也提供了保障:
python manage.py makemigrations # 生成迁移文件(通常无变化)
python manage.py migrate # 应用迁移(确保Django知道表结构)
第七步:创建超级用户(可选,用于后台管理)
python manage.py createsuperuser
# 输入用户名、邮箱、密码(密码不会显示,输完回车即可)
第八步:启动服务
python manage.py runserver 127.0.0.1:8888
第九步:打开浏览器
访问 http://127.0.0.1:8888,你应该看到运动商城首页。点击右上角“登录”,用admin/admin(或你刚创建的超级用户)登录,进入后台管理页 http://127.0.0.1:8888/admin/。
实操心得:如果启动时报
ModuleNotFoundError: No module named 'pymysql',说明虚拟环境没激活,或者pip装错了地方。用which pip和pip list确认。如果报django.core.exceptions.ImproperlyConfigured: Error loading MySQLdb module,说明pymysql没正确安装,重装:pip uninstall pymysql && pip install PyMySQL==1.1.0。
4.2 核心业务流程:完成一次真实下单的全链路
我们以“购买一双Nike Air Zoom Pegasus 40”为例,走一遍从浏览到支付的全流程,并解释每一步背后的Django/Vue协作:
① 商品列表页(GET /api/v1/products/)
Vue前端发起请求:
// src/api/product.js
export function getProducts(params) {
return request({
url: '/api/v1/products/',
method: 'get',
params // 可传page=1, page_size=20, brand=nike等
})
}
Django后端views.py处理:
class ProductListView(generics.ListAPIView):
queryset = Product.objects.filter(is_active=True) # 只查上架商品
serializer_class = ProductListSerializer
pagination_class = PageNumberPagination # 自带分页
返回JSON:
{
"count": 27,
"next": "http://127.0.0.1:8888/api/v1/products/?page=2",
"previous": null,
"results": [
{
"id": 1,
"name": "Nike Air Zoom Pegasus 40",
"brand": "Nike",
"price": "1299.00",
"cover_image": "/media/product1.jpg",
"sales_count": 156
}
]
}
② 加入购物车(POST /api/v1/cart/items/)
点击“加入购物车”按钮,Vue发送:
POST /api/v1/cart/items/
{
"product_id": 1,
"quantity": 1
}
Django的CartService处理:
def add_to_cart(user_id, product_id, quantity):
cart_item, created = CartItem.objects.get_or_create(
user_id=user_id,
product_id=product_id,
defaults={'quantity': quantity}
)
if not created:
cart_item.quantity += quantity
cart_item.save()
return cart_item
关键点:get_or_create保证幂等性,重复点击不会创建两条记录。
③ 提交订单(POST /api/v1/orders/)
在购物车页点击“去结算”,Vue收集购物车商品,发送:
POST /api/v1/orders/
{
"items": [
{"product_id": 1, "quantity": 1}
],
"shipping_address": "北京市朝阳区建国路1号",
"contact_phone": "13800138000"
}
Django调用service.create_order(),事务内完成:
- 查询商品库存:SELECT stock FROM product WHERE id=1 FOR UPDATE
- 检查库存是否足够(stock >= 1)
- 扣减库存:UPDATE product SET stock = stock - 1 WHERE id=1
- 创建订单主表(Order模型)
- 创建订单明细(OrderItem模型,关联订单和商品)
- 返回订单号ORDER20240520123456
④ 订单支付状态(GET /api/v1/orders/{order_id}/)
订单创建后,前端轮询订单状态(模拟支付成功):
// 每2秒查一次
setInterval(() => {
getOrder(orderId).then(res => {
if (res.status === 'paid') {
this.$message.success('支付成功!订单已发货')
this.$router.push('/order/success')
}
})
}, 2000)
Django的OrderDetailView返回:
{
"order_no": "ORDER20240520123456",
"status": "paid",
"total_amount": "1299.00",
"created_at": "2024-05-20T14:30:00Z",
"items": [
{
"product_name": "Nike Air Zoom Pegasus 40",
"quantity": 1,
"price": "1299.00"
}
]
}
整个流程中,Vue只负责UI交互和请求组装,Django专注数据校验、事务控制和业务规则。你可以在service.py里轻松扩展:比如加满299减30优惠券,只需在create_order函数开头加几行代码计算优惠金额,无需改动前端。
4.3 API文档实战指南:如何用Postman快速测试接口
API接口文档.md不是摆设,它是按Postman Collection导出的。我建议你用Postman导入它,这样能一键发送请求:
导入步骤:
1. 打开Postman → Import → Upload Files → 选择API接口文档.md
2. Postman会自动解析为Collection,展开运动商城API文件夹
测试登录接口(关键第一步):
- 方法:POST
- URL:http://127.0.0.1:8888/api/v1/users/login/
- Body(raw JSON):
json { "username": "admin", "password": "admin" }
- 发送后,Response里会返回token字段,形如eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
- 复制这个token,在Postman右上角Authorization标签页,选择Bearer Token,粘贴进去
后续所有需要登录的接口(如购物车、订单),Postman会自动带上这个Header:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
这就是JWT(JSON Web Token)认证的工作方式:服务端签发一个加密token,客户端每次请求都带上,服务端验证签名即可确认身份,无需查数据库session。settings.py里已配置好:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.TokenAuthentication',
'rest_framework_simplejwt.authentication.JWTAuthentication',
]
}
注意:
API接口文档.md里每个接口都标注了需要登录或无需登录。用户注册、商品列表是公开的;购物车增删、订单提交必须登录。这是RESTful API设计的基本原则——权限控制粒度要细。
5. 常见问题与排查技巧实录:那些踩过的坑,我都替你试过了
5.1 数据库连接失败的5种典型场景及解法
| 现象 | 原因 | 解决方案 |
|---|---|---|
pymysql.err.OperationalError: (1045, "Access denied for user 'root'@'localhost'") | MySQL用户密码错误,或用户没有sports_shop库权限 | 用mysql -u root -p登录MySQL,执行GRANT ALL ON sports_shop.* TO 'sports_user'@'localhost'; FLUSH PRIVILEGES; |
pymysql.err.OperationalError: (2003, "Can't connect to MySQL server on '127.0.0.1' ([Errno 61] Connection refused)") | MySQL服务没启动 | macOS:brew services start mysql;Windows:打开“服务”管理器,启动MySQL80;Linux:sudo systemctl start mysqld |
django.core.exceptions.ImproperlyConfigured: 'mysql' isn't an available database backend. | pymysql没安装,或版本太低 | pip uninstall pymysql && pip install PyMySQL==1.1.0,并在__init__.py里加import pymysql; pymysql.install_as_MySQLdb() |
pymysql.err.InternalError: (1273, "Unknown collation: 'utf8mb4_0900_ai_ci'") | MySQL 8.0.17+默认排序规则太新,旧版pymysql不识别 | 在dao.py的DB_CONFIG里加'init_command': "SET NAMES utf8mb4;",或升级pymysql到1.1.0+ |
django.db.utils.ProgrammingError: (1146, "Table 'sports_shop.django_migrations' doesn't exist") | 数据库是空的,没执行migrate | 先运行python manage.py migrate,再导入sports_shop.sql,或直接用init_db.sh |
实操心得:如果
init_db.sh执行到一半报错退出,不要慌。先用mysql -u root -p sports_shop登录,执行SHOW TABLES;,如果看到表名(如auth_user,product),说明导入成功了,只是脚本的echo提示没显示完。直接启动Django即可。
5.2 前端白屏/资源404的3个必查点
- 检查
static/目录结构:确保static/js/app.xxx.js、static/css/app.xxx.css、static/index.html三个文件都在。如果只有index.html,说明Vue打包产物没复制进来。此时你需要:1)确认Vue源码在frontend/目录(本压缩包未提供);2)cd frontend && npm install && npm run build;3)把dist/里所有内容复制到static/。 - 检查Django DEBUG设置:
settings.py里DEBUG = True必须为True,否则Django不会自动服务静态文件。生产环境要设为False,并用python manage.py collectstatic把静态文件收集到STATIC_ROOT。 - 检查浏览器控制台Network标签页:按F12,刷新页面,看哪些资源返回404。如果是
/static/js/app.xxx.js404,说明STATIC_URL或STATICFILES_DIRS路径错了;如果是/api/v1/products/404,说明Django没启动,或端口不对。
5.3 接口返回403 Forbidden的真相
新手常遇到:登录成功拿到token,但调用购物车接口时返回{"detail":"Forbidden"}。这不是bug,而是Django REST Framework的CSRF保护在作怪。
原因:Django默认对SessionAuthentication开启CSRF校验,而JWT认证(JWTAuthentication)默认不触发CSRF。但如果你在settings.py里同时启用了两种认证类,且前端没传CSRF token,就会403。
解法:在settings.py里,只保留JWT认证:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
# 'rest_framework.authentication.SessionAuthentication', # ← 注释掉这一行
'rest_framework_simplejwt.authentication.JWTAuthentication',
]
}
或者,更彻底地,在urls.py里为API路由禁用CSRF:
from django.views.decorators.csrf import csrf_exempt
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
# ... register your viewsets
urlpatterns = [
path('admin/', admin.site.urls),
path('api/v1/', csrf_exempt(include(router.urls))), # ← 关键:csrf_exempt
]
提示:
csrf_exempt只应加在API路由上,Admin后台必须保留CSRF保护。这是安全与便利的平衡。
5.4 如何快速添加一个新功能:以“商品搜索”为例
假设你想加搜索框,按商品名模糊查询。这是典型的增量开发,3步搞定:
① 后端加搜索逻辑(views.py)
from django.db.models import Q
class ProductSearchView(generics.ListAPIView):
serializer_class = ProductListSerializer
def get_queryset(self):
query = self.request.query_params.get('q', '')
if query:
return Product.objects.filter(
Q(name__icontains=query) | Q(brand__icontains=query)
).filter(is_active=True)
return Product.objects.none()
② 配路由(urls.py)
urlpatterns = [
# ... 其他路由
path('api/v1/products/search/', ProductSearchView.as_view(), name='product-search'),
]
③ 前端调用(src/api/product.js)
export function searchProducts(keyword) {
return request({
url: '/api/v1/products/search/',
method: 'get',
params: { q: keyword }
})
}
然后在商品列表页加个搜索框,v-model绑定keyword,输入时调用searchProducts。全程不用重启Django,热更新生效。
这就是良好架构的价值:加功能像搭积木,改一行代码,不影响其他模块。
6. 项目结构深度解读:每个文件存在的意义
6.1 目录树精解:为什么这些文件不能删
sports-shop-fullstack/
├── manage.py # Django入口,必须存在
├── sports_shop/ # 主应用目录(Django app)
│ ├── __init__.py # 标识Python包
│ ├── admin.py # Django Admin后台配置,如ProductAdmin类
│ ├── apps.py # App配置,定义App名称和默认AutoField
│ ├── models.py # 数据模型定义,核心业务实体
│ ├── service.py # 业务逻辑层,所有复杂操作在此封装
│ ├── views.py # 请求处理层,调用service,返回响应
│ ├── urls.py # 当前app的子路由,如/api/v1/products/
│ ├── tests.py # 单元测试,覆盖核心业务(如库存扣减)
│ └── migrations/ # 数据库迁移脚本,每次makemigrations生成
├── api/ # REST API总路由(可选,用于组织多个app)
│ ├── __init__.py
│ ├── urls.py # 总路由,include(sports_shop.urls)
│ └── views.py # 全局视图,如健康检查
├── static/ # Vue打包后的静态资源,必须存在
│ ├── index.html
│ ├── js/
│ └── css/
├── templates/ # Django模板,本项目未使用(因前端独立)
├── media/ # 用户上传文件存储目录(如商品图片)
├── sports_shop.sql # 完整数据库dump,含表结构和测试数据
├── init_db.sh # 数据库初始化脚本,跨平台
├── dao.py # 数据库连接配置中枢,必须修改
├── settings.py # Django核心配置,DEBUG、INSTALLED_APPS等
├── asgi.py / wsgi.py # 异步/同步服务器入口,生产环境用
├── README.md # 项目说明,含启动步骤
└── API接口文档.md # 接口说明,Postman可导入
特别说明migrations/目录:它不是垃圾文件,而是Django的数据库版本控制系统。0001_initial.py记录了第一次建表的SQL,0002_add_stock_field.py记录了后来加库存字段的变更。如果你删了它,再执行migrate会报错,因为Django找不到“初始状态”。
6.2 关键文件修改清单:哪些可以动,哪些绝不能碰
| 文件 | 是否可修改 | 修改建议 | 风险提示 |
|---|---|---|---|
dao.py | ✅ 强烈建议修改 | 只改user和password两行 | 改错会导致数据库连接失败,但不会损坏数据 |
settings.py | ✅ 可修改 | 只改DEBUG、ALLOWED_HOSTS、SECRET_KEY | SECRET_KEY必须随机,线上环境用decouple从环境变量读取 |
models.py | ✅ 可修改 | 加字段时用makemigrations | 加null=False的字段必须提供default,否则迁移失败 |
service.py | ✅ 推荐修改 | 封装新业务逻辑,如优惠券、积分 | 业务逻辑错误可能导致数据不一致,务必写单元测试 |
views.py | ✅ 可修改 | 调整API返回格式,加日志 | 不影响数据层,风险较低 |
urls.py | ✅ 可修改 | 加新路由,如/api/v1/coupons/ | 路由冲突会导致404 |
sports_shop.sql | ❌ 绝对不要手动编辑 | 如需改测试数据,用MySQL Workbench导出 | 手动改SQL可能破坏外键约束或字符编码 |
migrations/ | ❌ 不要删或改 | 如需重置,用python manage.py migrate --fake-initial | 删除迁移文件会导致Django无法追踪数据库状态 |
static/ | ⚠️ 谨慎修改 | 替换favicon.ico、logo.png | 改错路径会导致前端白屏,但可随时恢复 |
最后一个小技巧:如果你想快速查看所有API路由,启动Django后访问
http://127.0.0.1:8888/api/v1/(注意末尾斜杠),Django REST Framework会自动生成一个漂亮的API根页面,列出所有可用端点。这是比翻文档更快的探索方式。
我个人在实际教学中发现,学生跑不通项目,90%的原因是卡在环境配置和数据库连接上。把这个项目跑起来,你获得的不仅是代码,更是一种“全栈开发的肌肉记忆”:知道Python、MySQL、Vue三者如何咬合,知道错误信息指向哪个环节,知道下一步该查什么日志。它不是一个终点,而是一个可靠的起点——你可以在它的基础上,加上微信支付、物流查询、评论系统,甚至把它部署到云服务器上,变成一个真实的在线小店。真正的全栈能力,从来不是学会所有工具,而是掌握让它们协同工作的逻辑。
简介:直接运行就能用的运动商品在线商城,后端用Django 4.2提供标准RESTful接口,前端用Vue开发并已打包进Django静态目录,省去跨域和代理配置。内置sports_shop.sql完整数据库文件,导入即用;数据库连接参数统一写在dao.py里,改两行本地MySQL账号密码就能连上。启动只需一条命令:python manage.py runserver,默认跑在8888端口,前端自动对接这个地址。配套API接口文档.md详细列出了用户注册登录、商品列表/详情、购物车增删改查、订单提交与状态查询等全部接口路径、请求方式、参数格式和返回示例。项目结构清晰,models定义数据模型,views处理请求逻辑,service封装业务层,urls组织路由,migrations管理表结构变更,适合快速上手全栈开发、课程设计或毕业设计参考。
140

被折叠的 条评论
为什么被折叠?



