写在前面
- 本节主要内容是实现四个函数,getinfo,login,logout,register
- 虽然说是四个函数还有比较麻烦的html+css,但是实际上只有一个逻辑——就是前后端交互,也就是view,url,js三个文件
- view负责在后台接受前端的请求然后和数据库交互,返回请求的值
- 这个“值”,就是json。view不是从数据库里拿出东西直接就扔给前端,而是把数据整理成json格式(就是个字典),然后交给前端;或者是接受前端的数据然后取出里面的数据用和数据库交互的函数存到数据库里面。
- 说白了我们编写view的时候干的事情就一件事情——整理数据,把我们想要的数据给前端,然后编写前端js代码把数据拿来用;或者把前端传过来的数据放到数据库相应的表里面。
- 至于怎么接受前端请求,怎么发给前端数据,怎么存取数据库,用不着我们来写。我们写的全是bug。
- url 就是django的路由,一个找到view的地址
- js负责问后端要或是给后台扔数据
- 怎么传输数据——用ajax(一种web数据交互方式)。说白了ajax就是个长得奇怪的函数(个人理解,我也没细研究ajax),url,type,data是参数,resp是返回值。
- data就是json格式,自己定里面放啥,和后端一致就好。
- 说完了。
小技巧
- ctrl + r 搜索历史指令
- 在编写后端代码的时候将setting.py中的debug选项改成true,这样就会输出错误信息。
- 如果记不得djano的包,可以打开python3的交互shell,它带自动补全。
- qq截图可以取色
- 在调css的时候,或许会因为inline的原因达不到预想的效果,所以加上一个\[HTML_REMOVED](1:33)
- 因为我们在写代码的时候为了方便调试会写console.log()函数,后期用ag命令来搜索文件中的“console.log”便可以找到全部的函数对应位置。
坑
- 图片或许会有权限
编写代码
编写player表
- 在models文件下创建所有数据库里面的表
- 新建player文件夹,别忘了新建__init__.py文件
- 编写player.py文件——定义新表
- 注册到管理员页面里
- 执行两个命令
python3 manage.py makemigrations
python3 manage.py migrate
- 然后我们就可以在admin页面找到我们定义的player表了
# game/models/player/player.py
from django.db import models
from django.contrib.auth.models import User
class Player(models.Model):
# 将user表(django自带的)和palyer表关联起来,on_delete=models.CASCADE只当user删除,player也跟着删除
user = models.OneToOneField(User, on_delete=models.CASCADE)
photo = models.URLField(max_length=256, blank=True) #头像图像
def __str__(self): # 意思就是django后台管理是图形化的,那么打开player表用什么来展示每一项呢,就是这个函数的作用
return str(self.user)
# 注册表
# game/admin.py
from django.contrib import admin
from game.models.player.player import Player
# Register your models here.
admin.site.register(Player)
修改AcGame类
- 因为我们是先后端分离,为了能让后端知道前端是来自web还是acapp还是小程序或者什么。我们在AcGame中添加点东西。
- 然后添加一个settings类的实例化,这settings类会在后面写
# game/static/js/src/zbase.js
export class AcGame {
# 这个AcWingOS是额外的一个参数,这个参数是你自己自定义的。比如现在我们加了acwingos,当我们在acwing app中打开应用,它在实例化AcGame这个类的时候就会加上AcWingOS这个参数(里面是acwing云端app的一些接口)。那么如果你自己把AcGame部署到了小程序或者哪里,你也可以再添加AcGame的实例化的参数,来表明来自哪个前端。
constructor(id, AcWingOS) {
this.id = id;
this.$ac_game = $('#' + id);
this.AcWingOS = AcWingOS;
this.settings = new Settings(this);
this.menu = new AcGameMenu(this);
this.playground = new AcGamePlayground(this);
this.start();
}
start() {
}
}
实现getinfo函数
编写函数(view)
# game/views/settings/getinfo.py
from django.http import JsonResponse
from game.models.player.player import Player
def getinfo_acapp(request):
player = Player.objects.all()[0]
return JsonResponse({
'result': "success",
'username': player.user.username,
'photo': player.photo,
})
def getinfo_web(request):
user = request.user
if not user.is_authenticated: # 判断是否登录
return JsonResponse({
'result': "未登录"
})
else:
player = Player.objects.all()[0]
return JsonResponse({ # 返回的就是个字典
'result': "success",
'username': player.user.username,
'photo': player.photo,
})
def getinfo(request):
platform = request.GET.get('platform') # platform自定义的
if platform == "ACAPP": # 不同端,不同的getinfo
return getinfo_acapp(request)
elif platform == "WEB":
return getinfo_web(request)
编写路由(url)
- 因为路由都是在一个文件里面的,所以代码不展示了,在最后面统一展示。
编写前端(js)
- 因为逻辑上登录在菜单之前嘛,所以先把game/static/js/src/menu/zbase.js中加上hide()函数show()在登录的js中调用
-
然后我们来编写设置菜单下的js代码
-
首先要确定js代码运行在哪个端
-
编写getinfo代码——用来向后台请求该用户的相关信息
- 我们这里主要是获取用户的头像
- 你可以自己添加东西,比如用户专属音乐,个性签名等等
// game/static/js/src/settings/zbase.js
class Settings {
constructor(root) {
this.root = root;
this.platform = "WEB"; // 默认是web平台
if (this.root.AcWingOS) this.platform = "ACAPP";
this.username = ""; // 用来存储用户名
this.photo = ""; //头像链接
省略
}
start() {
this.getinfo();
}
省略
getinfo() {
let outer = this;
$.ajax({
url: "https://app165.acapp.acwing.com.cn/settings/getinfo/",
type: "GET",
data: {
platform: outer.platform,
},
success: function(resp) { //这个resp就是我们之前view中传的字典
console.log(resp);
if (resp.result === "success") {
outer.username = resp.username;
outer.photo = resp.photo;
outer.hide();
outer.root.menu.show();
} else {
//发现用户没登录,那就打开登录界面
outer.login();
}
}
});
}
register() { // 打开注册界面
this.$login.hide();
this.$register.show();
}
login() { // 打开登录界面
this.$register.hide();
this.$login.show();
}
hide() {
this.$settings.hide();
}
show() {
this.$settings.show();
}
}
修改player类
- 把获得的用户数据反应在player类中
# game/static/js/src/playground/player/zbase.js
class Player extends AcGameObject {
constructor(playground, x, y, radius, color, speed, is_me) {
省略
// 如果是自己,就把图片信息拿来
if (this.is_me) {
this.img = new Image();
this.img.src = this.playground.root.settings.photo;
}
}
render() {
//这里也是,如果是自己就渲染头像,是敌人就渲染颜色
if (this.is_me) {
this.ctx.save();
this.ctx.beginPath();
this.ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
this.ctx.stroke();
this.ctx.clip();
this.ctx.drawImage(this.img, this.x - this.radius, this.y - this.radius, this.radius * 2, this.radius * 2);
this.ctx.restore();
} else {
this.ctx.beginPath();
this.ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
this.ctx.fillStyle = this.color;
this.ctx.fill();
}
}
}
登录注册界面
-
添加setting的css
-
登录注册的div先起好名字摆上,然后用find函数和变量绑定起来
-
写注册和登录界面的html还有css
-
登录:用户名框+密码框+登录按钮+报错信息+注册按钮
- 注意这里y总给div起名字的时候并没有涉及到login和register,为的就是注册界面的html可以直接复制,而且css也不用再添加了。
- 注册:直接复制登录界面然后改改字
- 第三方登录——acwing的logo
// game/static/js/src/settings/zbase.js
省略
this.username = "";
this.photo = "";
this.$settings = $(`
<div class="ac-game-settings">
<div class="ac-game-settings-login">
<div class="ac-game-settings-title">
登录
</div>
<div class="ac-game-settings-username">
<div class="ac-game-settings-item">
<input type="text" placeholder="用户名">
</div>
</div>
<div class="ac-game-settings-password">
<div class="ac-game-settings-item">
<input type="password" placeholder="密码">
</div>
</div>
<div class="ac-game-settings-submit">
<div class="ac-game-settings-item">
<button>登录</button>
</div>
</div>
<div class="ac-game-settings-error-message">
</div>
<div class="ac-game-settings-option">
注册
</div>
<br>
<div class="ac-game-settings-acwing">
<img width="30" src="https://app165.acapp.acwing.com.cn/static/image/settings/acwing_logo.png">
<br>
<div>
AcWing一键登录
</div>
</div>
</div>
<div class="ac-game-settings-register">
<div class="ac-game-settings-title">
注册
</div>
<div class="ac-game-settings-username">
<div class="ac-game-settings-item">
<input type="text" placeholder="用户名">
</div>
</div>
<div class="ac-game-settings-password ac-game-settings-password-first">
<div class="ac-game-settings-item">
<input type="password" placeholder="密码">
</div>
</div>
<div class="ac-game-settings-password ac-game-settings-password-second">
<div class="ac-game-settings-item">
<input type="password" placeholder="确认密码">
</div>
</div>
<div class="ac-game-settings-submit">
<div class="ac-game-settings-item">
<button>注册</button>
</div>
</div>
<div class="ac-game-settings-error-message">
</div>
<div class="ac-game-settings-option">
登录
</div>
<br>
<div class="ac-game-settings-acwing">
<img width="30" src="https://app165.acapp.acwing.com.cn/static/image/settings/acwing_logo.png">
<br>
<div>
AcWing一键登录
</div>
</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_error_message = this.$login.find(".ac-game-settings-error-message");
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_password_confirm = this.$register.find(".ac-game-settings-password-second input");
this.$register_submit = this.$register.find(".ac-game-settings-submit button");
this.$register_error_message = this.$register.find(".ac-game-settings-error-message");
this.$register_login = this.$register.find(".ac-game-settings-option");
this.$register.hide();
this.root.$ac_game.append(this.$settings);
this.start();
}
省略
登录和登出的函数
- 绑定函数——将界面按键和响应函数绑定
// game/static/js/src/settings/zbase.js
省略
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();
});
}
省略
然后view,url,js三步走
view
# game/views/settings/login.py
from django.http import JsonResponse
from django.contrib.auth import authenticate, login
def signin(request):
data = request.GET
username = data.get('username')
password = data.get('password')
user = authenticate(username=username, password=password)
if not user:
return JsonResponse({
'result': "用户名或密码不正确"
})
login(request, user)
return JsonResponse({
'result': "success"
})
# game/views/settings/logout.py
from django.http import JsonResponse
from django.contrib.auth import logout
def signout(request):
user = request.user
if not user.is_authenticated:
return JsonResponse({
'result': "success",
})
logout(request)
return JsonResponse({
'result': "success",
})
url
统一在最后。
js
// game/static/js/src/settings/zbase.js
login_on_remote() { // 在远程服务器上登录
let outer = this;
let username = this.$login_username.val();// 取出input的值
let password = this.$login_password.val();
this.$login_error_message.empty();
$.ajax({
url: "https://app165.acapp.acwing.com.cn/settings/login/", //对该页面发送请求
type: "GET",
data: {
username: username,
password: password,
},
success: function(resp) {
console.log(resp);
if (resp.result === "success") {
location.reload();
} else {
outer.$login_error_message.html(resp.result);
}
}
});
}
logout_on_remote() { // 在远程服务器上登出
if (this.platform === "ACAPP") return false;
$.ajax({
url: "https://app165.acapp.acwing.com.cn/settings/logout/",
type: "GET",
success: function(resp) {
console.log(resp);
if (resp.result === "success") {
location.reload();
}
}
});
}
注册功能
view
# game/views/settings/register.py
from django.http import JsonResponse
from django.contrib.auth import login
from django.contrib.auth.models import User
from game.models.player.player import Player
def register(request):
data = request.GET
username = data.get("username", "").strip()
password = data.get("password", "").strip()
password_confirm = data.get("password_confirm", "").strip()
if not username or not password:
return JsonResponse({
'result': "用户名和密码不能为空"
})
if password != password_confirm:
return JsonResponse({
'result': "两个密码不一致",
})
if User.objects.filter(username=username).exists():
return JsonResponse({
'result': "用户名已存在"
})
user = User(username=username)
user.set_password(password)
user.save()
Player.objects.create(user=user, photo="https://img2.baidu.com/it/u=2161949891,656888789&fm=26&fmt=auto")
login(request, user)
return JsonResponse({
'result': "success",
})
url
# game/urls/settings/index.py
from django.urls import path
from game.views.settings.getinfo import getinfo
from game.views.settings.login import signin
from game.views.settings.logout import signout
from game.views.settings.register import register
urlpatterns = [
path("getinfo/", getinfo, name="settings_getinfo"),
path("login/", signin, name="settings_login"),
path("logout/", signout, name="settings_logout"),
path("register/", register, name="settings_register"),
]
js
# game/static/js/src/settings/zbase.js
register_on_remote() { // 在远程服务器上注册
let outer = this;
let username = this.$register_username.val();
let password = this.$register_password.val();
let password_confirm = this.$register_password_confirm.val();
this.$register_error_message.empty();
$.ajax({
url: "https://app165.acapp.acwing.com.cn/settings/register/",
type: "GET",
data: {
username: username,
password: password,
password_confirm: password_confirm,
},
success: function(resp) {
console.log(resp);
if (resp.result === "success") {
location.reload(); // 刷新页面
} else {
outer.$register_error_message.html(resp.result);
}
}
});
}