Django上课笔记(四)——(用户系统的实现)
对于上节课对接acapp的过程由于不涉及知识性的内容,就不单独写一篇博客了。对接acapp及分配域名的内容请参考:
也欢迎大家光临我另外项目课的其他博客:
Django上课笔记(一)——环境配置与项目创建(过程十分详细) - AcWing
(更新版)Django上课笔记(二)——菜单模块的实现, 含自动创建项目的脚本
Django上课笔记(三)——简单游戏的实现(模块拆分化详解) - AcWing
项目地址
https://git.acwing.com/codeRokie/acapp
准备工作
1.为了在调试时能让报错信息能显示在浏览器上,需要在acapp/acapp/settings
中将DEBUG = False
改为DEBUG = True
2.为了查看后台数据管理系统,需要创建超级账户(之前讲过),创建的命令为:
#需要在项目的根目录acapp下执行
python3 manage.py createsuperuser
3.进入后台的方式:你的域名后+/admin
扩充数据库
背景知识
1.django中默认的数据关系使用的的是sqlite3,
- 他是一款轻型的数据库,遵守了ACID(原子性,一致性,隔离性,持久性)的关系型数据库管理系统
- 零配置-无需安装和管理配置
- 储存在单一磁盘文件中的一个完整的数据库
- 支持数据库大小志2TB足够小,大概13万行C代码,4.43M,比一些流行的数据库在大部分数据库操作要快
- 独立:没有额外的依赖
- 源码完全的开源,你可以用于任何途径,你也可以出售他
- 支持多种开发语言,C,C++,C#,PHP,Perl,Java,Python,Ruby等
- 良好注释的源代码, 并且有着90%以上的测试覆盖率,且支持多种SQL语句
2.django当然也可以支持其他的数据库。
在实际的开发场景中,如y总课上所说,数据库一般是放在另一台专门存储数据库的服务器上
的。对于我们现在这种轻量级想,则不用考虑用mysql作为数据库。直接用他默认给的数据库就可以。
3.django换用其他数据库的方法,请参考:
django 使用MySQL数据库 - 刘江的django教程
Django详解之models操作 - 汪汪小喵咪 - 博客园
4.django的数据库管理模块是models.py
,如果逻辑复杂就把这个文件变为models
文件夹,并在里面创建一个__init__.py
文件
添加自定义的数据表
因为账号信息可能有很多表,所以用文件夹进行管理
每用一个文件夹代替一个文件,都要在文件夹下创建一个__init__.py文件
创建表player
在game/models
下创建文件player
,在player
下创建一个文件player.py
存储player
表的信息
player.py
内容如下:
#继承django数据库的基类
from django.db import models
from django.contrib.auth.models import User
#继承的类写在括号中
class Player(models.Model):
#定义关联关系,每个player对应一个user
#当user被删除,对应的player也被删除
user = models.OneToOneField(User,on_delete=models.CASCADE)
photo = models.URLField(max_length=256,blank=True)
#显示每个player的数据在后台的名字
def __str__(self):
return str(self.user)
将player表注册到后台管理界面中
1.改动game/admin.py
:
from django.contrib import admin
from game.models.player.player import Player
# Register your models here.
admin.site.register(Player)
2.运行2个命令,使后台更新数据库表
python3 manage.py makemigrations
python3 manage.py migrate
3.注意:
Nginx本身是一个静态资源的服务器,当只有静态资源的时候,就可以使用Nginx来做服务器。
所以当静态资源更新时,客户端也会同步更新;但当后台资源更新时,客户端不会同步更新,需要重启ngnix
服务,才能更新。
启动ngnix
:
uwsgi --ini scripts/uwsgi.ini
用户系统的建立
1.为了支持多端操作,需要在AcGame
类中传入参数,用来支持和判断不同的端
2.每写一个函数(前后端交互的任务)的时候需要实现:
views
层实现调用数据库的逻辑url
层中实现路由js
中实现对后端返回参数的调用
逻辑图:
实现view
层
在业务上,我们把视图层用户设置菜单
和与用户相关操作
的逻辑放在game/views/settings
文件夹下,与用户登录相关的操作,放在game/views/settings/getinfo.py
中:
from django.http import JsonResponse
from game.models.player.player import Player
#在acapp端的请求函数
def getinfo_acapp(request):
#表中的第一条数据
player = Player.objects.all()[0]
return JsonResponse({
'result': "success",
'username': player.user.username,
'photo': player.photo,
})
#在web端的请求函数
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):
#通过get请求从前端获取一个参数
platform = request.GET.get('platform')
#判断是哪个端发来的请求
if platform == "ACAPP":
return getinfo_acapp(request)
elif platform == "WEB":
return getinfo_web(request)
else:
return JsonResponse({
'result': "其他"
})
实现url
层
在game/urls/settings/index.py
中,引入刚才实现的view
层中的函数:
from django.urls import path
from game.views.settings.getinfo import getinfo
urlpatterns = [
path("getinfo/", getinfo, name="settings_getinfo"),
]
实现js
修改game/static/js/src/menu/zbase.js
:
这里由于代码过长,就不在此展示,请移步:
整个用户系统的访问流程
请参考我的另一篇详细博客
用户注册登录系统的实现
注意:在执行多端操作的时候,web端的登录状态和后台的登录状态同步,如果要调试,一定要注意登出后台账号
js
层
由于过于冗长且非重点,html
和css
部分大家可以自行查询源码
前端信息发送和接收的逻辑实现
- 从网页表单中获取用户填入的信息。在dom树中将对应表单的元素”抠”出来,保存在
constructor()
中 - 实现鼠标点击后向后端发送信息的函数
在game/static/src/menu/settings/zbase.js
class Settings {
constructor(root) {
this.root = root;
//表明在哪个端的参数
this.platform = "WEB";
if (this.root.AcWingOS) this.platform = "ACAPP";
//接收后端参数
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">
<input type="button" value="登录">
</div>
</div>
<div class="ac-game-settings-error-message">
</div>
<div class="ac-game-settings-option">
注册
</div>
<br>
<div class="ac-game-settings-quick-login">
<div class="ac-game-settings-quick-login-acwing">
<img width="30" src="https://app165.acapp.acwing.com.cn/static/image/settings/acwing_logo.png">
<br>
<div>
AcWing一键登录
</div>
</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">
<input type="button" value="注册">
</div>
</div>
<div class="ac-game-settings-error-message">
</div>
<div class="ac-game-settings-option">
登录
</div>
<br>
<div class="ac-game-settings-quick-login">
<div class="ac-game-settings-quick-login-acwing">
<img width="30" src="https://app165.acapp.acwing.com.cn/static/image/settings/acwing_logo.png" >
<div>
AcWing一键登录
</div>
</div>
<br>
</div>
</div>
</div>
`);
//把登录界面加载到DOM树中
// this.root.$ac_game.append(this.$settings);
//找到登录窗口节点及其子树
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>div>input");
//显示错误信息
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>div>input");
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();
}
/**
* 在对象创建时执行的函数
*/
start() {
this.getinfo();
this.add_listening_events();
}
/**
* 获得后端信息的函数
*/
getinfo() {
let outer = this;
$.ajax({
url: "https://app220.acapp.acwing.com.cn/settings/getinfo/",
type: "GET",
data: {
platform: outer.platform,
},
//得到后端参数后执行的函数
success: function (resp) {
console.log(resp);
if (resp.result === "success") {
outer.username = resp.username;
outer.photo = resp.photo;
//登录界面隐藏
outer.hide();
//显示菜单界面
outer.root.menu.show();
} else {
//获取信息失败(即用户未登录),则继续显示登录界面
outer.open_login();
}
}
});
}
/**
* 打开登录界面
*/
open_login() {
this.$register.hide();
this.$login.show();
}
/**
* 打开注册界面
*/
open_register() {
this.$login.hide();
this.$register.show();
}
/**
* 隐藏注册/登录界面
*/
hide() {
this.$settings.hide();
}
/**
* 显示注册/登录界面
*/
show() {
this.$settings.show();
}
/**
* 鼠标点击后触发的函数
*/
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 () {
console.log("666")
outer.open_register();
});
this.$login_submit.click(function () {
outer.login_on_remote();
});
}
/**
* 点击注册按钮后触发的函数
*/
add_listening_events_register() {
let outer = this;
this.$register_login.click(function () {
console.log('666')
outer.open_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_error_message.empty();
$.ajax({
url: "https://app220.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);
}
}
});
}
/**
* 注册用户
*/
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://app220.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);
}
}
});
}
/**
* 在远程服务器上登出
* @returns {boolean}
*/
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
层
背景知识
Django 用户认证(Auth)组件 | 菜鸟教程 (runoob.com)
Django中authenticate和login模块 - ccorz - 博客园 (cnblogs.com)
实现
登录
在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',
})
注册
在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",
})
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",
})
urls
层
在game/urls/settings/index.py
中把view
层实现的函数加进路由中
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"),
]
完结撒花~
狠人呐