news 2026/10/9 9:02:43

Python+Vue前后端分离搭建婴幼儿用品销售网站实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python+Vue前后端分离搭建婴幼儿用品销售网站实战

做婴幼儿用品销售网站,是目前很多学Python的朋友喜欢拿来练手的一个项目,刚好能把Python、Vue、Pycharm、Django/Flask这一整套技术串起来。我之前带毕业设计的时候,遇到过不少同学问:后端到底用Django还是Flask?前端为什么要配Vue?从零开始怎么在Pycharm里面把一个前后端分离的商城系统跑起来?这篇文章我就从实际开发的角度,把婴幼儿用品销售网站的完整搭建过程、技术选型、核心代码和踩坑记录都整理出来,希望对正在做类似项目的你有帮助。我会尽量跳过废话,直接讲能落地的内容,所有步骤都是我实际跑过、调过、翻过车的经验。

1. 项目定位与技术选型

1.1 婴幼儿用品网站到底需要哪些功能

先别急着写代码,任何项目第一步都是把需求理清楚。婴幼儿用品销售网站虽然叫“商城”,但它的边界比普通电商更清晰。我梳理下来,核心模块一般就这几个:

  • 用户端:注册、登录、退出、商品按分类浏览、关键词搜索、商品详情、购物车、下单、模拟支付、个人订单列表。
  • 管理端(后台):商品分类管理、商品增删改查、库存管理、订单状态管理、用户管理。

如果做毕业设计,后台可以直接用Django原生Admin,省去不少工作量;如果想体现技术点,就自己写一套Vue管理页面。我的建议是:前端用Vue做用户端,后台先用Django Admin撑着,优先把商城的主流程跑通,后面还有时间再美化后台。

数据库设计也不复杂,核心表就六张:用户表、分类表、商品表、购物车表、订单表、订单明细表。婴幼儿用品有一个特殊点:商品信息里往往要标注适用月龄,比如“0-6个月”“6-12个月”,这个字段在设计商品表时一定要预留,后面做筛选会很好用。

1.2 Django和Flask怎么选更合适

很多人在Django和Flask之间纠结,其实这两者的选型逻辑非常简单:如果你想快速做出一个自带后台管理、ORM、认证体系的完整网站,选Django;如果你只想写几个JSON接口、想完全自己掌控代码结构,选Flask。放到婴幼儿用品销售网站这个场景里,我比较推荐Django,因为商城类项目天然需要用户管理、数据建模、后台维护,这些Django都内置了。

下面是两条路线的对比,都基于我实际用下来的感受:

对比项DjangoFlask
ORM自带,功能完整,支持迁移需要另装SQLAlchemy,配置多一点
Admin后台自带,改一改就能用没有原生后台,需要自己写
用户认证自带User模型和登录会话需要Flask-Login
序列化API配合DRF非常好用手动返回JSON,或用Flask-RESTful
学习曲线稍陡,但解决的是完整套路平缓,但到后期很多功能要自己拼
典型场景内容管理、电商、内部系统轻量服务、小工具、算法演示

如果你特别想用Flask,也没问题,后面的数据库设计和接口思路完全通用。只是注册登录、后台管理这些需要自己多写一些代码。其实这两套我都跑过,Flask写出来的代码结构更“自由”,但自由也意味着你要有足够的架构能力;Django则是“该做的都帮你做好了”,只要按它的规矩走,基本不踩大坑。

1.3 前端为什么选Vue而不是服务端模板

用Django也可以直接渲染HTML模板,但那样的话,商品列表每次刷新都要整个页面重新加载,体验比较老式。Vue的核心价值在于:它把前端变成了一个“单页应用”,用户点击切换分类时,只更新页面局部数据,不用刷新整页。这种交互方式对购物场景非常重要,用户加购物车、看商品详情、切换分类都会顺畅很多。

