Django学习笔记-VS Code本地运行项目

  1. 1. 将项目传到本地
  2. 2. 虚拟环境配置
  3. 3. 修改项目相关文件

截止到上一章节,我们的项目一直是部署在云服务器上,包括编写代码以及调试运行也是在云服务器上,现在我们尝试将其放回 Windows 本地环境运行。

1. 将项目传到本地

这一步可以使用 Git 也可以使用 SCP,由于之前项目上传在 AcGit 上,只能在 AC Terminal 中拉取,因此使用 SCP 远程传输:

1
scp -r -P 20000 asanosaki@<公网IP>:djangoapp .  # 在要存放项目的目录执行该指令

2. 虚拟环境配置

我们需要先配置一个和云服务上相同的虚拟环境,首先在 VS Code 中打开该项目并且打开终端,在项目根目录下创建虚拟环境:

1
python -m venv venv

虚拟环境创建成功之后,一般不会自动启用,所以需要启用它,进入 venv/Scripts 目录运行脚本 Activate.ps1

1
2
cd .\venv\Scripts\
.\Activate.ps1

此时在 VS Code 中可以看到命令行首部多了 (venv),说明已进入虚拟环境,然后在右下角选择虚拟环境中的解释器即可。

现在我们在虚拟环境中安装所需的环境:

1
pip install django

3. 修改项目相关文件

首先修改一下我们的打包脚本 compress_game_js.sh

1
2
3
4
5
6
7
#! /bin/bash

JS_PATH=../game/static/js/
JS_PATH_DIST=${JS_PATH}dist/
JS_PATH_SRC=${JS_PATH}src/

find ${JS_PATH_SRC} -type f -name '*.js' | sort | xargs cat > ${JS_PATH_DIST}game.js

然后打开 Git Bash,进入 scripts 文件夹即可运行脚本:

1
sh compress_game_js.sh

然后将 djangoapp/settings.py 中的 Redis 配置删除,即删除以下这段话:

1
2
3
4
5
6
7
8
9
10
CACHES = {
'default': {
'BACKEND': 'django_redis.cache.RedisCache',
'LOCATION': 'redis://127.0.0.1:6379/1',
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
},
},
}
USER_AGENTS_CACHE = 'default'

同样在该文件中需要将 localhost 添加到 ALLOWED_HOSTS 中:

1
ALLOWED_HOSTS = ["8.130.54.44", "app4007.acapp.acwing.com.cn", "localhost"]

然后将 AcWing 一键授权登录的相关文件夹删除:game/views/settings/acwinggame/urls/settings/acwing,然后修改 urls/settings/index.py 中的路由:

1
2
3
4
5
6
7
8
9
10
11
12
from django.urls import path
from game.views.settings.getinfo import getinfo
from game.views.settings.login import mylogin
from game.views.settings.logout import mylogout
from game.views.settings.register import register

urlpatterns = [
path('getinfo/', getinfo, name='settings_getinfo'),
path('login/', mylogin, name='settings_login'),
path('logout/', mylogout, name='settings_logout'),
path('register/', register, name='settings_register'),
]

修改 AcGame 类,去掉 AcWingOS API,且改为非模块化引入 JS 方式,即去掉 export 关键字:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class AcGame {
constructor(id) {
this.id = id;
this.$ac_game = $('#' + id); // jQuery通过id找对象的方式

this.settings = new Settings(this);
this.menu = new AcGameMenu(this);
this.playground = new AcGamePlayground(this);

this.start();
}

start() {
}
}

同时前端的 web.html 文件也需要修改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{% load static %}

<head>
<link rel="stylesheet" href="{% static 'css/jquery-ui.min.css' %}">
<script src="{% static 'js/jquery-3.6.1.min.js' %}"></script>
<link rel="stylesheet" href="{% static 'css/game.css' %}">
<script src="{% static 'js/dist/game.js' %}"></script>
</head>

<body style="margin: 0">
<div id="ac_game_1"></div>
<script>
$(document).ready(function() {
let ac_game = new AcGame("ac_game_1");
});
</script>
</body>

