技术栈:Vue + Django + MySQL
IDE:Pycharm
本项目为2022秋北航网络空间安全学院数据库实验课程作业。作业需要进行简单的全栈开发。时间紧迫,且本人初次接触Vue,过程中遇到了许多问题。在此记录项目的开发过程。
项目GitHub仓库:https://github.com/yunsaijc/Autumn-DB_Lab3-Django-Vue-Project.git
后端-Django
初始化与配置
新建项目
通过命令django-admin startproject project_name
来在当前路径下创建一个新的项目,项目文件夹结构如下:
随后通过命令python manage.py startapp app_name
来创建一个app,并将该app的名字加入到项目文件夹的settings.py
下:
1 2 3 4
| INSTALLED_APPS = [ ... 'api' ]
|
在该app文件夹下创建一个urls.py
用于管理url
,文件夹目录结构如下:
连接数据库
在项目文件夹的settings.py
下设置数据库信息:
1 2 3 4 5 6 7 8 9 10 11 12 13
| DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'DB_Lab3', 'HOST': '127.0.0.1', 'PORT': '3306', 'USER': 'root', 'PASSWORD': 'admin', 'OPTIONS': { 'init_command': "SET sql_mode='STRICT_TRANS_TABLES'", } } }
|
同时在项目文件夹的__init__.py
下配置:
1 2
| import pymysql pymysql.install_as_MySQLdb()
|
然后运行命令python manage.py makemigrations
生成迁移文件,再通过python manage.py migrate
进行迁移,最后运行python manage.py inspectdb > users/models.py
,这时候app文件夹下的models.py
就会根据数据库中的表自动生成类。
跨站点请求设置
需要进行跨站点请求的相关设置。
首先通过pip
安装django-cors-headers
包,然后在settings.py
下进行设置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| INSTALLED_APPS = [ ...... 'corsheaders', ...... ]
MIDDLEWARE = [ ...... 'corsheaders.middleware.CorsMiddleware', 'django.middleware.common.CommonMiddleware', ...... ]
CORS_ORIGIN_ALLOW_ALL = True CORS_ALLOW_CREDENTIALS = True
|
具体实现
后端逻辑的实现在app
文件夹的views.py
文件中完成,在本项目中也就是/api/views.py
。其中登录函数的代码如下所示,完整代码见Github仓库。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| def login(request): if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password')
if username == '' or password == '': return JsonResponse({'errno': 2, 'msg': "用户名或密码为空"}) try: user = Users.objects.get(user_name=username) except: return JsonResponse({'errno': 2, 'msg': "用户不存在"}) if password == user.pass_word: return JsonResponse({'errno': 0, 'msg': "登录成功", 'username': username}) return JsonResponse({'errno': 2, 'msg': "密码不正确"}) return JsonResponse({'errno': 1, 'msg': "请求方法错误"})
|
前端-Vue
初始化
创建项目:vue create project_name
,选择vuex
(前端状态存储管理)和router
(路由管理)
项目结构如下。其中src
文件夹存放源代码,dist
文件夹存放npm run build
,命令编译后生成的文件:
src
文件夹结构如下:
各个目录或文件的说明如下:
node_modules |
npm 加载的项目依赖模块 |
public |
静态资源,build 构建后为根目录,含网站导航栏图标、首页入口文件 |
src |
开发做的事情基本都在这个目录下,含: • assets:
放置一些图片、字体等资源 • components: 放置组件文件,一般为全局组件 •
router: 网站路由跳转设置 • store: 前端数据存储 • views: 放置各页面文件 •
App.vue: 项目入口文件 • main.js:
项目的核心文件,在这里可以导入各种全局依赖 |
.xxx文件 |
配置文件,包括语法配置、git配置(.gitignore)等 |
package.json |
项目配置文件 |
README.md |
项目的说明文档 |
文件结构理解
入口的main.js
文件引入vue
框架和App.vue
主组件,然后实例化vue
对象,在components
属性中定义组件名
App.vue
主组件,里面集合了html, js, css
语法,分别对应vue
文件的<template>, <script>, <style>
部分。
在此主组件文件中,也可以引用其他子组件,引用时在script
标签所在的js
中用import
引入,然后在实例化vue
对象中的components
中定义名字,然后在template
所在的html
中运用
子组件在components
文件中,一般此文件夹中放置的都是子组件,被App.vue
主组件引用
具体实现
前端的实现在appfront
文件夹的views
文件夹中完成,在本项目中也就是/appfront/views
。其中登录页面的代码如下所示,完整代码见Github仓库。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
| <template> <div id="login" class="login"> <div class="wrap"> <h1>登 录</h1> <h2 v-if="userInfo!=null">您已经以 {{userInfo.user.username}} 的身份登录</h2> <el-form ref="form" class="form"> <el-form-item prop="username"> <el-input placeholder="用户名" type="username" v-model="username" autocomplete="off"></el-input> </el-form-item> <el-form-item id="password" prop="password"> <el-input placeholder="密码" show-password type="password" v-model="password" autocomplete="off" @keyup.enter.native="login" ></el-input> </el-form-item> <div > <el-button type="primary" @click="click_login">登 录</el-button> <el-button type="primary" @click="click_register">注 册</el-button> <el-button v-if="userInfo!=null" type="danger" @click="click_logout">登 出</el-button> </div> </el-form> </div> </div> </template>
<script> import qs from "qs"; import user from "@/store/user" const userInfo = user.getters.getUser(user.state()); export default { name: "login", data() { return { userInfo, username: '', password: '' } }, mounted: function() { this.username = userInfo.user.username }, methods: { click_login() { this.$axios({ method: 'post', url: '/api/login/', data: qs.stringify({ username: this.username, password: this.password }) }) .then(res => { switch (res.data.errno) { case 0: window.alert("登录成功!"); /* 将后端返回的 user 信息使用 vuex 存储起来 */ this.$store.dispatch('saveUserInfo', { user: { 'username': res.data.username } }); location.reload(); break; default: window.alert("登录失败!" + res.data.msg); break; } }) .catch(err => { console.log(err); /* 若出现异常则在终端输出相关信息 */ }) }, click_register() { this.$axios({ method: 'post', url: '/api/register/', data: qs.stringify({ username: this.username, password: this.password }) }) .then(res => { switch (res.data.errno) { case 0: window.alert("注册成功!"); break; default: window.alert("注册失败!" + res.data.msg); break; } }) .catch(err => { console.log(err); /* 若出现异常则在终端输出相关信息 */ }) }, click_logout() { this.$store.dispatch('clear'); window.alert('登出成功!'); location.reload(); }, } } </script>
<style scoped> #login { font-family: 'Noto Serif SC', serif; margin-top: 60px; } #login >>> .el-input__inner { font-family: 'Noto Serif SC', serif; } #login .bgbox { display: block; opacity: 1; z-index: -3; position: fixed; left: 0; top: 0; width: 100%; height: 100%; object-fit: cover; transition: opacity 1s,transform .25s,filter .25s; backface-visibility: hidden; } #login .logo { cursor: pointer; overflow: hidden; height: 150px; } #login .wrap { width: 300px; height: 315px; padding: 0 25px 0 25px; line-height: 40px; position: relative; display: inline-block; background-color: rgba(255, 255, 255, 0.85); border-radius: 20px; } #login .btn_login { margin-top: 25px; text-align: center; } #login .btn_login button{ line-height: 10px; font-family: 'Noto Serif SC', serif; width: 70%; height: 38px; } #login .suffix { font-size:14px; line-height:10px; color:#999; cursor: pointer; float:right; } </style>
|
将Vue与Django绑定
在前端文件夹下运行命令npm run build
进行编译
目前我们已经分别完成了Django后端和Vue前端工程的创建和编写,但实际上它们是运行在各自的服务器上。因此我们须要把Django的·TemplateView·指向我们刚才生成的前端dist文件。
在/DB_Lab3/urls.py
下进行如下设置:
1 2 3 4 5
| urlpatterns = [ path('admin/', admin.site.urls), path('api/', include('api.urls')), url(r'^$', TemplateView.as_view(template_name="index.html")) ]
|
在/DB_Lab3/settings.py
下进行如下设置:
1 2 3 4 5 6 7 8 9 10 11 12
| TEMPLATES = [ { ..., 'DIRS': ['appfront/dist'], ... }, ]
STATICFILES_DIRS = [ os.path.join(BASE_DIR, "appfront/dist/static"), ]
|
随后在项目根目录执行python manage.py runserver
即可。
遇到的坑
在使用element-ui
时,遇到el-table
表格不显示的问题,卡了两个小时才找到原因...
原因:版本问题!!!
解决办法:重新装一个低版本的包!!!
1 2
| npm uninstall element-ui npm install element-ui@2.9.2 -S
|
参考
https://super-buaa-2021.github.io/Vuebook/
https://cloud.tencent.com/developer/article/1005607
http://t.zoukankan.com/smile-fanyin-p-11258300.html
https://blog.csdn.net/weixin_48282959/article/details/124126667
https://blog.csdn.net/m0_59023970/article/details/123427008
https://blog.csdn.net/yehaocheng520/article/details/117025938
https://blog.csdn.net/qq_41216743/article/details/104605524