热词里总有“vue路由”“vue插槽”“vue指令简写”,说明大家在前端这层的问题比较集中。Vue的路由用来控制页面跳转,插槽用来复用组件结构,指令简写则是写着方便,比如 v-on 写成 @,v-bind 写成 :。这些内容我会在第3章结合页面实现一起讲。

用Vue做前端,一般走前后端分离:Vue负责页面和交互,Python负责提供JSON数据接口。这样分工清楚,后面部署时前端打包成静态文件,后端用Nginx反代,整体结构也很舒服。

2. 开发环境准备与项目初始化

2.1 Python、Pycharm、Node环境的安装要点

很多新手卡在环境这一步,其实学会看版本号就能解决大部分问题。我用的是Python 3.10,Pycharm 2024社区版,Node 18以上。Python和Pycharm的安装教程网上很多,我只提醒三个容易出问题的点:

  • 安装Python时一定要勾选“Add Python to PATH”,否则后面在命令行敲python会提示找不到命令。
  • Pycharm创建新项目时,要选“New environment using Virtualenv”,让每个项目独立的包环境,避免和全局环境冲突。
  • Node安装后要注意npm的镜像源,国内环境不配置镜像源的话,npm install能卡到你怀疑人生。

配置npm镜像源只需要一行命令:

npm config set registry https://registry.npmmirror.com

Vue项目初始化我更喜欢用Vite,因为比老版的Vue CLI快很多。刚才提到的热词里有一个“vue安装依赖”,指的就是在项目根目录执行npm install,这一步会把项目需要的所有前端包都装进来。装完之后,用Pycharm打开后端项目,再用VS Code或Pycharm的“Open”打开前端文件夹,两个项目各自独立,但都在一个总目录下,方便管理。

2.2 创建Django后端项目并完成基础配置

后端项目我建议用命令行创建,Pycharm只是用来编辑和调试。先新建一个总目录,然后打开命令行:

pip install django djangorestframework django-cors-headers pillow django-admin startproject momshop cd momshop python manage.py startapp goods python manage.py startapp users python manage.py startapp orders

我把功能拆成了三个app:goods管商品和分类,users管用户,orders管购物车和订单。Django的app概念可以理解成“功能模块”,它不是什么高深的东西,就是让同类代码放到同一个文件夹里。

创建完之后,把rest_framework、corsheaders和我们新建的users、goods、orders都注册到settings.py的INSTALLED_APPS里。同时配置数据库。练手阶段用SQLite最省事,因为不需要额外装数据库服务,但真正要部署上线建议换MySQL。配置项大概是这样的:

DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': BASE_DIR / 'db.sqlite3', } } MIDDLEWARE = [ 'corsheaders.middleware.CorsMiddleware', 'django.middleware.common.CommonMiddleware', # ... 其他默认中间件 ] CORS_ALLOW_ALL_ORIGINS = True

CORS_ALLOW_ALL_ORIGINS是开发阶段用来解决跨域问题的临时手段,前后端分离时,前端跑在5173端口,后端跑在8000端口,端口不同浏览器就会拦截跨域请求,加上这个配置可以快速放行。上线前再改成白名单模式。

2.3 创建Vue前端项目并安装基础依赖

前端我用Vite创建一个Vue3项目,命令如下:

npm create vue@latest shopweb

创建过程中会问你需不需要TypeScript、Router、Pinia等,我建议全部选“Yes”。TypeScript现在已经是主流,一开始可能不适应,但后面能帮你避免很多数据格式错误。Router是路由库,Pinia是状态管理库,商城项目一定用得上。

创建完成后,进入目录安装axios,用于发AJAX请求:

cd shopweb npm install npm install axios

跑起来之后,Vite默认端口是5173,浏览器打开就能看到Vue雏形。这时候前后端两个项目都跑起来了:Django在8000,Vue在5173。接下来要做的就是把它们通过接口联通。

有一个热词是“failed to load tsconfig”,这个问题在Vite创建Vue项目时很容易出现,通常是因为@vue/tsconfig包没有正确安装。解决办法是删掉node_modules和package-lock.json,重新执行npm install。如果还报错,就把tsconfig.json里的extends路径改成相对路径,或者把该包单独执行一次npm install -D @vue/tsconfig。