最后修改 Settings 类,去掉 AcWing 一键登录功能,且修改 ajax 请求的地址:

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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
class Settings {
constructor(root) {
this.root = root;
this.platform = 'WEB'; // 默认为Web前端
this.username = ''; // 初始用户信息为空
this.avatar = '';

this.$settings = $(`
<div class='ac_game_settings'>
<div class='ac_game_settings_login'>
<div class='ac_game_settings_title'>
Login
</div>
<div class='ac_game_settings_username'>
<div class='ac_game_settings_item'>
<input type='text' placeholder='Username'>
</div>
</div>
<div class='ac_game_settings_password'>
<div class='ac_game_settings_item'>
<input type='password' placeholder='Password'>
</div>
</div>
<div class='ac_game_settings_submit'>
<div class='ac_game_settings_item'>
<button>Login</button>
</div>
</div>
<div class='ac_game_settings_errormessage'>
</div>
<div class='ac_game_settings_option'>
Register
</div>
</div>
<div class='ac_game_settings_register'>
<div class='ac_game_settings_title'>
Register
</div>
<div class='ac_game_settings_username'>
<div class='ac_game_settings_item'>
<input type='text' placeholder='Username'>
</div>
</div>
<div class='ac_game_settings_password ac_game_settings_password_first'>
<div class='ac_game_settings_item'>
<input type='password' placeholder='Password'>
</div>
</div>
<div class='ac_game_settings_password ac_game_settings_password_second'>
<div class='ac_game_settings_item'>
<input type='password' placeholder='Confirm Password'>
</div>
</div>
<div class='ac_game_settings_submit'>
<div class='ac_game_settings_item'>
<button>Register</button>
</div>
</div>
<div class='ac_game_settings_errormessage'>
</div>
<div class='ac_game_settings_option'>
Login
</div>
</div>
</div>
`);

this.$login = this.$settings.find('.ac_game_settings_login');
this.$login_username = this.$login.find('.ac_game_settings_username input');
this.$login_password = this.$login.find('.ac_game_settings_password input');
this.$login_submit = this.$login.find('.ac_game_settings_submit button');
this.$login_errormessage = this.$login.find('.ac_game_settings_errormessage');
this.$login_register = this.$login.find('.ac_game_settings_option');
this.$login.hide();

this.$register = this.$settings.find('.ac_game_settings_register');
this.$register_username = this.$register.find('.ac_game_settings_username input');
this.$register_password = this.$register.find('.ac_game_settings_password_first input');
this.$register_confirm_password = this.$register.find('.ac_game_settings_password_second input');
this.$register_submit = this.$register.find('.ac_game_settings_submit button');
this.$register_errormessage = this.$register.find('.ac_game_settings_errormessage');
this.$register_login = this.$register.find('.ac_game_settings_option')
this.$register.hide();

this.root.$ac_game.append(this.$settings);

this.start();
}

start() { // 在初始化时需要从服务器端获取用户信息
this.getinfo();
this.add_listening_events();
}

add_listening_events() { // 绑定监听函数
this.add_listening_events_login();
this.add_listening_events_register();
}

add_listening_events_login() {
let outer = this;
this.$login_register.click(function() {
outer.register();
});
this.$login_submit.click(function() {
outer.login_on_remote();
});
}

add_listening_events_register() {
let outer = this;
this.$register_login.click(function() {
outer.login();
});
this.$register_submit.click(function() {
outer.register_on_remote();
});
}

login_on_remote() { // 在远程服务器上登录
let outer = this;
let username = this.$login_username.val();
let password = this.$login_password.val();
this.$login_errormessage.empty(); // 先清空报错信息

$.ajax({
url: 'http://localhost:8000/settings/login/',
type: 'GET',
data: {
username: username,
password: password,
},
success: function(resp) {
console.log(resp);
if (resp.result === 'success') { // 登录成功
location.reload(); // 刷新页面
} else { // 登录失败
outer.$login_errormessage.html(resp.result); // 显示报错信息
}
}
});
}

register_on_remote() { // 在远程服务器上注册
let outer = this;
let username = this.$register_username.val();
let password = this.$register_password.val();
let confirm_password = this.$register_confirm_password.val();
this.$register_errormessage.empty();

$.ajax({
url: 'http://localhost:8000/settings/register/',
type: 'GET',
data: {
username: username,
password: password,
confirm_password: confirm_password,
},
success: function(resp) {
console.log(resp);
if (resp.result === 'success') {
location.reload();
} else {
outer.$register_errormessage.html(resp.result);
}
}
});
}

logout_on_remote() { // 在远程服务器上登出
if (this.platform === 'ACAPP') return false; // AcApp应该是直接关闭窗口退出

$.ajax({
url: 'http://localhost:8000/settings/logout/',
type: 'GET',
success: function(resp) {
console.log(resp);
if (resp.result === 'success') {
location.reload();
}
}
});
}

register() { // 打开注册界面
this.$login.hide();
this.$register.show();
}

login() { // 打开登录界面
this.$register.hide();
this.$login.show();
}

getinfo() {
let outer = this;
$.ajax({
url: 'http://localhost:8000/settings/getinfo/',
type: 'GET',
data: {
platform: outer.platform,
},
success: function(resp) { // 调用成功的回调函数,返回的Json字典会传给resp
console.log(resp); // 控制台输出查看结果
if (resp.result === 'success') {
outer.username = resp.username;
outer.avatar = resp.avatar;
outer.hide();
outer.root.menu.show();
} else { // 如果未登录则需要弹出登录界面
outer.login();
}
}
});
}

hide() {
this.$settings.hide();
}

show() {
this.$settings.show();
}
}

