Django+Vue前后端分离家居商城项目实战:从设计到部署全流程

📅 发布时间:2026/10/7 12:18:17
Django+Vue前后端分离家居商城项目实战:从设计到部署全流程
01 引言这个家居商城项目到底值不值得做如果你搜索到这个标题大概率已经在毕业设计选题列表里、或者是在简历项目栏规划里看到了“网上家居商城”这类需求。先说结论家居商城是Python Web全栈方向非常典型的实战项目它不像纯博客或CRUD管理系统那么单薄但又比完整的电商平台更容易落地是集中练习Django/Flask Vue前后端分离开发的绝佳载体。这个项目能解决的问题很实在你会在开发过程中完整体验到数据建模、接口设计、前端组件化、前后端联调、权限控制以及项目部署的全流程。对于刚学完Python基础但对Web开发没有整体概念的同学来说它的边界很清晰——不用去碰分布式、高并发那些暂时用不上的东西对于求职的同学来说一套功能完整的商城系统挂在简历和GitHub上也远比“仿某某官网”更有说服力。我在带项目实战时观察到大多数人倒在这个项目上并不是因为某个技术点太难而是不知道每一步该怎么走、为什么这么走。本文就是基于我用PyCharm Python 3.10 Django 4.x Vue 3完整搭过的一套家居商城系统的实践经验整理出来的。全文会先讲清楚选型思路和工程结构再带你把商品、购物车、订单这几个核心模块从前端到后端完整过一遍最后是踩坑实录和问题排查速查。你可以直接照着敲也能在关键决策点上换成Flask替代方案——两条路我都给到。02 整体设计为什么说家居商城比普通电商项目更适合练手2.1 家居品类的业务特性会逼你把数据模型想得更细很多新手一上来就把家居商城做成“通用电商模板”商品表、订单表、用户表一贴就完事。这其实是浪费了这个项目最大的学习价值。家居商品有几个特殊性如果你的数据模型没有体现出来项目在面试官或答辩老师那里会很吃亏大件属性明显沙发、床垫的体积和重量会影响运费计算和配送方式这和卖手机、卖图书是完全不同的业务逻辑。多SKU构成一套餐桌可能包含“一桌四椅”的配置选项同款沙发有不同的颜色、尺寸、面料材质需要在商品详情页通过规格组合刷新价格和库存。分类维度丰富家居商品通常需要两级甚至三级分类比如“卧室家具 床 双人床”而且一个商品可以挂到多个场景分类下“现代简约”和“小户型推荐”。所以在设计阶段我建议你至少在模型层面使用商品品牌表 商品分类表 商品SPU表 商品SKU表的四层结构。如果嫌复杂最低也要做到分类表 商品表 商品规格表。查阅大量网上的家居商城源码你会发现那些看起来功能一致的项目拉开数据表一看就能分出高下区别就在于建模的人有没有考虑过真实业务场景。这是这个项目比“图书管理系统”练手价值高出一截也是我优先推荐你仔细花时间设计好表结构的原因。好的表结构能让后续的查询逻辑和接口设计都自然很多。2.2 Django和Flask二选一我到底该怎么站队看到标题里“django-flask”连在一起说明你也正在纠结。按我的经验不用浪费超过半小时在这种选择上理清下面这个决策逻辑就行如果目标是以最少代码完成最多功能比如一个人要做后台管理界面、要做用户认证、要做数据库迁移选Django。它自带Admin后台、ORM、认证系统这些在Flask里全都要自己搭或者接第三方库。一个复杂的商城项目Django的起步速度会比Flask快两到三倍。如果目标是练习手写能力、追求灵活的接口设计或者之后打算转向FastAPI这类轻量框架的生态选Flask。它没有框架层面的约束让你能看到一个Web应用从底层是如何被组装起来的对理解HTTP请求-响应链路非常有帮助。对比项DjangoFlask上手曲线偏陡概念多但成体系平缓简单直接但多数组件需自行集成开发速度快自带Admin、Auth、ORM慢需选用第三方扩展并手动配置适合场景大型管理系统、内容站点、电商小型API服务、原型验证、学习底层中文资料量资料极多资料多但高深内容需要自行翻源码我用Django做主线是因为这套项目会自动替你解决掉很多“非业务”但很耗时的基础设施问题把精力留给真正的业务逻辑。如果你需要展示自己代码的“含金量”可以在答辩或面试时主动聊“我用Django的ORM做了哪些复杂的关联查询”或者反过来“我知道Flask有它的优势但这类项目里Django的选择让开发效率更高”而不是泛泛而谈“我用了某某框架”。选择本身不扣分能否说清为什么才加分。2.3 前后端分离时Vue到底负责哪些事如果这套系统用Django的模板引擎Template渲染能用更少的代码做出界面但那会把页面交互逻辑和JavaScript代码全部混在后端文件里项目一旦变大就非常难维护。采用Vue作为独立前端工程后前后端的界限非常清晰Vue工程只负责UI渲染和用户交互它通过HTTP接口向后端要数据。Django只负责提供JSON数据处理业务规则不再关心页面长什么样。开发阶段是“两个独立项目”并行跑一个在 localhost:8000一个在 localhost:5173通过代理和跨域配置互相沟通部署时可以再合并或分开部署。Vue 3的Composition API配合script setup语法写业务逻辑比Vue 2的Options API直观很多尤其是购物车数量变化、商品规格切换这类交互状态用ref和computed管理起来更自然。你完全不用去背那些生命周期钩子真正用到的就那么几个。这套项目里Vue的角色是“前端中枢”负责从登录注册到下单支付的全部页面逻辑因此它也被赋予了承上启下的职责上接用户行为下达后端接口。接下来就进入实操环节照着做你就能跑起来。03 开发环境与工程初始化PyCharm里从零到双端启动3.1 Python环境和PyCharm配置的三个关键设置在反复重装环境之后我建议你按照下面这套顺序操作能规避80%的环境问题Python版本装3.10或3.11不建议装最新的3.12。原因很简单Django、DRF以及很多第三方库的稳定版本在3.10/3.11上经过的验证最充分我在3.12上遇到过某些依赖库的编译报错排查起来非常头疼。Python装好后把安装目录和Scripts子目录加入系统PATH。PyCharm里为每个项目创建独立的虚拟环境。用PyCharm新建Django项目时选择“New environment using Virtualenv”Python解释器指到刚才安装的Python版本。独立的虚拟环境能防止项目之间依赖冲突这是新手最容易忽略的操作他图省事在base环境装包结果某天升级一个包把另一个项目搞崩了都不知道原因。配置三件套File Settings里打开 Project: xxx Python Interpreter确认当前环境是刚才创建的虚拟环境然后 Settings Tools Terminal把Shell path设置成本机的cmdWindows上默认PowerShell有时环境变量加载不全最后在Settings Plugins里装好Vue.js插件用于前端代码高亮和中文语言包可选项纯粹省心。3.2 创建后端Django工程目录划分与配置修改打开PyCharm终端确保终端左侧小括号里显示你的虚拟环境名执行以下命令pip install django djangorestframework django-cors-headers pillow django-admin startproject furniture_mall . python manage.py startapp goods python manage.py startapp carts python manage.py startapp orders python manage.py startapp users在项目根目录生成工程文件后立即打开settings.py做五件事在INSTALLED_APPS里添加rest_framework、corsheaders、users、goods、carts、orders。在MIDDLEWARE里将corsheaders.middleware.CorsMiddleware放到最顶部位置位置放错会导致跨域配置不生效。添加CORS_ALLOWED_ORIGINS [http://localhost:5173]这是开发阶段让Vue前端能访问后端的配置。设置AUTH_USER_MODEL users.User用自己扩展的用户表替换默认表。修改LANGUAGE_CODE zh-hans和时间区TIME_ZONE Asia/Shanghai避免后面时间字段出现8小时偏差。为什么创建多个app而不是全写在一个app里因为Django的app天然对应业务模块边界。把商品、购物车、订单、用户分开管理既能让代码结构清晰也能防止项目变大后改一处牵一发而动全身。做完这步执行python manage.py migrate初始化数据库。3.3 创建前端Vue工程版本选择和目录结构准备在项目根目录下再开一个终端不用虚拟环境执行npm create vuelatest frontend cd frontend npm install npm install axios element-plus npm run devnpm create vue是Vue官方推荐的脚手架创建方式。创建过程中会问是否安装Vue Router、Pinia等这里全部选择“Yes”商城系统一定会用到路由和全局状态管理后期自己装徒增麻烦。创建完毕后我习惯把src下的目录整理成这个样子src/ |-- api/ // 所有后端接口调用封装 |-- assets/ // 静态资源 |-- components/ // 公共组件商品卡片、分页、导航栏 |-- router/ // 路由表配置 |-- stores/ // Pinia全局状态购物车、用户信息 |-- views/ // 页面级组件首页、商品列表、详情、购物车、结算这个结构看起来稀松平常但它的价值在于把网络请求从组件里剥离出来了。如果你的每个组件里都直接写axios.get(...)项目改到后期几乎无法维护。你需要在项目一开始就按这种“请求统一管理”风格来写而不是等接口多了再重构。04 后端核心模块实现用Django快速打磨商品、购物车与订单4.1 家居商品的数据模型设计从表结构开始把控核心在goods/models.py里建立分类表和商品表这是我多次调整后的精炼版本既能体现家居特性又不过度设计class Category(models.Model): name models.CharField(max_length50, verbose_name分类名称) parent models.ForeignKey(self, nullTrue, blankTrue, on_deletemodels.CASCADE, related_namechildren, verbose_name父分类) class Meta: verbose_name 商品分类 verbose_name_plural verbose_name class Brand(models.Model): name models.CharField(max_length50, verbose_name品牌名称) logo models.ImageField(upload_tobrands/, nullTrue, blankTrue, verbose_name品牌Logo) class SKU(models.Model): name models.CharField(max_length100, verbose_nameSKU名称) category models.ForeignKey(Category, on_deletemodels.PROTECT, related_nameskus, verbose_name分类) brand models.ForeignKey(Brand, on_deletemodels.PROTECT, related_nameskus, verbose_name品牌) specs models.JSONField(defaultdict, verbose_name规格参数, help_text例如{颜色:胡桃木,尺寸:1.8m}) price models.DecimalField(max_digits10, decimal_places2, verbose_name单价) stock models.IntegerField(default0, verbose_name库存) sales models.IntegerField(default0, verbose_name销量) main_image models.ImageField(upload_togoods/, verbose_name主图) images models.JSONField(defaultlist, verbose_name轮播图列表) detail models.TextField(blankTrue, verbose_name商品详情) is_on_sale models.BooleanField(defaultTrue, verbose_name是否上架) created_at models.DateTimeField(auto_now_addTrue, verbose_name创建时间)有几个细节要重点解释specs用JSONField而不是建单独的规格表是因为家居商品的规格维度差异太大床有“长宽高”和“材质”灯具有“色温”和“光源类型”用规范的ORM去约束不存在的字段反而笨拙。JSONField用起来灵活查询时通过specs__contains就能做筛选。on_deletemodels.PROTECT比CASCADE安全得多。分类或品牌如果有商品挂靠删除时会抛出异常这在真实业务里是正确的行为不然一个误操作会把整个商品链路上的数据都带没。价格一定要用DecimalField而不是FloatField浮点数在价格计算时会有二进制精度问题出现0.10.2 ! 0.3的情况商城要做到分毫不差这个坑从一开始就避开。4.2 DRF序列化器与视图把商品接口做成标准样板在goods/serializers.py里写序列化器class CategorySerializer(serializers.ModelSerializer): children serializers.SerializerMethodField() class Meta: model Category fields [id, name, children] def get_children(self, obj): children obj.children.all() return CategorySerializer(children, manyTrue).data if children else [] class SKUSerializer(serializers.ModelSerializer): category_name serializers.CharField(sourcecategory.name, read_onlyTrue) brand_name serializers.CharField(sourcebrand.name, read_onlyTrue) class Meta: model SKU fields [id, name, category_name, brand_name, specs, price, stock, sales, main_image, images, detail]SerializerMethodField是DRF里很常用的自定义字段写法它允许你不直接读取模型属性而是通过一个get_xxx方法计算出最终返回值。在这里它帮我们做成了带层级嵌套的分类树前端拿到后可以直接渲染多级联动菜单而不用自己循环遍历几乎所有节点。在goods/views.py里我推荐用DRF的ReadOnlyModelViewSet写商品接口class SKUViewSet(ReadOnlyModelViewSet): queryset SKU.objects.filter(is_on_saleTrue).select_related(category, brand) serializer_class SKUSerializer filterset_fields [category_id, brand_id] search_fields [name] ordering_fields [price, sales, created_at]配合引用django-filter并进行对应配置Django会为你自动实现分页、排序、按分类筛选、按关键词搜索。也许你会觉得过于“魔法”但你应该消除这种顾虑利用现成工具加速是Web开发的常态。你再看路由配置router DefaultRouter() router.register(skus, SKUViewSet, basenamesku) urlpatterns [path(api/, include(router.urls))]一条router.register就能把“列表页、详情页、过滤、排序、分页”全部暴露成标准RESTful接口这个面积只有十行代码却是整个商城系统后端最核心的骨架。4.3 购物车与订单状态机和事务保证不让数据出错购物车我建议用服务层的写法把核心判断逻辑从视图中抽离出来因为购物车变动频率最高且最容易出并发问题class CartService: staticmethod def add_to_cart(user, sku_id, quantity): sku SKU.objects.select_for_update().get(idsku_id) if sku.stock quantity: raise ValidationError({detail: 库存不足}) cart_item, created CartItem.objects.select_for_update().get_or_create( useruser, skusku, defaults{quantity: quantity} ) if not created: cart_item.quantity quantity cart_item.save() return cart_itemselect_for_update()是Django的悲观锁写法它会在数据库层面锁定选中的行直到事务结束。原因很直白如果两个人同时买同一个SKU他们同时读到库存都是10同时扣减成9最终实际售出数量会比库存多。商城类系统的强一致场景必须加锁。订单模块的核心是三张表订单主表保存收货地址、总金额、状态、订单商品表保存下单时商品的快照、订单状态日志表。为什么订单商品表要保存“快照”因为SKU表里的价格和名称随时可能改商城必须保证你下单时看到的1000元在订单详情里也是1000元而不是等你收货时变成了1250元。下单的执行过程应包裹在transaction.atomic()里任何一步失败则整个事务回滚with transaction.atomic(): cart_items CartItem.objects.select_for_update().filter(useruser) order Order.objects.create(useruser, total_amounttotal, ...) for item in cart_items: OrderGoods.objects.create(orderorder, skuitem.sku, priceitem.sku.price, ...) item.sku.stock - item.quantity item.sku.sales item.quantity item.sku.save() cart_items.delete()这一步把“创建订单、扣减库存、增加销量、清空购物车”四个操作打包成一个不可分割的整体彻底防止边角条件下购物车已清空但订单未生成、或库存扣了但订单失败的脏数据出现。关于订单状态机用整数字段维护即可10待付款 - 20待发货 - 30待收货 - 40已完成用户可执行“取消订单”管理员可执行“发货”每个状态的迁移都做合法性校验。4.4 用户认证与权限控制用户模块用Django自带的AbstractUser进行扩展添加手机号和头像字段。注册接口校验手机号格式、密码长度等登录接口使用Django内置的authenticate验证用户名密码。如果想做“手机验证码登录”或“JWT无状态登录”djangorestframework-simplejwt是标配配置起来也不复杂。权限控制方面添加购物车、结算下单的接口必须要求用户登录用DRF的IsAuthenticated加装饰器即可class AddCartView(APIView): permission_classes [IsAuthenticated] def post(self, request): user request.user ...05 Vue前端开发与联调从首页渲染到购物车交互5.1 路由页面与底部导航框架搭建前端路由的页面级结构如下/ - 首页商品推荐 分类入口 /category/:id - 分类商品列表支持筛选、排序、分页 /goods/:id - 商品详情轮播图 规格选择 加入购物车 /cart - 购物车页面 /order/confirm - 订单确认页 /order/list - 订单列表 /login - 登录页在router/index.js里用createWebHistory去掉URL里的#配合Vue Router的懒加载语法{ path: /goods/:id, name: GoodsDetail, component: () import(../views/GoodsDetail.vue), }懒加载的好处是首屏不用一次性把几十个页面JS全下载下来按需加载能明显提升打开速度。这一步是商城这种多页面系统的天然需求也应该在开始写页面时就顺手做好。5.2 统一API请求封装Axios实例是前端工程核心工程能力前端最忌每个页面各自写一个axios.get(url)URL散落在各组件里根本无法管理。我的规范做法是在src/api/http.js创建一个Axios实例import axios from axios import { ElMessage } from element-plus import router from /router import { useUserStore } from /stores/user const http axios.create({ baseURL: http://localhost:8000/api, timeout: 10000, }) http.interceptors.request.use(config { const userStore useUserStore() if (userStore.token) { config.headers.Authorization Bearer ${userStore.token} } return config }) http.interceptors.response.use( response response.data, error { if (error.response?.status 401) { ElMessage.error(登录状态已过期请重新登录) router.push(/login) } else { ElMessage.error(error.response?.data?.detail || 网络异常请稍后重试) } return Promise.reject(error) } ) export default http请求拦截器负责自动携带令牌响应拦截器负责统一处理错误码和异常提示页面里不需要反复写着try...catch和错误弹窗。定义API时按模块拆分// api/goods.js import http from ./http export const getGoodsList (params) http.get(/skus/, { params }) export const getGoodsDetail (id) http.get(/skus/${id}/)组件里只需要import { getGoodsList } from /api/goods一切都十分清爽。这套模式在团队开发里几乎是标配你如果自己写商城没有做这层封装后期联调时接口改了十几处逐个改组件里的URL你会非常后悔。5.3 商品详情页交互实现规格切换与加入购物车商品详情页是前端复杂度最高的部分它是这样运作的页面加载时从路由参数拿到商品id调用getGoodsDetail(id)获取商品信息渲染轮播图和价格。规格区展示sku.specs里的JSON对象用Object.keys()遍历出规格名用循环渲染出规格值按钮。用户点击某个规格值时高亮选中。同一商品的不同规格实际是多个SKU。详情页进入时拿到的SKU列表里包含规格组合用户点击具体规格组合后切换展示对应SKU的价格、库存和主图。规格切换的核心是维护一个selectedSpecs响应式对象当所有规格维度都选好时通过比较逻辑找到对应的SKUconst selectedSpecs ref({}) const matchedSKU computed(() { return skuList.value.find(sku { return Object.entries(selectedSpecs.value).every(([key, value]) sku.specs[key] value) }) })加入购物车按钮把当前匹配的SKU的id和数量提交给后端const addToCart async () { if (!matchedSKU.value) { ElMessage.warning(请选择完整的规格) return } await addCartItem({ sku_id: matchedSKU.value.id, quantity: quantity.value }) ElMessage.success(已加入购物车) }5.4 购物车页面与全局状态管理购物车页面用Pinia在全局维护购物车中的商品列表和总价。用户加入购物车之后数据存在后端刷新页面不丢前端拿到购物车数据后存在Pinia中页面内切换、勾选、增减数量时所有交互都能即时响应。购物车里勾选某几项结算总价是实时计算的const selectedItems ref([]) const totalPrice computed(() { return cartItems.value .filter(item selectedItems.value.includes(item.id)) .reduce((sum, item) sum item.price * item.quantity, 0) })一个值得你注意的细节购物车接口返回的价格要不要信任前端展示的商品单价来自后端接口下单时后端会重新校验一遍价格。你在开发时务必在后端订单创建逻辑里重新计算金额前端传过来的金额一律不信任。因为前端传参是可以通过开发者工具任意篡改的商城系统的任何金额计算都必须在后端完成这既是安全要求也是业务底线。06 后台管理与线上部署要做的几件事6.1 用Django Admin搭建简单的管理后台Django自带的Admin界面几乎是这个项目里最被低估的生产力工具。只需要在admin.py里注册模型admin.register(SKU) class SKUAdmin(admin.ModelAdmin): list_display [name, category, price, stock, is_on_sale] list_filter [category, brand, is_on_sale] search_fields [name] list_editable [price, stock, is_on_sale]商品管理就相当于免费获得了一个能改价格、能上下架、能搜索过滤的后台。管理员只需要登录/admin/路径就能维护商品数据省去从前端写管理页面的巨大工作量。这一功能对一个商城系统的完整度来说帮助比想象中大得多。6.2 部署到云服务器从开发机到生产环境的收敛开发阶段你开了两个服务前端5173端口、后端8000端口。部署时切换为生产模式前端执行npm run build生成dist静态文件目录。Django后端设置DEBUGFalse配置好ALLOWED_HOSTS再用collectstatic收集静态文件。将前端dist目录里的所有文件交给Nginx托管同时配置Nginx把/api/路径反向代理到Django后端的8000端口。数据库从SQLite切换到MySQL或PostgreSQL在settings.py里修改数据库配置。用gunicorn运行Django应用gunicorn furniture_mall.wsgi:application -w 4 -b 127.0.0.1:8000Nginx的关键配置片段server { listen 80; server_name your_domain.com; location / { root /var/www/furniture_mall/frontend/dist; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location /media/ { alias /var/www/furniture_mall/media/; } }try_files这个配置专为Vue Router的历史模式设计它保证当用户直接访问某个深层路由例如/goods/3时Nginx会把请求回退到index.html再由前端路由接管否则刷新页面会出现404。这是部署阶段最常见的坑十个人里至少有六个人会踩到。07 运行测试、问题排查与全项目速查表7.1 测试用例怎么写得实用商城项目最少要覆盖三个核心用例商品接口返回的列表包含所有字段且数量正确。未登录状态添加购物车返回401登录后返回200。库存不足时下单返回错误提示且不生成订单。这里给一个Django单元测试的示例from rest_framework.test import APITestCase from django.contrib.auth import get_user_model from goods.models import SKU class OrderTests(APITestCase): def setUp(self): self.user get_user_model().objects.create_user(usernametest, passwordtest123456) self.client.force_authenticate(self.user) self.sku SKU.objects.create(name单人沙发, price1999, stock1) def test_create_order_success(self): resp self.client.post(/api/orders/, {sku_id: self.sku.id, quantity: 1}, formatjson) self.assertEqual(resp.status_code, 201) def test_create_order_fail_when_stock_not_enough(self): resp self.client.post(/api/orders/, {sku_id: self.sku.id, quantity: 99}, formatjson) self.assertEqual(resp.status_code, 400) self.assertEqual(Order.objects.count(), 0)force_authenticate帮你越过登录直接以指定用户身份发起请求。无需重复分析登录逻辑这种模拟手段可以让你测试真正要验证的业务。7.2 从环境到联调的常见报错速查表我把这个项目开发中反复出现的问题整理成一张表你遇到问题直接对号入座报错/现象原因解决方案ModuleNotFoundError: No module named django当前使用的解释器不在项目虚拟环境内在PyCharm右下角切换解释器到venv环境connection refused 8000Django后端没启动或启动后端口冲突检查8000端口占用netstat -anoblocked by CORS policyDjango未安装corsheaders或中间件位置不对重新检查corsheaders中间件是否在列表顶部以及CORS_ALLOWED_ORIGINS是否包含http://localhost:5173Failed to load resource: 404请求接口路径写错或Nginx的try_files未生效在前端Network面板确认真实请求URL核对后端路由表或Nginx配置前端显示中文全部变成乱码数据返回时编码不一致在Djangosettings.py设置DEFAULT_CHARSET为utf-8确认数据库字符集为utf8mb4migration时报重复字段修改模型后未生成对应迁移文件执行python manage.py makemigrations后再migrate图片上传后访问403/404MEDIA_URL和MEDIA_ROOT配置问题settings.py里配好两处并在根URL中添加 static(settings.MEDIA_URL, document_rootsettings.MEDIA_ROOT)微信/支付宝/模拟支付回调收不到第三方接口或调试工具回调地址设置错误本地开发用内网穿透工具或直接代码里模拟支付回调提高调试效率7.3 我用这个项目练出来的几条实操心得讲几个只有做过一遍才会注意到的点你记下来可以少走很多弯路第一购物车和订单的联调一定要先定好接口契约。约定字段名用sku_id还是skuId、时间格式用yyyy-MM-dd HH:mm:ss还是ISO格式、分页参数用page还是pageNum前后端文档先写清楚。前后端分离项目80%的调试时间都浪费在“字段对不上”“类型不对”这种琐碎但致命的问题上。前端页面还没写的时候就先拿Postman把接口请求一遍确认字段和状态码。第二开发时要把Django Admin利用起来而不是手动往数据库插商品。用Admin维护第一批测试数据你会自然理解一个管理后台的字段设计逻辑。等以后写后台管理页面时直接参考这套模型的注册规则。第三Nginx部署时先改后端再改前端。不要指望一把梭重构完所有配置很容易绕晕。先把后端在本地用gunicorn跑起来测试/api/再把前端构建产物交给Nginx最后配代理。08 这个项目的边界与扩展方向如果你完整跟到这一步你已经拥有一套能运行、有测试、能部署的网上家居商城系统。接下来可以有三个方向继续做深搜索能力增强把SQLite自带的LIKE模糊查询替换为基于分词的中文搜索能力或接入Elasticsearch。这会明显拉开项目和其他普通商城的差距。订单状态完善加入“七天无理由退款”流程在订单状态机中增加售后状态分支这部分能体现出很强的业务建模能力。模块化拆分将商品模块升级为可插拔的应用把图片统一迁移到OSS对象存储文件的存储策略从本地磁盘替换为云存储。我个人在实际带项目的过程中最大的体会是这个项目能不能做出彩决定因素不是用了多少新技术而是你有没有把每个模块的边界想清楚。前端负责交互体验后端负责数据与规则安全中间用清晰的API串起来。Django Vue这对组合虽然不是最炫的但它足够稳、资料足够多、练习价值足够充分你踩过的每一个坑在真实岗位里大概率还会遇到这就是做这个项目最大的回报。最后再分享一个小技巧开发时把Django的runserver和Vue的npm run dev分别放在PyCharm的两个独立终端里各占半个屏幕。一旦接口报错一边看后端日志一边看前端Network面板定位问题至少能快一倍。建议用两个浏览器窗口分别调试前端页面和Admin后台逻辑更加清晰不受干扰。做到这些你已经比大多数来找我问“这个项目怎么做”的同学领先一大截了剩下的就是把代码写出来跑起来。