技术栈: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 项目的说明文档

文件结构理解

  1. 入口的main.js文件引入vue框架和App.vue主组件,然后实例化vue对象,在components属性中定义组件名

  2. App.vue主组件,里面集合了html, js, css语法,分别对应vue文件的<template>, <script>, <style>部分。

    在此主组件文件中,也可以引用其他子组件,引用时在script标签所在的js中用import引入,然后在实例化vue对象中的components中定义名字,然后在template所在的html中运用

  3. 子组件在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">登&nbsp;&nbsp;录</el-button>
<el-button type="primary" @click="click_register">注&nbsp;&nbsp;册</el-button>
<el-button v-if="userInfo!=null" type="danger" @click="click_logout">登&nbsp;&nbsp;出</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 = [
# BASE_DIR / "static",
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