
做商城类项目Python Vue这套组合可以说是个人开发者做全栈项目时最稳的搭配之一。这个电脑配件商城的标题我盯了好一会儿它几乎把初学者到中级开发者最关心的几个点全涵盖了后端用Django还是Flask、前端用Vue怎么搭、PyCharm怎么把整个环境串起来、以及电商这类核心业务到底该怎么设计和落地。这篇文章我会从项目整体设计思路、环境搭建、后端模块实现、前端页面联动到日常开发中一定会踩的坑完整走一遍。不管你是准备做课程设计、毕业设计还是想给自己攒一个拿得出手的全栈项目作品集这套东西都直接能抄作业。我会尽量把每一步背后的理由也讲清楚毕竟只会敲代码不够知道为什么这么写才值钱。1. 项目整体设计与技术选型思考1.1 核心需求拆解一个配件商城需要什么功能做这类商城项目最忌讳一上来就写代码。我见过太多人第一天就搭好了Django项目结果写到购物车发现缺字段写到订单发现缺状态前前后后重构了三遍。所以第一步一定是把需求拆清楚哪怕只是写在记事本里。电脑配件商城说白了就是一套标准B2C电商系统核心功能分两条线买家能看到什么、买家能干什么。买家能看到的就是商品列表、商品详情、分类筛选、搜索买家能干的就是注册登录、加购物车、下订单、查看订单状态、管理收货地址。这些都跑通了前台就合格了。后台管理端反而容易被忽略但它是整个项目的骨架。管理员要能维护商品信息、上下架商品、调整库存、查看和处理订单。这部分如果完全自己写工作量会翻一倍不止所以这也是为什么很多人选择Django——它自带的Admin后台能帮你省掉大量重复的CRUD页面开发。商家端我建议加一个简单的销售统计页面用图表展示每天的订单量和销售额。这个功能看着不起眼但很能体现一个项目的完整度答辩或展示的时候加点分是没问题。1.2 为什么选Django/Flask Vue这套组合先说后端。标题里同时提到Django和Flask说明这也是很多人在纠结的点。我的建议是如果你更看重开发效率和自带功能选Django如果想把框架的灵活性和轻量感握在自己手里选Flask。两者在项目里并不是敌人核心业务用Django的ORM和Admin能省很多事偶尔需要一个轻量服务或工具接口Flask临时起一个也很快。Django的优势在于全家桶自带ORM、Admin后台、认证系统、表单校验、迁移工具基本上你想到的它都有。它的ORM真的能大幅减少SQL手写量而且模型一旦改动makemigrations migrate指令就能同步数据库结构这种体验对于一个单枪匹马的项目来说太重要了。Flask则是另一种风格它只提供最核心的Web框架能力路由、模板、请求响应其余全部交给你自由组装。它的灵活意味着你可以完全掌控项目结构但也意味着数据库、表单、用户认证都要自己选择和集成。我个人经验是Flask写一个几十行的工具接口特别舒服但要做到Django Admin那种管理水平需要额外引入flask-admin之类的库整体没那么省心。再说前端。Vue这套生态本身足够完整Vue Router管路由Vuex或Pinia管全局状态Axios发请求Element Plus做UI组件库一套下来页面开发效率很高。而且Vue的响应式数据绑定特性对小白极其友好你改一个data里的变量页面自动跟着变不需要像原生JS那样手动操作DOM。1.3 前后端分离的整体架构与开发流程这个项目的架构我是按前后端分离的标准来设计的。后端只管提供JSON格式的API接口前端通过HTTP请求来拿数据和提交数据。这样有一个很大的好处前端开发和后端开发可以完全并行定好接口协议后各干各的谁也不卡谁。接口层我用的是RESTful风格用HTTP方法表达操作语义GET拿资源、POST创建资源、PUT/DELETE改动或删除资源。比如商品列表就是GET /api/products加入购物车就是POST /api/cart这种设计光看到接口路径和请求方法基本就知道业务在干嘛了。实际开发流程我会建议这么走先把数据库设计好然后写接口文档哪怕是自己给自己看也行接着把后端所有接口跑通用Postman或Apifox验证每个接口返回的数据结构最后才开始写前端页面。别一上来就前后端同时写接口没定好前端拿到什么字段都不知道写出来的代码十有八九要返工。2. 环境准备与工程初始化PyCharm与基础依赖2.1 Python环境与PyCharm配置先说Python环境。我建议使用Python 3.10以上的稳定版本不要用最新版的尝鲜特性也不需要停留在老版本。装好之后在命令行窗口里执行python --version验证一下能输出版本号就说明安装成功了。然后就是PyCharm。我认为它是写过Python的人最顺手的IDE尤其是专业版对Django和Flask都有原生支持能直接新建Django项目、自动配置运行命令、识别模板目录。如果你用的是社区版问题也不大手动创建项目后安装依赖也一样用只是少了点可视化辅助。新建项目的时候最关键的设置是解释器。务必给每个项目单独建一个虚拟环境这样不同项目的依赖不会互相污染。PyCharm新建项目时选VirtualenvPython版本选择你刚装的那个其余保持默认即可。虚拟环境建好后直接在Terminal窗口激活它然后安装依赖。2.2 后端项目初始化Django与Flask两种方案如果是Django方案执行这几条命令就能把项目骨架建好pip install django djangorestframework django-cors-headers django-admin startproject computer_store cd computer_store python manage.py startapp goods python manage.py startapp user python manage.py startapp order注意这里我额外装了djangorestframework和django-cors-headers。前者是DRF用来写API接口的它提供的序列化器和视图集能大大减少接口代码量后者解决跨域问题后面会专门讲。我建了三个appgoods负责商品、user负责用户、order负责订单按业务域划分目录代码不会越写越乱。Flask方案则轻量得多一个app.py就能起步再加几个蓝图模块化管理pip install flask flask-sqlalchemy flask-cors flask-jwt-extended蓝图的组织方式和Django的app有异曲同工之处只是要自己动手建目录。我习惯这样组织Flask项目models.py放数据库模型auth.py放用户认证相关路由product.py放商品相关路由order.py放订单相关路由app.py作为入口把蓝图注册进去。2.3 前端Vue项目搭建前端部分需要先装Node.js版本推荐16以上。装好之后全局安装vue/cli服务然后通过指令创建前端工程npm install -g vue/cli vue create mall-frontend cd mall-frontend npm install axios vue-router4 element-plus pinia创建工程的时候它会让你选预设配置新手建议选DefaultVue 3后面需要的依赖再手动逐个装。Element Plus是一套颜值和功能都在线的Vue 3组件库表格、表单、弹窗、分页这些东西都有现成的做管理系统类的页面尤其方便。项目骨架生成好后用npm run serve启动开发服务浏览器访问localhost:8080能看到默认欢迎页前端环境就算通了。3. 后端核心模块设计与实现3.1 数据库表结构设计商城项目的表结构直接决定了后面所有业务能不能顺畅跑起来。我设计的核心表包括用户表、商品分类表、商品表、购物车表、订单表、订单商品明细表。用户表在Django里可以直接继承AbstractUser类扩展出手机号和收货地址字段Flask没有这么方便需要自己建一个User模型密码字段用哈希保存。一个关键点是购物车和订单为什么要拆成多张表因为一个用户可以有多个订单一个订单对应多个商品购物车里一个用户也会放多个商品这些一对多和多对多关系必须靠关联表来承载。商品表里我特别强调三个字段价格要存整数或Decimal而非浮点数、库存要有默认值、商品状态要有上下架标记。价格这个很多人会犯错后面常见问题里我会详细说。订单状态我用整数来存储比如0-待付款、1-已付款、2-已发货、3-已签收、4-已取消。别直接存中文一是数据库冗余二是后面筛选统计还得做字符串匹配用整数加注释的形式最实用。3.2 用户注册登录与Token认证用户模块的核心是注册、登录、保持登录状态。Django自带auth体系非常完善配合DRF的TokenAuthentication或JWT方案都很轻松。我推荐用DRF的simplejwt库它是标准JSON Web Token实现登录后返回一个access_token和一个refresh_token前端存下来每次请求带上即可。代码大致长这样# users/views.py from rest_framework_simplejwt.views import TokenObtainPairView from rest_framework_simplejwt.serializers import TokenObtainPairSerializer class MyTokenObtainPairSerializer(TokenObtainPairSerializer): classmethod def get_token(cls, user): token super().get_token(user) token[username] user.username return token class MyTokenObtainPairView(TokenObtainPairView): serializer_class MyTokenObtainPairSerializer这里在token里塞了username前端就能直接从token拿到用户名不用每次查询数据库。注册接口则是接收用户名、密码、手机号创建用户时用set_password方法保存密码自动完成哈希加密。顺便提一嘴密码千万别明文存一旦数据库泄露所有用户账号信息就全完蛋了。Flask里我用flask-jwt-extended来生成和校验token思路完全一致只是API不同create_access_token做签发jwt_required()做校验。3.3 商品查询、筛选与排序接口商品列表接口是商城被调用最频繁的接口也是性能优化的重点。最基础的功能是分页返回所有商品DRESS上叫列表DRF里直接用视图集加分页类# goods/views.py from rest_framework import viewsets, filters from django_filters.rest_framework import DjangoFilterBackend from .models import Product from .serializers import ProductSerializer class ProductViewSet(viewsets.ModelViewSet): queryset Product.objects.filter(is_activeTrue) serializer_class ProductSerializer filter_backends [DjangoFilterBackend, filters.OrderingFilter] filterset_fields [category, brand] ordering_fields [price, sales]整个接口支持按分类、按品牌过滤还支持按价格或销量排序前端只需要在请求URL上加query参数就能实现比如/api/products/?category1ordering-price。DjangoFilterBackend帮我们把筛选逻辑全部封装好了这是纯手写列表接口完全比不了的效率。搜索我建议直接用DRF自带filters.SearchFilter它会生成SQL的LIKE查询对商品名、简介这类短文本足够用了。如果以后数据量到了百万级那再去考虑Elasticsearch或数据库全文索引现阶段没必要。3.4 购物车与下单业务流程购物车模块的核心是以用户为单位把选中的商品和数量一一记下来。购物车表可以简单设计成用户ID 商品ID 数量三个字段但要注意不能重复添加同一个用户对同一个商品只能有一条记录重复添加应该增加数量而不是新插一条。下单流程是整个项目里最考验逻辑的部分。用户从购物车勾选商品提交订单时后端要做的事情按顺序是校验商品是否存在且是上架状态、校验数量是否大于零、扣减库存、计算订单总金额、生成订单记录和订单商品明细、清空购物车中已下单的商品。扣库存这个动作必须放在事务里做否则一旦中途报错库存被扣了但订单没生成数据就出大问题了。事务的操作方式是开启一个数据库事务、执行完整流程、捕捉异常回滚把流程代码包在with transaction.atomic():块内Django和Flask-SQLAlchemy都能实现。另外一个细节是扣库存时最好加上条件判断用类似Product.objects.filter(idx, stock__gtenum).update(stockF(stock)-num)的方式先判断够不够不够就抛出异常终止下单这样能避免并发下单导致库存变为负数。4. 前端页面与核心交互实现4.1 页面结构与路由规划前端我分了两个大区域用户端和管理端。用户端首页展示商品列表和分类导航商品详情页展示图片和参数信息购物车页面管理已选商品结算页填写收货信息和提交订单个人中心有登录、注册、订单列表几个页面。管理端则是商品管理、订单管理、销售统计。路由的配置用Vue Router登录页、首页、商品详情、购物车这些一级路由直接列出来管理端统一挂在layout组件作为父路由子路由用children包起来。这里有个很实用的技巧管理端的所有页面都放在adminLayout这个组件里这个组件包含了侧边栏和顶部导航子页面通过router-view插进去整体布局就统一了。路由守卫也不要漏。登录用户才能访问的页面我加了一个前置守卫每次跳转先读token没有token直接跳转登录页登录过的用户访问登录页则重定向回首页。4.2 请求封装与响应拦截前端的axios请求一定要做统一封装这是很多人容易忽略的。我习惯建一个request.js文件设置baseURL指向后端API地址然后加两个拦截器请求拦截器用来挂载token从本地存储里把token取出放进请求头响应拦截器用来统一处理错误码比如token过期就跳转登录页。// api/request.js import axios from axios const request axios.create({ baseURL: http://127.0.0.1:8000/api, timeout: 5000 }) request.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers.Authorization Bearer ${token} } return config }) request.interceptors.response.use( response response.data, error { if (error.response.status 401) { localStorage.removeItem(token) window.location.href /login } return Promise.reject(error) } )这样写的好处是每个页面里只需调用request.get(products)不用重复处理token和错误代码清爽很多。4.3 商品列表与购物车联动商品列表页的数据加载逻辑是mounted生命周期里调用商品列表接口把返回的数组塞进items数组模板用v-for循环渲染卡片。筛选功能则监听分类变化重新请求对应分类的商品。Element Plus的el-pagination组件处理分页很顺手把当前页和总条数绑定好翻页就重新拉数据。购物车页面有个交互细节购物车里的商品数量增减应先在本地更新状态让用户感觉秒开然后异步把最新数量提交给后端。如果等请求回来再改界面响应延迟会让人觉得卡顿如果只改本地不同步后端刷新页面的瞬间数据就丢了。所以正确的做法是本地变、远程同步两步都要做。结算时把购物车里勾选中的商品ID列表传过去后端根据这些ID生成订单。注意传的是用户勾选的那部分商品而不是整个购物车很多人一开始都在这里翻车。4.4 管理端与销售数据展示管理端的商品管理表是el-table加表单弹窗的组合表格展示商品名、价格、库存、状态几列操作列有编辑和上下架按钮新增和编辑共用一个el-dialog弹出式表单提交后刷新表格数据。销售统计页面可以用ECharts来做折线图和柱状图后端提供一个统计接口按日期分组返回每天的订单数量和销售额。Django里用annotate加TruncDate就能按天聚合from django.db.models.functions import TruncDate from django.db.models import Sum, Count daily_sales Order.objects.filter(status1).annotate( dayTruncDate(created_at) ).values(day).annotate( total_amountSum(total_price), order_countCount(id) ).order_by(day)这个统计接口返回的数组直接塞给ECharts的series就行。这就是为什么我前面建议订单总额用Decimal存——聚合计算浮点数的精度误差在这个场景会让你抓狂。5. 常见问题与排查方法5.1 前后端跨域问题前端跑在8080端口后端跑在8000端口浏览器会因为跨域策略拦截请求这是前后端分离项目的第一个拦路虎。Django方案用django-cors-headers解决安装后在INSTALLED_APPS注册在MIDDLEWARE里添加上CorsMiddleware再设置CORS_ALLOW_ALL_ORIGINS True开发阶段即可。Flask方案就是flask-cors注册之后一行CORS(app)完事。等上线部署的时候再把白名单改成自己域名比如CORS_ALLOWED_ORIGINS只放行https://mall.example.com安全性和可用性都能兼顾。5.2 商品图片上传后访问404本地开发时商品图片上传到Media目录但页面访问不到九成是Django没配置媒体文件服务。需要在settings.py里加上MEDIA_URL和MEDIA_ROOT两个配置然后在项目的urls.py里手动添加静态路由from django.conf import settings from django.conf.urls.static import static urlpatterns static(settings.MEDIA_URL, document_rootsettings.MEDIA_ROOT)设置完以后上传的图片就会有固定可访问的URL前缀前端拼接字段就能显示。这个坑开发阶段几乎必踩提前配好在后面能省一晚上的调试时间。5.3 JWT过期后前端无感刷新JWT的access_token一般设较短有效期比如30分钟到2小时过期后前端请求会收到401。简单粗暴的做法是让用户重新登录但体验很差。我的处理方式是前端封装一个刷新机制收到401时调用refresh接口用refresh_token换新的access_token再重放刚才的请求。这个逻辑我放在响应拦截器里实现加一个队列存下失败请求刷新token成功后再重新发一次。注意refresh_token本身也过期时才真正跳转登录页。5.4 Vue打包后部署到Django/Flask服务开发完要部署前后端分离变成单体部署是常见做法。Vue项目先用npm run build打包生成dist目录里面是纯静态文件。Django需要找到这些文件在settings.py里配置静态文件目录指向dist目录然后在urls.py里加入一个兜底路由所有非/api开头的请求都返回index.html。首页路由配好之后访问站点根路径就能看到Vue前端/api/开头的请求由Django处理一个服务搞定全部。写在最后两个我吃了亏才明白的小细节第一个是金额字段的精度问题。浮点数的二进制表示天然有误差0.1 0.2算出来是0.30000000000000004直接作为金额存储会导致对账永远对不平。我吃过这个亏后来不管Django还是Flask的模型里价格字段统统用DecimalField或者Numeric类型显示和计算都用Decimal。如果你已经不小心用了Float趁项目早期赶紧改掉后期数据多了迁移就是个痛苦的过程。第二个是数据库索引。商品表的分类ID、订单表的用户ID、订单明细表的订单ID这些都是高频查询条件必须加上索引。Django模型里加db_indexTrueFlask-SQLAlchemy加indexTrue建表时就定义好。数据量小的时候感受不到差别等商品上万、订单上千后没有索引的查询会让你明显感觉到页面卡顿。电脑配件商城这个项目从头到尾做完你会把Python后端、Vue前端、数据库设计、接口调试、部署上线整个链条完整过一遍这个经验用在任何Web项目上都通用。如果照着这篇文章的思路走下来遇到问题也欢迎随时交流这类项目踩坑的点其实就那么几个提前知道比事后补救舒服多了。