3. 数据库设计与核心接口实现

3.1 数据表设计:从需求到字段

数据库设计是整个项目的地基,我见过不少项目写到一半开始改表,改到后面连带接口全乱。所以我习惯先用一张表把字段固定下来。

用户表可以直接继承Django的AbstractUser,加上手机号和收货地址:

from django.contrib.auth.models import AbstractUser class User(AbstractUser): phone = models.CharField(max_length=11, blank=True) address = models.CharField(max_length=255, blank=True) class Meta: db_table = 'user'

分类表很简单,就是分类名称和排序权重。商品表是最核心的,除了基础字段,要特别注意stock(库存)和suitable_age(适用月龄)这两个字段:

class Category(models.Model): name = models.CharField(max_length=50) order = models.IntegerField(default=0) class Meta: db_table = 'category' class Product(models.Model): category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='products') name = models.CharField(max_length=100) price = models.DecimalField(max_digits=8, decimal_places=2) stock = models.IntegerField() suitable_age = models.CharField(max_length=50, blank=True) image = models.ImageField(upload_to='products/', blank=True, null=True) description = models.TextField(blank=True) created_at = models.DateTimeField(auto_now_add=True) class Meta: db_table = 'product'

购物车表和订单表是关联关系最强的。购物车每个条目关联一个用户和一个商品,订单关联用户且一对多到订单明细。需要注意的是:订单金额应该把总价也存进去,因为商品价格将来可能变动,订单历史金额不能被“联动”修改。这是电商系统一个很经典的冗余设计。

订单删除操作在Django里使用delete()方法,比如order.delete()。热词里那个“django执行查询-删除对象”实际就指模型实例调用delete(),或者用objects.filter(...).delete()做批量删除。这个操作是不可逆的,我在开发环境里吃过亏,后来养成了“删除前先打印查询集条数”的习惯。

3.2 后端接口设计与实现

我把接口风格统一成REST风格,用Django REST framework(DRF)来写。为什么要用DRF?因为它把JSON序列化、字段校验、路由注册都简化了,比手动写JsonResponse干净太多。

比如商品列表接口,只需要一个视图集:

from rest_framework import viewsets from .models import Product from .serializers import ProductSerializer class ProductViewSet(viewsets.ModelViewSet): queryset = Product.objects.all().order_by('-created_at') serializer_class = ProductSerializer

然后在urls.py里注册:

from rest_framework.routers import DefaultRouter router = DefaultRouter() router.register('products', ProductViewSet) urlpatterns = [ path('api/', include(router.urls)), ]

这样一个接口就能同时支持商品列表、商品详情、新增、修改、删除等多个操作,非常省事。DRF还自带了可视化调试页面,浏览器访问/api/products/可以直接看到数据,对于调试接口比手动请求方便得多。

用户注册登录稍微涉及一点Django认证机制。注册时不要直接明文存密码,Django自带create_user()会自动加密密码。登录我们采用JWT方式,整个流程是:前端提交用户名密码,后端验证成功后签一个token返回给前端,前端保存token,之后每次请求都在Header里带上Authorization: Bearer 你的token。

代码示例:

from rest_framework import status from rest_framework.decorators import api_view from rest_framework.response import Response from django.contrib.auth import authenticate from rest_framework_simplejwt.tokens import RefreshToken @api_view(['POST']) def login(request): username = request.data.get('username') password = request.data.get('password') user = authenticate(username=username, password=password) if user is not None: refresh = RefreshToken.for_user(user) return Response({ 'access': str(refresh.access_token), 'refresh': str(refresh), }) return Response({'detail': '密码错误'}, status=status.HTTP_400_BAD_REQUEST)

如果你走Flask路线,登录认证同样可以用JWT,只是框架不同,逻辑完全一致。Flask加SQLAlchemy的模型写法虽然和Django不同,但数据表设计和接口设计思路可以直接复用。

