python day85
手机号登录接口
views.py
@action(methods=['POST'], detail=False)
def login_phone(self, request, *args, **kwargs):
# 序列化类
ser = UserPhoneModelSerializer(data=request.data)
ser.is_valid(raise_exception=True)
token = ser.context['token']
user = ser.context['user']
return APIResponse(token=token, username=user.username, id=user.id)
serializer.py
from django.core.cache import cache
from django.conf import settings
class UserPhoneModelSerializer(serializers.ModelSerializer):
# 需要重写code
code = serializers.CharField()
mobile = serializers.CharField(max_length=11)
class Meta:
model = models.User
fields = ['mobile', 'code']
# 写全局钩子(校验手机号和验证码是否正确,登录成功,签发token)
def validate(self, attrs):
mobile = attrs.get('mobile')
code = attrs.get('code')
# 从缓存中取出这个手机号对应的验证码
cache_code = cache.get(settings.SMS_PHONE_CACHE % mobile)
if cache_code and cache_code == code:
# 可以登录,根据手机号,查到用户,给这个用户签发token
user = models.User.objects.get(mobile=mobile)
# 签发token
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
self.context['token'] = token
self.context['user'] = user
return attrs
else:
raise ValidationError('验证码错误')
前端获取验证码,手机号登录,前端注册功能
Register.vue
<template>
<div class="register">
<div class="box">
<i class="el-icon-close" @click="close_register"></i>
<div class="content">
<div class="nav">
<span class="active">新用户注册</span>
</div>
<el-form>
<el-input
placeholder="手机号"
prefix-icon="el-icon-phone-outline"
v-model="mobile"
clearable
@blur="check_mobile">
</el-input>
<el-input
placeholder="密码"
prefix-icon="el-icon-key"
v-model="password"
clearable
show-password>
</el-input>
<el-input
placeholder="验证码"
prefix-icon="el-icon-chat-line-round"
v-model="sms"
clearable>
<template slot="append">
<span class="sms" @click="send_sms">{{ sms_interval }}</span>
</template>
</el-input>
<el-button type="primary" @click="register">注册</el-button>
</el-form>
<div class="foot">
<span @click="go_login">立即登录</span>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "Register",
data() {
return {
mobile: '',
password: '',
sms: '',
sms_interval: '获取验证码',
is_send: false,
}
},
methods: {
close_register() {
this.$emit('close', false)
},
go_login() {
this.$emit('go')
},
check_mobile() {
if (!this.mobile) return;
if (!this.mobile.match(/^1[3-9][0-9]{9}$/)) {
this.$message({
message: '手机号有误',
type: 'warning',
duration: 1000,
onClose: () => {
this.mobile = '';
}
});
return false;
}
this.is_send = true;
//校验手机号如果存在就不用注册,直接去登录
this.$http.get(this.$BASE_URL + 'user/check_phone/', {params: {phone: this.mobile}}).then(res => {
console.log(res.data)
if (res.data.code == 100) {
this.is_send = false;
this.$message({
message: '该账号已经存在,请直接去登录',
type: 'success',
})
this.is_send = true;
} else {
this.is_send = true;
this.$message({
message: '可以正常注册',
type: 'warning',
})
}
})
},
register() {
if(!(this.mobile &&this.sms && this.password)){
this.$message({
message: '手机号,验证码,密码不能为空',
type: 'error',
})
return
}
this.$http.post(this.$BASE_URL + 'user/register/', {
mobile: this.mobile,
code: this.sms,
password: this.password
}).then(res => {
if (res.data.code == 100) {
this.$message({
message: '注册成功,请去登录',
type: 'success',
})
//关掉注册框,打开登录框
this.go_login()
} else {
this.$message({
message: '注册失败,请稍后再试',
type: 'error',
})
}
})
},
send_sms() {
if (!this.is_send) return;
this.is_send = false;
let sms_interval_time = 60;
this.sms_interval = "发送中...";
let timer = setInterval(() => {
if (sms_interval_time <= 1) {
clearInterval(timer);
this.sms_interval = "获取验证码";
this.is_send = true; // 重新回复点击发送功能的条件
} else {
sms_interval_time -= 1;
this.sms_interval = `${sms_interval_time}秒后再发`;
}
}, 1000);
this.$http.get(this.$BASE_URL + 'user/send_sms/?phone=' + this.mobile).then(res => {
console.log(res.data)
if (res.data.code == 100) {
this.$message({
message: '短信发送成功',
type: 'success',
})
} else {
this.$message({
message: '短信发送失败,请稍后再试。。。',
type: 'error',
})
}
})
}
}
}
</script>
<style scoped>
.register {
width: 100vw;
height: 100vh;
position: fixed;
top: 0;
left: 0;
z-index: 10;
background-color: rgba(0, 0, 0, 0.3);
}
.box {
width: 400px;
height: 480px;
background-color: white;
border-radius: 10px;
position: relative;
top: calc(50vh - 240px);
left: calc(50vw - 200px);
}
.el-icon-close {
position: absolute;
font-weight: bold;
font-size: 20px;
top: 10px;
right: 10px;
cursor: pointer;
}
.el-icon-close:hover {
color: darkred;
}
.content {
position: absolute;
top: 40px;
width: 280px;
left: 60px;
}
.nav {
font-size: 20px;
height: 38px;
border-bottom: 2px solid darkgrey;
}
.nav > span {
margin-left: 90px;
color: darkgrey;
user-select: none;
cursor: pointer;
padding-bottom: 10px;
border-bottom: 2px solid darkgrey;
}
.nav > span.active {
color: black;
border-bottom: 3px solid black;
padding-bottom: 9px;
}
.el-input, .el-button {
margin-top: 40px;
}
.el-button {
width: 100%;
font-size: 18px;
}
.foot > span {
float: right;
margin-top: 20px;
color: orange;
cursor: pointer;
}
.sms {
color: orange;
cursor: pointer;
display: inline-block;
width: 70px;
text-align: center;
user-select: none;
}
</style>
Login.vue
<template>
<div class="login">
<div class="box">
<i class="el-icon-close" @click="close_login"></i>
<div class="content">
<div class="nav">
<span :class="{active: login_method === 'is_pwd'}"
@click="change_login_method('is_pwd')">密码登录</span>
<span :class="{active: login_method === 'is_sms'}"
@click="change_login_method('is_sms')">短信登录</span>
</div>
<el-form v-if="login_method === 'is_pwd'">
<el-input
placeholder="用户名/手机号/邮箱"
prefix-icon="el-icon-user"
v-model="username"
clearable>
</el-input>
<el-input
placeholder="密码"
prefix-icon="el-icon-key"
v-model="password"
clearable
show-password>
</el-input>
<el-button type="primary" @click="pwd_login">登录</el-button>
</el-form>
<el-form v-if="login_method === 'is_sms'">
<el-input
placeholder="手机号"
prefix-icon="el-icon-phone-outline"
v-model="mobile"
clearable
@blur="check_mobile">
</el-input>
<el-input
placeholder="验证码"
prefix-icon="el-icon-chat-line-round"
v-model="sms"
clearable>
<template slot="append">
<span class="sms" @click="send_sms">{{ sms_interval }}</span>
</template>
</el-input>
<el-button type="primary" @click="phone_login">登录</el-button>
</el-form>
<div class="foot">
<span @click="go_register">立即注册</span>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "Login",
data() {
return {
username: '',
password: '',
mobile: '',
sms: '',
login_method: 'is_pwd',
sms_interval: '获取验证码',
is_send: false,
}
},
methods: {
close_login() {
this.$emit('close')
},
go_register() {
this.$emit('go')
},
//密码登录方法
pwd_login() {
if (!(this.username && this.password)) {
//用户名或密文为空
this.$message({
message: '用户名或密码不能为空',
type: 'warning'
});
} else {
//发送axios请求
this.$http.post(this.$BASE_URL + 'user/login/', {
username: this.username,
password: this.password
}).then(res => {
console.log(res)
if (res.data.code == 100) {
//登录成功
//1 存储返回的token,username(可以存的地方有三个)
//咱们放到cookie中(放到cookie中)
this.$cookies.set('token', res.data.token, '7d')
this.$cookies.set('username', res.data.username, '7d')
// sessionStorage.setItem()
// sessionStorage.getItem()
// localStorage.setItem()
// localStorage.getItem()
//2 销毁框
this.close_login()
} else {
this.$message({
message: res.data.msg,
type: 'warning'
});
}
})
}
},
//手机号登录
phone_login() {
if (!(this.mobile && this.sms)) {
//用户名或密文为空
this.$message({
message: '手机号或验证码不能为空',
type: 'warning'
});
} else {
//发送axios请求
this.$http.post(this.$BASE_URL + 'user/login_phone/', {
mobile: this.mobile,
code: this.sms
}).then(res => {
console.log(res)
if (res.data.code == 100) {
//登录成功
this.$cookies.set('token', res.data.token, '7d')
this.$cookies.set('username', res.data.username, '7d')
this.close_login()
} else {
this.$message({
message: res.data.msg,
type: 'warning'
});
}
})
}
},
change_login_method(method) {
this.login_method = method;
},
check_mobile() {
if (!this.mobile) return; //如果手机号没填,这个方法结束了
//js的正则 字符串.match('/正则表达式/')
if (!this.mobile.match(/^1[3-9][0-9]{9}$/)) {
this.$message({
message: '手机号有误',
type: 'warning',
duration: 1000, //message的显示时间
onClose: () => {
this.mobile = ''; //把手机号清空
},
});
return false;
}
//前端输入的手机号是正确手机号,去后端,查看手机号是否存在
// this.$http.get(this.$BASE_URL+'user/check_phone/?phone='+this.mobile)
this.$http.get(this.$BASE_URL + 'user/check_phone/', {params: {phone: this.mobile}}).then(res => {
console.log(res.data)
if (res.data.code == 100) {
//正常的,可以发送验证码,如果点击发送验证码按钮,就能点击,否则为false,就不能点击
this.$message({
message: '该手机可以发送验证码',
type: 'success',
})
this.is_send = true;
} else {
this.is_send = false;
this.mobile = ''
this.$message({
message: res.data.msg,
type: 'warning',
})
}
})
},
send_sms() {
if (!this.is_send) return;
this.is_send = false;
let sms_interval_time = 60;
this.sms_interval = "发送中...";
let timer = setInterval(() => {
if (sms_interval_time <= 1) {
clearInterval(timer);
this.sms_interval = "获取验证码";
this.is_send = true; // 重新回复点击发送功能的条件
} else {
sms_interval_time -= 1;
this.sms_interval = `${sms_interval_time}秒后再发`;
}
}, 1000);
//向后端发送短信接口发请求,给这个手机号发短信
this.$http.get(this.$BASE_URL + 'user/send_sms/?phone=' + this.mobile).then(res => {
console.log(res.data)
if (res.data.code == 100) {
this.$message({
message: '短信发送成功',
type: 'success',
})
} else {
this.$message({
message: '短信发送失败,请稍后再试。。。',
type: 'error',
})
}
})
}
}
}
</script>
<style scoped>
.login {
width: 100vw;
height: 100vh;
position: fixed;
top: 0;
left: 0;
z-index: 10;
background-color: rgba(0, 0, 0, 0.3);
}
.box {
width: 400px;
height: 420px;
background-color: white;
border-radius: 10px;
position: relative;
top: calc(50vh - 210px);
left: calc(50vw - 200px);
}
.el-icon-close {
position: absolute;
font-weight: bold;
font-size: 20px;
top: 10px;
right: 10px;
cursor: pointer;
}
.el-icon-close:hover {
color: darkred;
}
.content {
position: absolute;
top: 40px;
width: 280px;
left: 60px;
}
.nav {
font-size: 20px;
height: 38px;
border-bottom: 2px solid darkgrey;
}
.nav > span {
margin: 0 20px 0 35px;
color: darkgrey;
user-select: none;
cursor: pointer;
padding-bottom: 10px;
border-bottom: 2px solid darkgrey;
}
.nav > span.active {
color: black;
border-bottom: 3px solid black;
padding-bottom: 9px;
}
.el-input, .el-button {
margin-top: 40px;
}
.el-button {
width: 100%;
font-size: 18px;
}
.foot > span {
float: right;
margin-top: 20px;
color: orange;
cursor: pointer;
}
.sms {
color: orange;
cursor: pointer;
display: inline-block;
width: 70px;
text-align: center;
user-select: none;
}
</style>
后端注册接口
views.py
@action(methods=['POST'], detail=False)
def register(self, request, *args, **kwargs):
# 序列化类
ser = UserRegisterModelSerializer(data=request.data)
ser.is_valid(raise_exception=True)
ser.save() # 新增会触发,serializer的create方法,重写create方法
return APIResponse(code=100,msg='注册成功')
serializer.py
class UserRegisterModelSerializer(serializers.ModelSerializer):
code = serializers.CharField(write_only=True)
class Meta:
model = models.User
fields = ['mobile', 'code', 'password']
# # code这个字段只用来写
# extra_kwargs = {
# 'code': {'write_only': True}
# }
# 可给手机号和password加局部校验钩子
def validate_mobile(self, data):
import re
if re.match('^1[3-9][0-9]{9}$', data):
return data
else:
raise ValidationError('手机号不合法')
def validate(self, attrs):
mobile = attrs.get('mobile')
code = attrs.get('code')
# 校验code是否是我们给的
# 从缓存中取出这个手机号对应的验证码
cache_code = cache.get(settings.SMS_PHONE_CACHE % mobile)
if cache_code and cache_code == code:
# 可以注册
# 如何注册?需要重写create,由于密码是密文,需要重写,使用create_user来创建用户
# 把code剔除
attrs.pop('code')
# 加入username
attrs['username'] = mobile
return attrs
else:
raise ValidationError('验证码错误')
def create(self, validated_data):
# validated_data:username,password,mobile (email,icon都可以为空)
user = models.User.objects.create_user(**validated_data)
return user
redis介绍安装
redis数据库,非关系型(redis:内存数据库,所有数据放在内存中,mongodb:数据放在硬盘上,es:放在硬盘上)
关系型:mysql,db2,oracle,posgresql,sqlserver,达梦(国产数据),sql都是通用的,表和表之间的关联关系,事务的支持比较好,锁机制
redis是一个key-value存储系统。
-key-value存储
-支持5大数据类型:字符串,列表,字典(hash),集合,有序集合
-6.0之前单线程,单进程的
-redis是单线程为什么这么快?
-全内存操作
-基于io多路复用的模型
-没有多线程多进程的切换
-性能高,单机qps高达10w,实际生产环境实际6w
-可以持久化
-c语言写的
Memcached:内存数据库,key-value存储,支持的数据类型少,只有str类型,没有持久化,断电消失
redis不支持windows,io多路复用的模型,在linux使用的是epoll,在windwos上没有epoll,于是有一些第三方,基于源码改动了一些,让他可以在windows上执行
windows安装
-一路下一步:添加到环境变量,端口号是6379,使用的最大内存,不限制
装完之后,就会有个redis服务
-通过启动服务,停止服务来运行停止redis
命令方式启动和停止
-启动
redis-server 配置文件路径 # 使用配置文件启动
redis-server # 不使用配置文件启动
-客户端连接
redis-cli # 连接上本地 127.0.0.1 6379
redis-cli -h 127.0.0.1 -p 6378
在客户端:shutdown
在客户端敲:ping 回一个pone
redis的客户端很多
-redis-desktop-manager
-qt:平台,专门用来写图形化介模
-pyqt:用python代码在qt平台写图形化界面
python操作redis之普通连接和连接池
先需要下载redis包
pip install redis
# redis之普通连接
# from redis import Redis
# conn = Redis(host='127.0.0.1',port=6379) # 什么都不写,连的就是本地的6379
# # 获取name这个key对应的值
# res=conn.get('name')
# print(res)
# conn.close()
# redis连接之连接池
import redis
# 先定义一个池子,池子大小指定
# 这个pool必须做成一个单例(这句话只写一次)
#
# pool = redis.ConnectionPool(max_connections=10)
# 当该py文件以脚本运行,其中包的导入不能使用相对导入
# 如果是以模块形式导入使用,这个模块内可以使用相对导入
from redis_pool import POOL #天然的单例,不管导入多少次,只会实例化一次
# 建立连接,从池子中获取一个连接
conn=redis.Redis(connection_pool=POOL)
res=conn.get('name')
print(res)
字符串操作
from redis import Redis
conn = Redis(host='127.0.0.1',port=6379) # 什么都不写,连的就是本地的6379
# conn.set('age',18)
# conn.set('name','zhangsan')
# conn.set('hobby','篮球')
# conn.set('hobby','篮球',ex=3,nx=True)
# conn.set('hobby','足球',ex=3,nx=True)
# conn.set('hobby','足球',xx=True)
# conn.set('hobby1','足球',xx=True)
'''
ex,过期时间(秒)
px,过期时间(毫秒)
nx,如果设置为True,则只有name不存在时,当前set操作才执行,值存在,就修改不了,执行没效果
xx,如果设置为True,则只有name存在时,当前set操作才执行,值存在才能修改,值不存在,不会设置新值
'''
print(str(conn.get('name'),encoding='utf-8')) #取出来的值是bytes格式,以utf-8形式
setnx
setex
# conn.setex('xx',5,'yy')
psetex
# conn.psetex('xx',5000,'yy')
mset 批量设置
# conn.mset({'name':'lqz','age':19})
mget(keys, *args)
# print(conn.mget(['name','age']))
# print(conn.mget('name','age'))
getset(name, value)
# res=conn.getset('name','egon')
getrange
# gbk: 2个字节表示一个字符
# utf-8 3个字节表示一个字符
# res=conn.getrange('name',0,0) # 前闭后闭区间
# res=conn.getrange('name',0,2).decode('utf-8') # 前闭后闭区间
# print(res)
setrange
# conn.setrange('name',2,'zzz')
strlen 一个汉字,是3个字节
# print(conn.strlen('name'))
incr
# print(conn.incr('age'))
decr
# conn.decr('age')
append
conn.append('xxxxxxx','nb')