一、Django的请求生命周期
1.wsgi,它就是socket的服务端,用于接收用户请求并将请求进行初次封装,然后讲求交给(Flask,Django)进行二次封装。
2.中间件,帮助我们对请求进行校验或在请求对象中添加其他相关数据,例如:csrf、request.session
3.路由匹配
4.视图函数,在视图函数中进行业务逻辑的处理,可能涉及到:ORM、templates ==>>渲染
5.中间件,对响应的数据进行处理。
6.wsgi,将响应的内容发给浏览器。
Django的流程图:
二、什么是wsgi?
web服务网关接口,wsgi是一个协议,实现该写一个的模块:
- wsgiref
- werkzeug
实现其协议的模块本质上就是socket服务端用于接收用户请求,并处理。
一般web框架基于wsgi实现,这样实现关注点分离。
wsgiref示例: from wsgiref.simple_server import make_server def run_server(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) return [bytes('<h1>Hello, web!</h1>', encoding='utf-8'), ] if __name__ == '__main__': httpd = make_server('127.0.0.1', 8000, run_server) httpd.serve_forever() werkzeug示例: from werkzeug.wrappers import Response from werkzeug.serving import run_simple def run_server(environ, start_response): response = Response('hello') return response(environ, start_response) if __name__ == '__main__': run_simple('127.0.0.1', 8000, run_server)
三、什么是HTTP协议?
HTTP协议就是一种传输数据的格式,是基于TCP的之上的。原来学习django框架,是从socket服务端开始学起。
自己创造一个socket服务器来充当网站,浏览器充当socket客户端。
更能够明白http协议到底是什么:
----- 请求头 请求体
----- 响应头 响应体
一次请求响应后,断开链接。(无状态,短连接)
四、常见的请求头
- Content-Type
- User-Agent: 值得不同,返回不同的页面。(根据设备)
- referer: 可以做图片防盗链。
- Host: 当前域名
- cookies: 携带在请求头里面
五、常见的请求体?
Form表单提交:
POST /index http1.1\r\nhost:www.luffycity.com...\r\n\r\nusername=alex&password=123&...
Ajax请求:
POST /index http1.1\r\nhost:www.luffycity.com...\r\n\r\nusername=alex&password=123&...
POST /index http1.1\r\nhost:www.luffycity.com...\r\n\r\n{“username”:"alex","password":123}
补充:django中获取请求体
- request.POST 只能获取这种可是的(nusername=alex&password=123)
- request.body 能够获得 ({“username”:"alex","password":123})
常见的请求方法:
GET/POST/DELETE/PUT/PATCH/OPTIONS
六、中间件
中间件有五种方法:
process_request(self,request)
process_view(self, request, callback, callback_args, callback_kwargs)
process_template_response(self,request,response)
process_exception(self, request, exception)
process_response(self, request, response
中间件的应用场景:
1.登录认证,不再需要在每个函数中添加装饰器。
2.权限,当用户登录的时候获得当前用户的所有权限并放入session,然后再次访问其他页面,获取当前url并在session中进行匹配。如果没有匹配成功,则在中间件返回“无权访问”
3.跨域
4.使用中间件做过什么?
(1)内置 csrf sesson
(2)自定义 登录认证 权限 cors
七、为什么会有跨域?
浏览器具有同源策略才出现跨域。
同源策略:
开放:src
禁止:ajax
解决跨域的两种方法:
1、jsonp:在客户端动态的创建一个script标签
(1) 客户端:创建一个
<script src='http://www.jxntv.cn/data/jmd-jxtv2.html'></script>
<script>
function func(arg){
alert(arg);
}
</script>
(2) 服务端:接收到请求并处理返回值"func('success')"相当于:
<script>
func('success')
</script>
必须与上边一致,而且jsonp只能发送GET请求
2、cors:设置响应头
(1)简单请求
(2)复杂请求
options 请求做预检
PUT/POST
应用:本地测试前后端分离时使用。
在Django中的解决方案:
1、中间件中设置响应头
2、Django中的一个第三方组件:cors
补充Ajax:
jQuery Ajax:
$.ajax({
...
})
原生Ajax:XMLHttpRequest对象:
var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
// 已经接收到全部响应数据,执行以下操作
var data = xhr.responseText;
console.log(data);
}
};
xhr.open('POST', "/test/", true);
// 设置请求头
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset- UTF-8');
// 发送请求
xhr.send('n1=1;n2=2;');
八、视图
在本质的上FBV和CBV是一样的
区别:
FBV:url----->函数(自己定义)
CBV: url----->view (继承的类)
继承的类:
class View(object):
class APIView(View):
class GenericAPIView(views.APIView):
class GenericViewSet(ViewSetMixin, generics.GenericAPIView)
class ModelViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):
九、restful规范
restful是一个规范,规定API如何编写,通过它可以让我们api更加简洁方便维护。
例如,最直观的:
method: get/post/put/delete
原来都是在url中设置的。
除此之外还有:
(1) api
(2)版本
(3)名词(面向资源编程)
(4)条件
(5)响应式设置状态码
(6)返回值
GET: 所有列表
{
code: 10000,
data: [
{'id':1,'title':'xx'},
{'id':1,'title':'yy'},
{'id':1,'title':'zzz'},
]
}
POST: 返回新增的数据
{'id':1,'title':'xx'}
https://www.luffycity.com/api/v2/salary/1/
GET: 获取单条数据
{'id':1,'title':'xx'}
PUT:更新
{'id':1,'title':'xx'}
PATCH: 局部更新
{'id':1,'title':'xx'}
DELETE:删除
(7)错误信息
(8)hypermedia link
什么是接口?
URL
约束:约束继承(实现)了它的类中必须含有的方法
十、django rest framework 框架
作用:快速搭建基于restful规定的接口
1、路由
(1)可以通过as_view传参数,根据请求方式不同执行相应的方法
(2)可以在url中设置一个结尾,类似于:.json
2、视图
帮助了开发者提供了一些类,并在类中提供了多个方法以供我们使用
3、版本
在url中设置version参数,用户请求时候传入参数。在request.version中获取版本,根据版本不同做不同处理
4、认证
写一个类并注册到认证类,在类的authtocate方法中编写认证逻辑
认证成功(user,auth)
raise AuthticateFaild(....)
None
5、权限
写一个类并注册到权限类,在类的的has_permission方法中编写认证逻辑。
True
False
6、访问频率的限制
- 访问频率控制原理:
匿名:
1.1.1.1:[时间,时间,时间,时间,]
登录:
user:[时间,时间,时间,时间,]
默认将访问记录放在缓存中:redis/memcached
写一个类并注册到频率类,在类的的 allow_request/wait 方法中编写认证逻辑。
allow_request
True
False 如果返回False,那么就要执行wait
7、解析器
根据ContentType请求头,选择不同解析器对 请求体中的数据进行解析。
POST /index/ http1.1.\r\nhost:11.11.11.11\r\nContent-Type:url-formendo.... \r\n\r\nuser=alex&age=123
POST /index/ http1.1.\r\nhost:11.11.11.11\r\nContent-Type:application/json\r\n\r\n{....}
8、序列化
对queryset序列化以及对请求数据格式校验。
from rest_framework.serializers import Serializer
class XX(Serializer):
pass
ser =XX(queryset,many=True) # ListSerializer对象
ser =XX(obj, many=False) # XX对象
- 列表生成式
- 根据字符串的形式,自动导入模块并使用反射找到模块中的类【参考:s9day108】。
9、分页
对从数据库中获取到的数据进行分页处理: SQL -> limit offset
根据页码:http://www.luffycity.com/api/v1/student/?page=1&size=10
根据索引:http://www.luffycity.com/api/v1/student/?offset=60&limit=10
根据加密:http://www.luffycity.com/api/v1/student/?page=erd8
页码越大速度越慢,为什么以及如何解决?
原因:页码越大向后需要扫描的行数越多,因为每次都是从0开始扫描。
解决:
限制显示的页数
记录当前页数据ID最大值和最小值,再次分页时,根据ID现行筛选,然后再分页。
10、渲染器
根据URL中传入的后缀,决定在数据如何渲染到到页面上。
十一、ORM的补充
a. 需求: 只取某n列
queryset=[ {},{}]
models.User.objects.all().values( 'id','name')
queryset=[ (),()]
models.User.objects.all().values_list( 'id','name')
queryset=[ obj,obj]
b. - only
result = models.User.objects.all().only('id','name','age')
c. - defer
# result = models.User.objects.all().defer('id','name','age')
for item in reuslt:
print(item.id,item.name,item.age)
示例:
class Depart(models.Model): 5个部门
title = models.CharField(...)
class User(models.Model): 10个用户
name = models.CharField(...)
email = models.CharField(...)
dp = models.FK(Depart)
1.之前:11次单表查询
result = User.objects.all()
for item in result:
print(item.name,item.dp.title)
2. seleted_related,主动做连表查询(1次链表)
result = User.objects.all().seleted_related('dp')
for item in result:
print(item.name,item.dp.title)
问题:如果链表多,性能越来越差。
3. prefetch_related:2次单表查询
# select * from user ;
# 通过python代码获取:dp_id = [1,2]
# select * from depart where id in dp_id
result = User.objects.all().prefetch_related('dp')
for item in result:
print(item.name,item.dp.title)
注:数据量比较大,不会使用FK,允许出现数据冗余。
- select_related,连表操作,相当于主动做join
- prefeth_related,多次单表操作,先查询想要的数据,然后构造条件,如:id=[1,2,3],再次查询其他表根据id做条件。
- only
- defer
- F 更新数据库字段
- Q 构造复杂条件
- 通过ORM写偏原生SQL:
- extra
Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,))
Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])
Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid'])
- raw
# 执行原生SQL
models.UserInfo.objects.raw('select * from userinfo')
# 如果SQL是其他表时,必须将名字设置为当前UserInfo对象的主键列名
models.UserInfo.objects.raw('select id as nid from 其他表')
# 为原生SQL设置参数
models.UserInfo.objects.raw('select id as nid from userinfo where nid>%s', params=[12,])
name_map = {'first': 'first_name', 'last': 'last_name', 'bd': 'birth_date', 'pk': 'id'}
Person.objects.raw('SELECT * FROM some_other_table', translations=name_map)
- 原生SQL
from django.db import connection, connections
cursor = connection.cursor() # cursor = connections['default'].cursor()
cursor.execute("""SELECT * from auth_user where id = %s""", [1])
row = cursor.fetchone() # fetchall()/fetchmany(..)
PS: 选择数据库
queryset = models.Course.objects.using('default').all()