3.3 Vue页面与接口对接

前端这边,我需要做的页面包括:首页商品列表、商品详情页、分类筛选、购物车、结算页、登录注册页。Vue Router负责这些页面之间的跳转,比如首页点商品卡片跳转到详情页,配置方式如下:

const routes = [ { path: '/', component: Home }, { path: '/product/:id', component: ProductDetail }, { path: '/cart', component: Cart }, { path: '/login', component: Login }, ]

跳转时用带参路由,在组件里通过route.params.id拿到商品ID,再调接口拿商品详情。

axios请求我们可以抽出一个公共封装,把baseURL、token、错误处理都集中起来:

import axios from 'axios' const http = axios.create({ baseURL: 'http://localhost:8000/api', timeout: 5000 }) http.interceptors.request.use(config => { const token = localStorage.getItem('access_token') if (token) { config.headers.Authorization = `Bearer ${token}` } return config })

商品列表页最核心的展示逻辑就三个:加载数据、渲染数据、条件筛选。加载数据在onMounted里调用接口,数据放到ref里驱动页面渲染。分类筛选用路由query或选中项,每次变化重新请求对应分类的商品。

Vue插槽这个热词,在商品卡片组件里很实用。比如商品卡片都有“图片区”“信息区”“操作按钮区”,不同页面可能对操作区要求不一样,此时就可以用插槽让父组件自定义按钮内容。而Vue的指令简写也能让代码更简洁:@click="goDetail"等同于v-on:click="goDetail",:src="item.image"等同于v-bind:src="item.image"。写习惯之后会发现,很多人用插槽和指令简写只是为了减少代码量,但实际上这两者真正的价值是提高了组件复用性。

购物车是前后端分离项目里最需要设计的一块。我的方案是分两种状态:未登录时,购物车存在浏览器localStorage里,用户选商品、改数量都直接改本地数据;登录后,购物车同步到后端。用户没登录也能逛、能加购物车,这个体验很关键,很多新手只做“必须登录才能加购”的版本,转化率其实很差。

4. 前后端联调、图片上传与部署

4.1 跨域与代理配置

跨域是前后端分离绕不开的第一个坑。前面我在Django里设置了CORS_ALLOW_ALL_ORIGINS = True,那是后端允许跨域的方式。但更推荐的前端做法是配置代理:让Vite在开发环境把/api开头的请求转发到后端的8000端口。这样前端代码里的baseURL可以写/api,浏览器看到的请求是同源的,就不存在跨域问题了。

在Vite项目的vite.config.js中加入:

export default defineConfig({ server: { proxy: { '/api': { target: 'http://localhost:8000', changeOrigin: true } } } })

设置完成后,前端调用/api/products/,Vite会把请求转发到http://localhost:8000/api/products/。这个配置能让你在开发阶段少掉80%的跨域烦恼。后端那边我会同时保留cors-headers配置,因为生产环境前后端可能部署在不同域名,代理不一定管用。

4.2 商品图片上传与访问路径

商品图片是一个非常影响体验的模块。Django中图片上传,我们需要设置MEDIA_ROOT和MEDIA_URL:

MEDIA_URL = '/media/' MEDIA_ROOT = BASE_DIR / 'media'

开发模式下,在urls.py中追加静态路由:

from django.conf import settings from django.conf.urls.static import static urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

这样,上传到media/products/目录下的图片就能通过http://localhost:8000/media/products/xxx.jpg访问了。前端页面里直接把这个URL拼到<img>标签的src上即可。

但这里有个必须注意的点:如果Nginx没有配置/media/路径,上传后图片会404。所以生产环境一定要把media目录映射到Nginx的静态文件路径里。

4.3 部署上线:从Pycharm到云服务器

部署部分,我以Django为例讲完整流程,Flask部署的Nginx配置差不多,只是启动命令不同。

本地开发完成后,在云服务器上下载代码,安装依赖,然后执行数据库迁移:

python manage.py collectstatic python manage.py migrate

用Gunicorn启动Django应用:

