一、后续课程安排
1 drf框架
2 git
3 redis使用
4 路飞项目(celery异步)
5 爬虫(mongodb)
6 linux
7 cmdb项目(资产收集)
8 代码发布系统
9 flask框架(给你一到两个项目)
10 数据结构和算法
11 docker,dockercompose(用docker部署项目,nginx负载均衡,横向扩展),k8s(看情况)
12 就业辅导(redis高级,Elasticsearch,如何提高项目并发量,分布式锁,分布式id,远程连接docker开发,git冲突如何解决)
二、作业讲解
作业一:
前后端传数据三种编码格式,传json格式,原生django不能从POST中取出字典,用中间件或者装饰器前端不管传json还是其他格式,requests对象中有个data属性
form django.utils.deprecaton import MiddlewareMixin
import json
class JsonMiddel(MiddlewareMixin)
def process_request(self,request):
try:
request.data = json.loads(request.body)
except Exception as e:
request.data = request.POST
前端用ajax提交数据:
<body>
<form method="post">
用户名:<input type="text" name="name" id="name">
密码:<input type="text" name="pwd" id="pwd">
<input type="submit" value="提交1">
<input type="button" value="提交" id="submit">
</form>
</body>
<script>
$('#submit').click(function () {
$.ajax({
url:'/index/',
type:'post',
contentType:'application/json',
data:JSON.stringify({'name':$('#name').val(),'pwd':$('#pwd').val()}),
//data:{'name':$('#name').val(),'pwd':$('#pwd').val()},
success:function (data) {
console.log(data)
}
})
})
</script>
易错点:
-
form表单和ajax重复提交请求:
form表单中input的submit类型和button按钮都会触发提交数据的动作,如果有ajax的话,点一次按钮就会提交两次,如果只需要用ajax提交数据那么就绑定input的button类型,或者将button按钮放在form表单外进行绑定。
-
form django.http.request import QueryDict
本质是一个字典,但是比字典的功能强大,并且不能修改值,一改就会报错,只能用copy复制一份进行修改。
ps:CommonMiddleware
中间件控制了是否重定向到带/的地址
"""
当在浏览器中输入的url没有以/结尾,请求到django中间件中会自动拼接上/,然后在匹配一次路由
这是django的CommonMiddleware帮我们实现的"""
# django.middleware.common.CommonMiddleware 中间件源码
class CommonMiddleware(MiddlewareMixin):
...
def should_redirect_with_slash(self, request):
"""
Return True if settings.APPEND_SLASH is True and appending a slash to
the request path turns an invalid path into a valid one.
"""
if settings.APPEND_SLASH and not request.path_info.endswith('/'):
urlconf = getattr(request, 'urlconf', None)
return (
not is_valid_path(request.path_info, urlconf) and
is_valid_path('%s/' % request.path_info, urlconf)
)
return False
三、python 中的魔法方法(双下滑方法)
常用的魔法方法:
-
__init__
:类实例化对象会触发 -
__stry__
:打印对象会触发 -
__call__
:对象加()调用会触发,类也是对象,元类(),类的实例化过程调用元类的__call__
-
__new__
:在类实例化会触发,它比__init__
早,(造一个空的对象) -
__del__
:del对象或者在对象被回收的时候触发 -
__setattr__
/__getattr__
:(.拦截方法),当对象.属性的时——》赋值的时候调用__setattr__
取值的时候调用
__getattr__
-
__getitem__
/__setitem__
:([]拦截),字典添加键值对或者[]取值的时候会触发 -
__enter__
/__exit__
:上下文管理器,进入with管理的时候触发、with代码块运行结束后触发
3.1 setattr、getattr、settiem、getitem演示
# 1 __setattr__与__getattr__
class MyClass:
def __init__(self, name):
self.name = name
def __setattr__(self, name, value):
if type(value) != str:
print('名字必须是字符串')
else:
# setattr(self,key,value) 内部还是用.赋值,又会触发__setattr__,形成递归报错。
self.__dict__[name] = value # 不要加引号
obj1 = MyClass('zts')
obj1.name = 123
print(obj1.name)
# 2 __getitem__与__setitem__
class MyClass1:
def __init__(self, name):
self.name = name
def __setitem__(self,key,value):
setattr(self,key,value)
def __getitem__(self, key):
return getattr(self,key)
obj2 = MyClass1('tom')
obj2['age'] = 18
print(obj2['age'])
3.1 with上下文管理器
class Person:
def __enter__(self):
print('我在with管理的时候,会触发')
return 'xx' # 进入with语句块执行此方法,此方法如果有返回值就会赋值给as声明的变量
def __exit__(self, exc_type,exc_val,exc_tb):
print('在退出with代码块,也就是with代码块运行完后会执行此方法')
print('1',exc_type)
print('2',exc_val)
print('3',exc_tb)
with Person() as p: # 这句代码执行,会触发类的__enter__方法
print(p) # 打印之后with代码块运行结束,会触发类的__exit__方法
四、cookie/session/token
HTTP协议四大特性:
- 无状态
- 无连接
- 基于请求相应
- 基于tcp/ip,应用层协议
- mysql:c/s架构,底层基于socket,自己封装的协议,mysql的服务端:navicate(c++图形化界面,实现了请求和相应协议)/pymsql(用python语言实现了请求和响应协议)
- redis:c/s架构,底层基于socket,自己封装的协议
- docker:c/s架构,基于http协议,使用restfull规范
cookie:
是存在浏览器上的键值对,向服务端发送请求,携带它过去(不安全)
session:
存在与服务端的键值对,可以放在文件中或者数据库中(mysql/rediex)
django中session的底层原理:
# 在中间件中,请求走的时候,process_response,取出request.session的modify属性,判断是否是true,如果是true,表示在视图函数中修改过session,数据库同步修改,如果是false,就不修改,返回给前端sessionid:随机字符串
# 请求来了,通过sessionid,取出随机字符串--》去数据库中查--》把表的数据转成字典,赋值给request.session,后面视图函数中就可以操作它了
缺点:如果用户量大,存储需要耗费服务端资源
token:
token现在应用非常广泛,契合了前后端分离
JWT:json web token
五、异常处理
try:
print("xxx")
# print(1/0)
except Exception as e:
print(e)
else: # 基本上不会用到
print("正常执行,没有出异常,会走")
finally:
print("我是finally") # 用于会走,无论是否有异常
六、pymsql的使用
# 1 连接数据库
conn = pymsql.connect(host='127.0.0.1',
user='root',
passwrod='1111',
database='test',
port=3306)
# 2 获取游标
cursor = conn.cursor(cursor = pymysql.cursors.DictCursor) # 字典格式
# 查
sql_1 = 'select id,name from book'
cursor.execute(sql_1)
ret = cursor.fetchall()
print(ret)
# 插入数据
sql_2 = 'insert into book(id,name) values(%s,%s)'
cursor.execute(slq_2,[3,'tom'])
conn.commit() # 提交到数据库
# 删除
sql_3 = 'delete from book where name=%s'
cursor.execute(sql,['tom'])
conn.commit()
# 更新
sql_4 = 'update book set name=%s where id=%s'
cursor.execute(sql_4,['xxx',1])
conn.commit()
作业:
1 写一个类,有个name属性,如果name赋值为非字符串,就不让放
class MyClass:
def __init__(self, name):
self.name = name
def __setattr__(self, name, value):
if type(value) != str:
print('名字必须是字符串')
else:
self.__dict__[name] = value
obj1 = MyClass('zts')
obj1.name = 123
print(obj1.name)
obj1.name = 'tom'
"""
名字必须是字符串
zts
tom"""
2 通过上下文管理器写一个mysql的连接,通过with管理
class MySQL:
conn = None
cursor = None
def __init__(self, host, port, user, password, database):
self.host = host
self.port = port
self.user = user
self.password = password
self.database = database
def __enter__(self):
import pymysql
self.conn = pymysql.connect(host=self.host,
port=self.port,
user=self.user,
database=self.database,
password=self.password)
self.cursor = self.conn.cursor(cursor=pymysql.cursors.DictCursor) # 将查询出来的数据由元祖的形式改成字典
return self.conn
def __exit__(self, exc_type, exc_val, exc_tb):
self.conn.close()
with MySQL('127.0.0.1', 3306, 'root', '123123', 'db_py') as conn:
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
# 1 查
sql_1 = 'select name,age from student'
cursor.execute(sql_1)
ret = cursor.fetchall()
print(ret)
# 2 增
sql_2 = 'insert into student(name,age) values(%s,%s)'
cursor.execute(sql_2, ['tank', 20])
conn.commit()
# 3 删
sql_3 = 'delete from student where name=%s'
cursor.execute(sql_3, ['tom'])
conn.commit()
# 4 改
sql_4 = 'update student set name=%s where id>0'
cursor.execute(sql_4, ['xxx'])
conn.commit()
3 使用django实现token功能
# 1 视图函数
def login(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
user_obj = auth.authenticate(request,username=username,password=password)
if user_obj:
request.user_id = username
return HttpResponse('登陆成功')
else:
request.user_id = None
return HttpResponse('登陆失败')
return render(request,'login.html')
# 2 自定义中间件
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse
import hashlib
class MyMiddleware(MiddlewareMixin):
def process_request(self, request):
res = request.COOKIES.get('token')
if res:
user_id, user_hash = res.split('|')
m = hashlib.md5()
m.update(user_id.encode())
m.update('世界那么大我想去看看'.encode('utf-8'))
user_hash_1 = str(m.hexdigest())
if user_hash_1 == user_hash:
print(f'token校验成功,这是{user_id}的信息')
else:
obj = HttpResponse(f'token已被修改,校验失败,无法获取{user_id}的信息')
obj.delete_cookie('token')
return obj
else:
print('没有登陆,无法获取用户信息')
def process_response(self, request, response):
try:
user_id = request.user_id
if user_id:
m = hashlib.md5()
m.update(user_id.encode())
m.update('世界那么大我想去看看'.encode('utf-8'))
user_hash = str(m.hexdigest())
token_list = [user_id, user_hash]
token = '|'.join(token_list)
obj = HttpResponse('token设置成功')
obj.set_cookie('token', token)
return obj
except Exception:
return response