ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Django+Vue前后端分离网址导航系统开发实战

Django+Vue前后端分离网址导航系统开发实战 简介基于DjangoVue的前后端分离网址导航系统毕业设计资源包面向计算机相关专业在校学生、教师及企业开发者适合用于毕设项目、课程设计或前后端分离开发练习能够有效帮助学习者理解现代Web应用的主流开发模式。资源内项目代码均已通过运行测试功能稳定可放心下载使用。压缩包共108个文件约1.1MB主要包含25个Python后端源码、22个Vue前端组件、5个JS脚本以及XML/JSON配置、图片素材、Markdown说明文档等整体体量精简目录结构清晰便于按模块查阅与二次开发。目前已有84人学习下载。使用者可以完整了解Django与Vue的接口交互、前端路由与导航布局、后端API设计与数据库操作等核心环节同时附有使用说明适合初学者快速跑通项目也支持在此基础上增加个性化功能直接用于毕设答辩、课设验收或项目起步演示。1. 为什么毕业设计用 DjangoVue 做前后端分离网址导航网址导航是毕业设计里性价比很高的选题功能边界清楚前端要列表、分类、登录和收藏后端对应增删改查、权限和检索正好把前后端分离项目的常用环节都覆盖到。用 Django 做后端、Vue 做前端是因为两者在校园里资料最多招聘里也常见复现成本比一套冷门技术栈低得多。很多同学下载到的压缩包里已经包含完整源码、数据库文件和一份使用说明但拿到手之后才发现真正的难点不是看懂代码而是把前后端跑通、让 Vue 发出的请求被 Django 正确接收这中间卡住的点往往集中在跨域、token 和打包路径上。这篇内容就按“项目结构 → 后端接口 → 前端联调 → 使用说明验证”的顺序把每个环节怎么搭、参数怎么设、报错看哪里讲清楚。2. 网址导航系统的 Django 后端模型与 API 设计2.1 数据模型先理清分类、网址、注册用户导航系统的核心对象不是“网址”一个表而是分类-网址-用户三者之间的关系。分类可以是一级目录网址记录标题、链接、图标和归属分类用户通过收藏表与网址建立多对多关系。这样设计的好处是导航首页只需要一次接口查询就能拿到分类下所有链接收藏页又可以通过用户反向查询后续做“常用”“最近添加”都很方便。下面是一份适合毕业设计的 models.py字段尽量精简方便答辩时按字段说明设计理由# apps/navigation/models.py from django.contrib.auth.models import User from django.db import models class Category(models.Model): name models.CharField(分类名, max_length50, uniqueTrue) sort_order models.IntegerField(排序, default0) class Meta: ordering [sort_order, id] def __str__(self): return self.name class Link(models.Model): title models.CharField(网站名, max_length100) url models.URLField(网址) icon models.URLField(图标地址, blankTrue) description models.CharField(描述, max_length200, blankTrue) category models.ForeignKey(Category, related_namelinks, on_deletemodels.CASCADE) created_at models.DateTimeField(创建时间, auto_now_addTrue) class Meta: ordering [-created_at] def __str__(self): return self.title class Favorite(models.Model): user models.ForeignKey(User, related_namefavorites, on_deletemodels.CASCADE) link models.ForeignKey(Link, related_namefavorited_by, on_deletemodels.CASCADE) created_at models.DateTimeField(auto_now_addTrue) class Meta: unique_together (user, link)字段说明里有两个点需要关注uniqueTrue放在分类名上是为了避免同名分类重复Favorite表约束了同一用户不能收藏同一链接两次这是导航站做“收藏/取消收藏”按钮时最省事的做法。related_name参数也很关键它决定了你写序列化器时是category.links.all()还是category.link_set.all()前者更直观。2.2 用 DRF 把导航接口快速输出给 Vue后端接口直接用 Django REST Framework用它自带的序列化器把模型数据转成 JSON。导航首页需要的结构是“分类列表每个分类里带网址列表”这可以用嵌套序列化器实现。下面这份代码是实际项目中最常用的写法# apps/navigation/serializers.py from rest_framework import serializers from .models import Category, Link, Favorite class LinkSerializer(serializers.ModelSerializer): is_favorite serializers.SerializerMethodField() class Meta: model Link fields [id, title, url, icon, description, category, is_favorite] def get_is_favorite(self, obj): request self.context.get(request) if request and request.user.is_authenticated: return Favorite.objects.filter(userrequest.user, linkobj).exists() return False class CategorySerializer(serializers.ModelSerializer): links LinkSerializer(manyTrue, read_onlyTrue) class Meta: model Category fields [id, name, links]get_is_favorite是一个动态字段它读取请求里的用户信息判断当前链接是否已经被该用户收藏。这个字段不会写到数据库但前端拿到的 JSON 里直接就是true/false省掉一次额外的收藏状态查询。视图层用 ViewSet 把列表和详情合并# apps/navigation/views.py from rest_framework import viewsets from rest_framework.permissions import IsAuthenticatedOrReadOnly from .models import Category, Link from .serializers import CategorySerializer, LinkSerializer class CategoryViewSet(viewsets.ReadOnlyModelViewSet): queryset Category.objects.all() serializer_class CategorySerializer class LinkViewSet(viewsets.ModelViewSet): queryset Link.objects.all() serializer_class LinkSerializer permission_classes [IsAuthenticatedOrReadOnly]权限这里要单独讲IsAuthenticatedOrReadOnly表示未登录用户可以浏览导航但增删改只能登录后操作。毕业设计里这种粒度比“全部公开”或“全部登录”更合理也符合真实网站的使用习惯。路由注册用 DRF 的 DefaultRouter一行就能出来# config/urls.py from django.contrib import admin from django.urls import path, include from rest_framework.routers import DefaultRouter from apps.navigation.views import CategoryViewSet, LinkViewSet router DefaultRouter() router.register(categories, CategoryViewSet) router.register(links, LinkViewSet) urlpatterns [ path(admin/, admin.site.urls), path(api/, include(router.urls)), ]2.3 分页、搜索与批量导入导航数据的思路网址多了之后接口响应会变慢我用 DRF 的分页配置解决默认每页 20 条前端滚动到底部再加载下一页。这个参数写在 Django 的 settings.py 中全局生效# config/settings.py REST_FRAMEWORK { DEFAULT_PAGINATION_CLASS: rest_framework.pagination.PageNumberPagination, PAGE_SIZE: 20, DEFAULT_FILTER_BACKENDS: [ rest_framework.filters.SearchFilter, rest_framework.filters.OrderingFilter, ], }配置好后Vue 请求/api/links/?searchgithub就能按关键词过滤?ordering-created_at按时间倒序。实际联调中前端拿到的是分页结构包含count、next、previous、results四个键不能在页面里直接遍历整个响应。对应关系如下请求方式接口路径参数用途GET/api/categories/无获取分类及分类下所有链接GET/api/links/page, search, category分页获取链接支持筛选POST/api/links/title, url, category新增导航链接PUT / PATCH/api/links/{id}/局部字段修改链接信息DELETE/api/links/{id}/无删除链接批量导入是毕业设计加分项通常压缩包里会带一个 SQL 文件直接恢复数据但更规范的做法是自己写一个 management command 读 CSV。简单场景用 Django shell 执行即可不必单开接口。批量导入时注意先创建分类再创建链接否则外键会报空。3. 从零跑通 Django 后端建 App、配置 CORS 和 MySQL3.1 用 django 创建 app 与环境初始化拿到源码后第一步不是看业务代码而是先把依赖装好。一般压缩包里有requirements.txt最快的方式是创建虚拟环境后直接读它一次装齐python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate pip install -r requirements.txt pip list # 确认 Django、DRF、corsheaders 都装上了如果使用说明里没提供依赖清单可以按最小组装手动安装django、djangorestframework、django-cors-headers、mysqlclient。然后是新建项目再注册 app 的标准流程django-admin startproject config . python manage.py startapp apps其中config是项目配置目录的名字.表示在当前目录创建省掉一层嵌套目录。之后要在INSTALLED_APPS里加入rest_framework、corsheaders、apps并把corsheaders.middleware.CorsMiddleware放在CommonMiddleware前面否则跨域响应头不会覆盖到所有视图。这一步常被遗漏表现就是 Vue 发请求能通响应里却没有Access-Control-Allow-Origin。3.2 解决前端跨域CORS 配置与 JWT token前后端分离后Vue 开发服务器默认跑在 5173 端口Django 跑在 8000 端口浏览器会拦截不同源请求。开发阶段最简单的做法是用django-cors-headers放行本机地址生产环境再收紧域名单# config/settings.py CORS_ALLOWED_ORIGINS [ http://localhost:5173, http://127.0.0.1:5173, ] CORS_ALLOW_CREDENTIALS TrueCORS_ALLOW_CREDENTIALS涉及到后续携带 Cookie 或 Authorization 请求头。毕业设计里如果登录用的是 JWT前端把 token 放在请求头里不需要 Cookie 的withCredentials模式这项可以设成 False。token 认证我一般用djangorestframework-simplejwt它自带获取和刷新 token 的接口后端不用写登录逻辑# config/settings.py REST_FRAMEWORK[DEFAULT_AUTHENTICATION_CLASSES] [ rest_framework_simplejwt.authentication.JWTAuthentication, ] from datetime import timedelta SIMPLE_JWT { ACCESS_TOKEN_LIFETIME: timedelta(days1), REFRESH_TOKEN_LIFETIME: timedelta(days7), }访问/api/token/提交用户名和密码返回 access 和 refresh 两个字段之后 Vue 每次请求都要带上Authorization: Bearer access。这里的坑是 token 有效期一天对导航站足够如果毕业设计演示时发现“刷新页面就退出登录”检查是不是前端没存 token 或过期后没调用刷新接口。3.3 mysqlclient 在 Windows 和宝塔面板下的坑压缩包里的使用说明如果建议你用 MySQL那你大概率会在pip install mysqlclient上卡住。Windows 上常见的报错是mysql_config not found或Microsoft Visual C 14.0 is required解决路径是先下载对应 Python 版本的mysqlclient离线 whl 文件再本地安装不建议用pymysql强行顶替因为它在新版 Django 下需要额外设置__init__.py而且某些查询语法不一致。宝塔部署场景相反系统本身有编译环境yum install mysql-devel gcc python3-devel pip install mysqlclient安装成功后把数据库连接配置改成这样DATABASES { default: { ENGINE: django.db.backends.mysql, NAME: nav_db, USER: nav_user, PASSWORD: 你的密码, HOST: 127.0.0.1, PORT: 3306, OPTIONS: { charset: utf8mb4, }, } }utf8mb4是必须的否则链接描述里出现 emoji 或生僻字会报Incorrect string value。改完配置后执行python manage.py makemigrations和python manage.py migrate再python manage.py createsuperuser建后台账号。如果之前导入过 SQL需要先清空旧表再迁移否则第一次访问会提示表已存在。4. Vue 前端搭建与前后端分离联调4.1 用 Vite 创建 Vue 项目并安装 vue-router、axios前端部分常见做法是用 Vite 创建 Vue 项目它比 Vue CLI 启动更快构建后静态文件也更容易配置。新建项目后要安装三个依赖分别是路由、HTTP 库和 UI 组件库npm create vitelatest nav-frontend -- --template vue cd nav-frontend npm install npm install vue-router axios npm run devnpm create vite结束后项目里会有 src、public、package.json 等目录并没有生成 router 配置所以需要手动在src下建立router/index.js。安装依赖时如果出现npm ERR! code ERESOLVE常见原因是 lock 文件版本冲突删除node_modules和package-lock.json后重新npm install再试。之后把开发环境的后端地址统一放进一个常量文件里避免写死在请求函数中// src/api/config.js export const BASE_URL http://127.0.0.1:8000/api4.2 Vue Router 按路径跳到分类列表并携带参数导航站的页面结构一般是首页是全部链接点击左侧分类路由到分类详情路由地址里带分类 id。这里用到了两个技术点动态路由匹配和params接收。下面的路由配置可以直接复制// src/router/index.js import { createRouter, createWebHistory } from vue-router const routes [ { path: /, name: home, component: () import(../views/Home.vue) }, { path: /category/:id, name: category, component: () import(../views/Category.vue) } ] const router createRouter({ history: createWebHistory(), routes }) export default router在首页点击某个分类时用路由参数跳转// Home.vue 中跳转逻辑 function goCategory(id) { router.push({ name: category, params: { id } }) }然后在Category.vue里通过route.params.id读取这个分类 Id并发请求取分类下链接。需要注意队行动同一分类反复点击不会刷新页面需要监听route.params.id变化后再发一次请求否则只有第一次点击有数据。这是 Vue 新手最容易踩的坑表现是地址栏变了但页面内容没变。watch(() route.params.id, loadLinks, { immediate: true })4.3 axios 请求拦截器统一加入 token让导航栏登录态联动导航站的收藏功能依赖登录状态而 Vue 前后端分离请求 token 处理是高频面试题。核心思路是 request 拦截器把本地存的 token 放进请求头response 拦截器遇到 token 过期时自动跳转登录页。封装一个统一的请求实例// src/api/request.js import axios from axios import router from ../router const api axios.create({ baseURL: BASE_URL, timeout: 10000 }) api.interceptors.request.use(config { const token localStorage.getItem(access_token) if (token) { config.headers.Authorization Bearer ${token} } return config }) api.interceptors.response.use( response response.data, error { if (error.response error.response.status 401) { localStorage.removeItem(access_token) router.push({ name: login }) } return Promise.reject(error) } ) export default apibaseURL让所有请求自动带上/api前缀调用时只需要写资源名。拦截器中最容易遗漏的是response.data的处理很多不封装的代码会在每个页面写一遍res.data.data接口一遍后团队协作成本就往上涨。登录页获取 token 后把两个 token 都存在 localStorage这样导航栏就能根据access_token是否存在显示“登录/注册”还是“我的收藏”。退出登录不仅要把本地 token 清掉还要刷新当前页面否则侧边栏的登录状态不会变。5. 按压缩包里的使用说明启动前后端验证收藏链路打开“使用说明.zip”里的 README第一件事通常是确认 Python 与 Node 版本。我用的是 Python 3.10 和 Node 18低于这个版本时部分依赖会有兼容警告。验证整个项目是否跑通建议按下面流程做一遍不要跳过数据库迁移直接启动服务。# 后端终端 mysql -u root -p source 项目目录下的 nav_db.sql # 如果压缩包提供 python manage.py migrate python manage.py createsuperuser python manage.py runserver 0.0.0.0:8000 # 前端终端 cd nav-frontend npm run dev浏览器访问http://localhost:5173看到导航首页后先用 Django 后台添加一个测试分类和三个网址再回前端页面确认列表能显示。收藏功能的验证我用的是 curl它比点页面的反馈更直接能看清 token 到底有没有生效# 获取 token curl -X POST http://127.0.0.1:8000/api/token/ \ -H Content-Type: application/json \ -d {username:admin,password:admin123} # 收藏第一条链接把返回的 access 复制进 Authorization curl -X POST http://127.0.0.1:8000/api/favorites/ \ -H Authorization: Bearer access_token \ -H Content-Type: application/json \ -d {link:1}如果 curl 返回 201在收藏接口的/api/favorites/里能查到记录说明前后端联调链路没问题。如果返回 401优先检查请求头里有没有多余空格以及 token 是否已经过期。部署到宝塔时前端执行npm run build后把 dist 目录指到站点根目录后端用 gunicorn 启动同时 Nginx 配置 location /api 转发到 8000 端口并处理 URL 重定向以保证 Vue 的 history 路由在刷新时不出现 404。最后一个建议还是饶不了base路径vite.config.js里加一行base: ./否则构建后放在子目录打开是白屏。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进