gunicorn momshop.wsgi:application -b 0.0.0.0:8000

前端项目先执行npm run build,生成了一个dist静态文件目录。然后配置Nginx,把用户访问的80端口指向dist目录,同时把/api开头的请求反向代理到8000端口,把/media开头的请求指向Django的media目录。核心配置如下:

server { listen 80; server_name example.com; root /path/to/shopweb/dist; index 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; } location /media/ { alias /path/to/momshop/media/; } }

配置完成后统一用Supervisor或systemd管理Gunicorn进程。千万别用python manage.py runserver跑生产环境,那个自带的开发服务器性能极差,是专门给本地调试用的。

5. 常见问题与排错速查

5.1 Django与Vue联调经典报错

前后端联调时,最常见的报错有两种:一种是403 Forbidden,另一种是404 Not Found。

403一般是CSRF验证没过。Django默认对POST请求做CSRF验证,而JWT登录方式是发POST请求,如果不加@csrf_exempt,就会报403。我在给登录视图加装饰器时踩过这个坑,后来统一在JWT认证中间件中忽略CSRF。

404就复杂一些,要区分是路由没匹配还是请求路径拼错。建议用浏览器开发者工具里的Network面板,看实际请求的URL到底是什么。很多404是前端baseURL写错了,比如多了一个斜杠,或者漏了/api。

5.2 数据库相关坑

数据库最常见的坑:迁移不同步。改完模型后忘了执行python manage.py makemigrations,或者执行了但没有重启服务,导致接口报no such column之类错误。我的习惯是:每次改完models.py,立刻在Pycharm的终端里连续执行两步,不要攒到后面再迁移。

另一个坑是外键关联删除。Django的on_delete参数如果没有设置,现在会报错,比如models.ForeignKey(Category, on_delete=models.CASCADE)。如果分类删了,它下面的商品要不要一起删?婴幼儿用品分类如果点错删除,可能导致商品数据全丢。后来我改成on_delete=models.SET_NULL,null=True,分类删除时商品保留,只把分类设为空。

5.3 包安装和环境问题

热词里总有“python安装numpy库的方法”“pycharm下载第三方库htmltestrunner”“pycharm怎么安装pandas包”,归根到底就是一个问题:Python包怎么装。方法只有三种:

  • 命令行执行pip install 包名
  • Pycharm设置里打开Project Interpreter,点加号搜索安装
  • 在有requirements.txt的项目里执行pip install -r requirements.txt

如果安装很慢,就用国内镜像源。以清华源为例:

pip install django -i https://pypi.tuna.tsinghua.edu.cn/simple

还有一种情况是包安装成功了,但Pycharm里还是报ModuleNotFoundError,这通常是因为Pycharm当前解释器不是你这个项目的虚拟环境。在Pycharm右下角状态栏点击Python版本号,重新选择项目里的venv解释器就能解决。

至于“pycharm激活”,社区版完全免费,用社区版就够了。我做的婴幼儿用品项目从头到尾都是用社区版跑的,没必要折腾其他版本。

6. 项目功能优化与个人经验

6.1 几个值得做的优化点

主流程跑通之后,一定要留一些时间去打磨细节。我觉得以下三个优化点最加分:

第一,商品搜索。不要用简单的icontains模糊查询就完事,可以加一个过滤功能,按价格区间、适用月龄、销量排序组合筛选。后端用DRF的django-filter库,前端Vue配合下拉和滑动条,整个体验会专业很多。

第二,购物车数量和库存校验。前端虽然能限制数量,但后端必须再做一次校验。用户下单时要检查商品状态是否上架、库存是否足够,如果不校验,恶意请求可以把库存改成负数。这个校验写在订单创建视图里,查询商品时加上select_for_update()锁行,防止并发情况下的超卖。

第三,订单状态可视化。把待支付、已支付、待发货、已发货、已完成这些状态做成时间线,用户每次查看订单时能看到进度。这个功能本身不难,但能明显提升项目的完整度。