顺带还需要修改一下 game.css

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
.ac_game_menu {
width: 100%;
height: 100%;
background-image: url('/static/image/menu/background2.jpeg'); /* 注意不用带公网IP */
background-size: 100% 100%;
user-select: none;
}

.ac_game_menu_btgroup {
width: 20vw;
position: relative;
top: 30%;
left: 20%;
}

.ac_game_menu_btgroup_bt {
height: 7vh;
width: 15vw;
color: white;
font-size: 3vh;
line-height: 7vh;
font-style: italic;
cursor: pointer;
text-align: center;
background-color: rgba(39, 21, 28, 0.6);
border-radius: 10px;
letter-spacing: 0.5vw;
}

.ac_game_menu_btgroup_bt:hover {
transform: scale(1.2);
transition: 100ms;
}

.ac_game_playground {
height: 100%;
width: 100%;
user-select: none;
}

.ac_game_settings {
width: 100%;
height: 100%;
background-image: url('/static/image/menu/background2.jpeg');
background-size: 100% 100%;
user-select: none;
}

.ac_game_settings_login {
height: 32vh;
width: 20vw;
position: relative;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: rgba(0, 0, 0, 0.7);
border-radius: 5px;
}

.ac_game_settings_register {
height: 39vh;
width: 20vw;
position: relative;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: rgba(0, 0, 0, 0.7);
border-radius: 5px;
}

.ac_game_settings_title {
color: white;
font-size: 3.5vh;
text-align: center;
height: 7vh;
line-height: 7vh;
}

.ac_game_settings_username {
display: block;
height: 7vh;
}

.ac_game_settings_password {
display: block;
height: 7vh;
}

.ac_game_settings_submit {
display: block;
height: 7vh;
}

.ac_game_settings_errormessage {
color: red;
font-size: 1.5vh;
display: inline;
float: left;
padding-left: 1vw;
}

.ac_game_settings_option {
color: white;
font-size: 1.5vh;
display: inline;
float: right;
padding-right: 1vw;
cursor: pointer;
}

.ac_game_settings_item {
width: 100%;
height: 100%;
}

.ac_game_settings_item > input {
width: 90%;
line-height: 3vh;
position: relative;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}

.ac_game_settings_item > button {
color: black;
width: 30%;
line-height: 3vh;
font-size: 2vh;
position: relative;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: rgb(199, 237, 204);
border-radius: 7px;
cursor: pointer;
}

到此已经可以在本地运行该项目了,我们也可以根据 Django 学习笔记-实现联机对战(上) Django 学习笔记-实现联机对战(下)统一长度单位以及添加闪现技能与技能冷却,因为这些部分与云端内容无关。

最后我们将 .git 文件夹删除,重新上传至 Github(先创建一个仓库 Small_Ball_Fight):

1
2
3
4
5
git init
git remote add origin git@github.com:AsanoSaki/Small_Ball_Fight.git
git add .
git commit -m "create small ball fight"
git push --set-upstream origin master

上一章:Django学习笔记-Web端授权AcWing一键登录

下一章:Django学习笔记-AcApp端授权AcWing一键登录