6.2 我最终选择这套方案的个人体会

这版项目我先后用Django和Flask各做过一版,最后的感受是:用Django写商城类网站确实舒服,很多功能不用重复造轮子,尤其是Admin后台能给运营带来极大便利;而Flask让我更明白路由、请求上下文、ORM这些底层概念。如果你是为了学习,我更建议先用Flask写一版小Demo理解原理,再用Django完成正儿八经的商城系统,这样两条路都走过,你对Python后端的理解会扎实很多。

我自己的项目后来还扩展了收藏功能、优惠券和下单短信提醒。婴幼儿用品网站还有一个特殊需求是“适用月龄推荐”,后来做成了一些简单的规则引擎代码放在后端,每次用户首页加载时根据搜索记录推送相关商品,效果还不错。这种从基础电商功能往外加业务逻辑的路径,是最适合练手和展示简历的方式。做这类项目,最重要的是先跑通一个最小闭环,然后再慢慢堆功能,千万不要一上来就想把所有模块全部做完,那只会让你卡在环境和模型设计阶段出不来了。

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

从学习随笔到知识管理:三步笔记法与复盘实践

最近整理手头的学习资料时&#xff0c;翻到了3月16日那天写下的随笔。当时只是为了把当天的思考和操作流程留住&#xff0c;没想到回头再看&#xff0c;反而比当时学的内容本身更有价值。很多当时没想明白的问题&#xff0c;在这篇随笔里都能看到思考的轨迹&#xff1b;一些当时…

作者头像 李华
网站建设 2026/10/9 9:02:02

多场耦合优化实战:从代理模型到MOPSO的完整流程与避坑指南

拿到一个新项目&#xff0c;如果对方说“这个设计要做多场耦合优化&#xff0c;你最好连优化算法一起搞定”&#xff0c;我的第一反应不是兴奋&#xff0c;而是先问单次耦合仿真要跑多久。过去几年我经手过的流固耦合、热结构耦合项目&#xff0c;没有哪一次能让优化算法直接去…

作者头像 李华
网站建设 2026/10/9 9:01:31

PHP服务端接入活体识别:从API验签到风控链路实战

去年我接手一个信贷业务的风控改造&#xff0c;业务方提的需求特别朴素&#xff1a;用户在提现之前&#xff0c;系统必须证明摄像头前的人是本人&#xff0c;而不是一张打印照片或者一段翻拍视频。翻译成开发任务就是两件事&#xff1a;接入一套可靠的活体识别能力&#xff0c;…

作者头像 李华
网站建设 2026/10/9 9:00:47

告别收藏夹吃灰:用输出倒逼输入,建立计算机学习闭环

收藏从未停止&#xff0c;练习从未开始。这句话在计算机专业的学生和从业者身上几乎成了魔咒。B站视频越存越多&#xff0c;极客时间、掘金小册买了好几套&#xff0c;GitHub上star了一堆"必读仓库"&#xff0c;最后真正常看的可能还是那几条短视频。今天这篇内容不聊…

作者头像 李华
网站建设 2026/10/9 8:59:17

AI智能体交互范式:从MCP套壳现象看CLI为何更胜一筹

2025年我做AI工具评测的那段时间&#xff0c;几乎把所有主流MCP server都装了个遍。为了这件事我甚至把几台开发机的Node环境和Python环境都重新整理了一遍&#xff0c;折腾到半夜。然后我发现一个特别有意思的事实&#xff1a;这二三十个MCP server里&#xff0c;有接近一半本…

作者头像 李华
网站建设 2026/10/9 8:59:09

C++实现职员管理系统:数据库增删改查与课设实战全解析

说实话&#xff0c;“数据库期末大作业之职员管理系统&#xff08;C语言&#xff09;”这个题目&#xff0c;几乎每年都有同学在问。很多人拿到题目的第一反应是先找一份现成源码&#xff0c;改个主函数、换几个变量名就交上去&#xff0c;结果答辩时连“为什么用这条SQL”都说…

作者头像 李华