1、django请求生命周期
更新时间:
2018/6/25 18:04
- wsgi, 他就是socket服务端,用于接收用户请求并将请求进行初次封装,然后将请求交给web框架 (Flask、Django)
- 中间件,帮助我们对请求进行校验或在请求对象中添加其他相关数据,例如:csrf、request.session - 路由匹配 根据浏览器发送的不同url去匹配不同的视图函数
- 视图函数,在视图函数中进行业务逻辑的处理,可能涉及到:orm、templates => 渲染
- 中间件,对响应的数据进行处理。
- wsgi,将响应的内容发送给浏览器。
2、什么是wsgi 中文名:web服务器网关接口 实现该协议的模块:
- wsgiref
- werkzurg
- uwsgi
PythonWeb服务器网关接口(Python Web Server Gateway Interface,缩写为WSGI)是Python应用程序或框 架和Web服务器之间的一种接 口,是一种协议,实现该协议的模块有uwsgi、wsgiref和werkzeug,这些模 块本质上就是socket服务端,用于接收用户请求并将请求进行初次封装,然后将请求交给web框架 (Flask、Django)
3、FBV和CBV本质是一样的,没有优劣之分,可根据不同的需求来使用 视图
- FBV 基于函数的视图 url - 函数
- CBV 基于类的视图 url - view
4、restful 规范(10) 什么是接口?
- URL - 约束
# 约束继承(实现)了他的类中必须含有IFoo中的方法 interface
IFoo:
def func(self): pass
print(11111)
5、restful 10大规范: 首先restful是一种软件架构风格或者说是一种设计风格,并不是标准,它只是提供了一组设计原则
和约束条件,主要用于客户端和服务器交互类的软件。 就像设计模式一样,并不是一定要遵循这些原则,而是基于这个风格设计的软件可以更简洁,更有层
次,我们可以根据开发的实际情况, 做相应的改变。它里面提到了一些规范,例如:
1、在url接口中推荐使用Https协议,让网络接口更加安全(Https是Http的安全版,即HTTP下加入 SSL层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL(安全套接层协议))
2、url中可以体现这是个API接口 3、url中还可以体现版本号,不同的版本可以有不同的接口,使其更加简洁,清晰
4、restful 提倡面向资源编程,所以在url接口中尽量要使用名词,不要使用动词
5、此外url中还可以添加条件去筛选匹配
6、可以根据Http不同的method,进行不同的资源操作(5种方法:GET / POST / PUT / DELETE
/ PATCH) 7、响应式应该包含状态码
1) 200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
2) 201 CREATED -[POST/PUT/PATCH]:用户新建或修改数据成功。
3) 202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
4) 204 NO CONTENT - [DELETE]:用户删除数据成功。
5) 400 INVALID REQUEST -[POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行 新建或修改数据的操作,该操作是幂等的。
6) 401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
7) 403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
8) 404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该
操作是幂等的。
9) 406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有
XML格式)。
10) 410Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
11) 422Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
12) 500INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否
成功。
8、应该有返回值,而且格式为统一的json格式
9、应该返回错误信息
10、返回结果中要提供帮助链接,即API最好做到Hypermedia。
6、rest framework框架(10大组件)&& 谈谈你对 django rest framework框架的认识? - 路由,
- 可以通过as_view传参数,根据请求方式不同执行相应的方法
- 可以在url中设置一个结尾,类似于: .json - 视图,
- 帮助开发者提供了一些类,并在类中提供了多个方法以供我们使用。 - 版本,
- 认证,
- 写一个类并注册到认证类,在类的的authticate方法中编写认证逻辑。
- 认证成功(user,auth) - raise AuthticateFaild(....) - None
- 权限
- 写一个类并注册到权限类,在类的的has_permission方法中编写认证逻辑。
- True
- False - 频率限制
- 写一个类并注册到频率类,在类的的 allow_request/wait 方法中编写认证逻辑。 allow_request
- True
- False 如果返回False,那么就要执行wait - 解析器,
- 根据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{....}
- 分页
- 对从数据库中获取到的数据进行分页处理: 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现行筛选,然后再分页。 - 序列化
- 对queryset序列化以及对请求数据格式校验。 - 渲染器
- 根据URL中传入的后缀,决定数据如何渲染到到页面上。 面试题:你写的视图类都继承过哪些类?
class View(object):
class APIView(View):
class GenericAPIView(views.APIView):
class GenericViewSet(ViewSetMixin, generics.GenericAPIView)
class ModelViewSet(mixins.CreateModelMixin,
7、为什么会出现跨域问题? 跨域是指从一个域名的网页去请求另一个域名的资源。浏览器出于安全的考虑,不允许不同源
的请求;
因为浏览器收到同源策略的限制,当前域名的js只能读取同域下的窗口属性。
同源策略:不同的域名, 不同端口, 不同的协议不允许共享资源,保障浏览器安全。
处理方法:
1、jsonp 跨域方法
同源策略会阻止ajax请求;不阻止具有src属性的标签,所以动态创建script标签即可。具体如下:
我们提供一个 script 标签。请求页面中的数据, 同时传入一个回调函数的名字。服务器端得到名字后,
拼接函数执行格式的字符串。发送回浏览器。script 在下载代码以后并执行, 执行的就是这个函数调 用形式的字符串,
因此就将本地函数调用了.同时拿到了从服务器端得到的数据。 2、CORS 跨域
CORS 是在 es5 之后提出的跨域方案. 只需要在服务器配置一个跨域响应头接口
允许你的域名来获取我的数据 response['Access-Control-Allow-Origin'] = "*"
允许你携带Content-Type请求头 response['Access-Control-Allow-Headers'] = "Content-Type"
允许你发送DELETE,PUT response['Access-Control-Allow-Methods'] = "DELETE,PUT"
CORS请求头的注意事项: - 简单请求
- 复杂请求
- 会发送两次请求
- 首先会发送options请求做预检
- 然后再发送真正的 PUT/POST....请求
只要同时满足以下两大条件,就属于简单请求。 (1) 请求方法是以下三种方法之一:
HEAD GET POST
(2)HTTP的头信息不超出以下几种字段: Accept
Accept-Language Content-Language
Last-Event-ID Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、
非简单请求的CORS请求,会在正式通信之前,增加一次HTTP(options)查询请 求,称为"预检"请求(preflight)。
浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使 用哪些HTTP动词和头信息字段。只有得到肯定答复,
浏览器才会发出正式的XMLHttpRequest请求,否则就报错。
与jsonp相比的优点:
1、 JSONP只能实现GET请求,而CORS支持所有类型的HTTP请求。
2、 使用CORS,开发者可以使用普通的XMLHttpRequest发起请求和获得数据,比起JSONP
有更好的错误处理。
3、 JSONP主要被老的浏览器支持,它们往往不支持CORS,而绝大多数现代浏览器都已经
支持了CORS。
8、面试题 (1)
var name = '景女神'
function Foo(name,age){ this.name = name;
this.age = age; this.getName = function(){
console.log(this.name); # 文州
(function(){
console.log(this.name); # 女神
})()
obj = new Foo('文州',19) obj.getName()
(2)
var name = '景女神'
function Foo(name,age){ this.name = name;
this.age = age; this.getName = function(){
console.log(this.name); # 文州
var that = this
(function(){
console.log(that.name); # 文州 })()
} }
obj = new Foo('文州',19) obj.getName()
(3)
var name = '景女神'
obj = {
name:'文州',
age: 19,
getName:function(){
} }
console.log(this.name); # 文州
var that = this
(function(){
console.log(that.name); # 文州 })()
obj.getName()
9、你理解的Http协议? - 建立在tcp之上
- 一次请求一次响应然后断开连接(无状态、短连接)
- 请求和响应
发送:请求头\r\n\r\n请求体
host:www.luffy.com\r\ncontent-type:application/json\r\n\r\n请求体 响应:响应头\r\n\r\n响应体
Http协议就是一个传输数据格式(官名是超文本传输协议)。 我之前在学习django框架的时候,是从socket服务端开始学起。 自己创造了一个socket服务器来充当:网站。 浏览器当socket客户端。
更清楚的明白到底http协议是什么?
- 请求头 请求体
- 响应头 响应体
一次请求响应后,断开连接(无状态,短连接)。
常见的请求头:
- Content-Type
原则上浏览器会根据Content-Type来决定显示返回的消息体内容的格
式。
- User-Agent
- referer,
来的,可以做图片防盗链。
告诉HTTP服务器,客户端使用的操作系统和浏览器的名称和版本 提供了Request的上下文信息的服务器,告诉服务器我是从哪个链接过
- cookie
常见的状态码:
-200
- 302
最重要的header,将cookie的值发送给HTTP服务器
请求成功 OK
重定向,新的URL会在response 中的Location中返回,浏览器将会自动使用新 的URL发出新的Request
- 304 - 400 - 403 - 404 - 500 - 503
代表上次的文档已经被缓存了, 还可以继续使用 客户端请求与语法错误,不能被服务器所理解 服务器收到请求,但是拒绝提供服务,比如没有找到cookie 请求资源不存在(输错了URL) 服务器发生了不可预期的错误 服务器当前不能处理客户端的请求,一段时间后可能恢复正常
常见的请求方法:
- GET/POST/DELETE/PUT/PATCH/OPTIONS
常见的请求体: 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
- request.body
10、中间件 定义:中间件是一个用来处理Django的请求和响应的框架级别的钩子。它是一个轻量、低级别的插 件系统, 用于在全局范围内改变Django的输入和输出。每个中间件组件都负责做一些特定的功能。 说的直白一点中间件是帮助我们在视图函数执行之前和执行之后都可以做一些额外的操作, 它本质上就是一个自定义类,类中定义了几个方法,Django框架会在请求的特定的时间去执行这些 方法。
内置的5个方法:
process_request(self,request)
process_view(self, request, view_func, view_args, view_kwargs)
process_template_response(self,request,response)
process_exception(self, request, exception)
process_response(self, request, response)
使用中间件做过什么? - 内置
- session - 自定义
- 登录认证:不再需要在每个函数中添加装饰器
- 权限:当用户登录时候获取当前用户所有权限并放入session,然后再次访问其他页
面,获取当前url并在session中
进行匹配。如果没有匹配成功,则在中间件返回“无权访问”
- 跨域,
- jsonp,动态创建一个script标签。
- cors,设置响应头
应用:本地开始前后端分离的时使用。
11. 如何实现的访问频率控制? 使用IP/用户账号作为键,每次的登陆时间戳作为值,构造一个字典形式的数据,存起来,每次登 陆时把时间戳列表的元素进行判断,把超时的删掉,再计算列表剩余的元素数就能做到频率限制了 匿名用户:使用IP控制,但是无法完全控制,因为用户可以换代理IP 登录用户:使用账号控制,但是如果有很多账号,也无法限制
12、django组件:contenttype 组件的作用:可以通过两个字段让表和N张表创建FK关系
例如:
class Course(models.Model):
"""专题课程"""
name = models.CharField(max_length=128, unique=True)
course_img = models.CharField(max_length=255)
# 不会在数据库生成列,只用于帮助你进行查询 policy_list = GenericRelation("PricePolicy")
class PricePolicy(models.Model):
"""价格与有课程效期表"""
content_type = models.ForeignKey(ContentType) # 关联course or degree_course
object_id = models.PositiveIntegerField()
#不会在数据库生成列,只用于帮助你进行添加和查询 content_object = GenericForeignKey('content_type', 'object_id')
13、原生Ajax:XMLHttpRequest对象: var xhr = new XMLHttpRequest() xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
// 已经接收到全部响应数据,执行以下操作
var data = xhr.responseText;
};
xhr.open('POST', "/test/", true);
// 设置请求头
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset-UTF-8');
// 发送请求 xhr.send('n1=1;n2=2;');
14、ORM相关
- only only()里的字段,表示只取相关字段的内容,注意:当only后进行for循环取值 时,如果取那些不在only括号内字段,会导致再次进行数据库查询,效率会差很多
和only相反,括号内的字段表示不取相关字段的内容
用于主动进行连表查询 FK时用,相当于主动做join(一次连表查询,连表查询
多次单表操作,避免连表查询损耗;先查询想要的数据,然后构造条件,如:
- defer
- seleted_related
速度会相对慢点)
- prefetch_related
id=[1,2,3],再次查询其他表根据id做条件。
- F - Q
用来更新获取原来值的功能,例如:Uinfo.objects.all().update(age=F("age")+1) 用于构造复杂的查询条件的,使用方法有对象方法和直接创建创建对象方法
Q功能详解: 数据库的查询条件我们可以使用filter,在filter里面的可以是两个条件他们之间是and的关
系,也可以是一个字典,例如下面的代码 models.Uinfo.objects.all().filter(id=1,name='李伟') conditon={
'id':'1',
'name':'李伟' }
models.Uinfo.objects.all().filter(**conditon)
除了上面的方法,我们还可以加Q的对象,例如:
from django.db.models import Q
models.Uinfo.objects.all().filter(Q(id=1))#条件是id为1的时候
models.Uinfo.objects.all().filter(Q(id=1)|Q(id__gt=3))#条件是或的关系
models.Uinfo.objects.all().filter(Q(id=1) & Q(id=4))# 条件是and的关系
Q的另外一种用法
#q1 里面的条件都是or的关系
q1=Q()
q1.connector = 'OR'
q1.children.append(('id',1))
q1.children.append(('id',3))
q1.children.append(('id',6))
#q2里面的条件都是or的关系
q2.connector = 'OR' q2.children.append(('c',2)) q2.children.append(('c',4)) q2.children.append(('c',6))
#con 通过and的条件把q1和q2 联系到一块 con=Q()
con.add(q1,'AND') con.add(q2,'AND')
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
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()
a. 需求: 只取某n列 queryset=[ {},{}]
models.User.objects.all().values( 'id','name')
queryset=[ (),()] models.User.objects.all().values_list( 'id','name')
queryset=[ obj,obj]
result = models.User.objects.all().only('id','name','age')
# result = models.User.objects.all().defer('id','name','age')
for item in reuslt:
print(item.id,item.name,item.age)
b. 需求: 打印所有用户姓名以及部门名称
class depart: title = ....
class User:
name = ...
dp = FK(depart)
c.
select * from user
result = models.User.objects.all()
for item in result:
print(item.name)
select * from user left join depart on user.dp_id = depart.id
result = models.User.objects.all().selected_related('dp')
for item in result:
print(item.name,item.dp.title )
注意:数据量比较大,不会使用FK,允许出现数据冗余,为了提高查询访问效率。
15、支付宝支付相关 1.正式申请:需要营业执照 2.测试:沙箱测试环境 3.公钥私钥:支付宝的公钥和商家的私钥 4.加密方式:rsa
成功:return HttpResponse('success')
6.精度:小数点后两位
16、git rebase的作用? 保持提交记录的整洁。
17、使用git时,如果代码出现bug,你们是如何解决?
创建一个bug分支,然后进行bug处理,处理完毕后,合并到master分支。
删除bug分支
回到dev分支继续开发。
18、redis是什么? 用于操作内存的软件。
数据存储模式有2种:cache-only,persistence; 1、cache-only即只做为“缓存”服务,不持久数据,数据在服务终止后将消失,此模式下也将 不存在“数据恢复”的手段,是一种安全性低/效率高/容易扩展的方式; 2、persistence即为内存中的数据持久备份到磁盘文件,在服务重启后可以恢复,此模式下数据 相对安全。
对于persistence持久化存储,Redis提供了两种持久化方法: Redis DataBase(简称RDB)
RDB是在某个时间点将数据写入一个临时文件,持久化结束后,用这个临时文件 替换上次持久化的文件,达到数据恢复。 优点:使用单独子进程来进行持久化,主进程不会进行任何IO操作,保证了redis 的高性能 缺点:RDB是间隔一段时间进行持久化,如果持久化之间redis发生故障,会发生 数据丢失。所以这种方式更适合数据要求不严谨的时候
Append-only file (简称AOF)
Append-only file,将“操作 + 数据”以格式化指令的方式追加到操作日志文件的尾
部,在append操作返回后(已经写入到文件或者即将写入),才进行实际的数据变
更,“日志文件”保存了历史所有的操作过程;当server需要数据恢复时,可以直
接replay此日志文件,即可还原所有的操作过程。AOF相对可靠,它和mysql中
bin.log、apache.log、zookeeper中txn-log简直异曲同工。AOF文件内容是字符串,非
常容易阅读和解析。
优点:可以保持更高的数据完整性,如果设置追加file的时间是1s,如果redis发生
故障,最多会丢失1s的数据;且如果日志写入不完整支持redis-check-aof来进行日志
修复;AOF文件没被rewrite之前(文件过大时会对命令进行合并重写),可以删除
其中的某些命令(比如误操作的flushall)。
缺点:AOF文件比RDB文件大,且恢复速度慢。
- 相当于是大字典
- 单进程单线程
- 5大数据类型
字符串 列表 集合 字典
redis的列表操作
- 左右操作,双向链接
- 阻塞
- 通过yield创建一个生成器完成一点一点获取(通过字典操作的源码来的灵感)
def list_iter(key,count=100): index = 0
while True:
data_list = conn.lrange('k1', index, index+count-1)
if not data_list:
return index += count
for item in data_list: yield item
PS: 队列:先进先出 栈:后进先出
- 事务+一次发送多个命令:
conn = redis.Redis(host='47.94.172.250',port=6379,password='Luffy!4321')
pipe = conn.pipeline(transaction=True) pipe.multi()
pipe.set('k2','123') pipe.hset('k3','n1',666) pipe.lpush('k4','laonanhai')
pipe.execute()
19、redis中使用连接池 本质,维护一个已经和服务端连接成功的socket。 以后再次发送数据时,直接获取一个socket,直接send数据。
20、解决数据库页数越大速度越慢。 - 限制页数
- 记录当前页最大ID、最小ID 错误答案:
- 扫描索引表
- 再去数据库表中获取数据
21、做代码review 如何做代码review?
- 创建review分支:
- 带你的人
22、问题: 怎么在github上给别人修改代码? 1、fork
github上 不是有个fork么, 好,那么给别人的项目提交代码的方式就有了,先fork下别人的代 码,于是你的github上就出现了同名的项目,这个项目就属于你自己了,你把这个自己的项目 git clone到本地,修修改改,然后push到你自己的项目里;
2、pull request
在github上你的项目页面有个按钮,叫Pull request,对 你点它就会把你的修改发到对方的项目
里,人还会收到邮件呢,由原项目主人决定是否接受你的修改
23、github不用反复输入用户名密码登录 Https:
https://用户名:密码@github.com/WuPeiqi/dbhot.git
git remote add origin https://用户名:密码@github.com/WuPeiqi/dbhot.git
SSH:
git@github.com:WuPeiqi/dbhot.git
步骤:
1、大多数 Git 服务器都会选择使用 SSH 公钥来进行授权。系统中的每个用户都必须提
供一个公钥用于授权,没有的话就要生成一个。生成公钥的过程在所有操作系统上都差
不多。 首先先确认一下是否已经有一个公钥了。SSH 公钥默认储存在账户的主目录下的
~/.ssh 目录。进去看看。
命令:cd ~/.ssh
2、关键是看有没有用 something 和 something.pub 来命名的一对文件,这个 something 通
常就是 id_dsa 或 id_rsa。有 .pub 后缀的文件就是公钥,另一个文件则是密钥。假如没有
这些文件,或者干脆连 .ssh 目录都没有,可以用 ssh-keygen 来创建。该程序在
Linux/Mac 系统上由 SSH 包提供,而在 Windows 上则包含在 MSysGit 包里。
命令:ssh-keygen
3、它先要求你确认保存公钥的位置(.ssh/id_rsa),然后它会让你重复一个密码两次,
如果不想在使用公钥的时候输入密码,可以留空。
现在,所有做过这一步的用户都得把它们的公钥给你或者 Git 服务器的管理员(假设
SSH 服务被设定为使用公钥机制)。他们只需要复制 .pub 文件的内容然后发邮件给管理
员。
命令:cat ~/.ssh/id_rsa.pub
4、在GitHub上的个人中心的settings里的 SSH and GPG keys 中添加已生成的 SSH keys 即
可。
24、 在django中应用redis:
1. 自定义使用redis import redis
2. 使用第三方组件
pip3 install django-redis
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"CONNECTION_POOL_KWARGS": {"max_connections": 100}
# "PASSWORD": "密码",
}
} }
使用:
conn = get_redis_connection("default")
25、关于微信消息推送
网址:https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login
基于:已认证的微信服务号 主动推送微信消息。
前提:关注服务号
环境:沙箱环境
总结:
-
注册账号
appID:wx89085e915d351cae appsecret:64f87abfc664f1d4f11d0ac98b24c42d
网页授权获取用户基本信息:47.98.134.86 或 域名
-
关注公众号(已认证的服务号)
-
生成二维码,用户扫描; 将用户信息发送给微信,微信再将数据发送给设置redirect_uri地址(md5值)
-
回调地址:47.98.134.86/callback/ - 授权
- 用户md5
- 获取wx_id在数据库中更新设置:wx_id
-
发送消息(模板消息)
- wx_id
- access_token(2小时有效期)
26、互联网协议OSI四层、五层和七层模型
四层:应用层、传输层、网络层和网络接口层 五层:应用层、传输层、网络层、数据链路层和物理层
每层运行常见物理设备: 传输层:四层交换机、四层路由器 网络层:路由器和三层交换机 数据链路层:网桥、以太网交换机、网卡 物理层:中继器、集线器和双绞线
每层运行常见的协议: 传输层:TCP和UDP协议 网络层:IP协议 数据链路层:arp协议
27、理解socket Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其 实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面, 对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。 其实socket就是一个模块。我们通过调用模块中已经实现的方法建立两个进程之间的连接和通信。 也有人将socket说成ip+port,因为ip是用来标识互联网中的一台主机的位置,而port是用来标识这台 机器上的一个应用程序。 所以我们只要确立了ip和port就能找到一个应用程序,并且使用socket模块来与之通信。
28、理解TCP协议和UDP协议 TCP---传输控制协议,提供的是面向连接、可靠的字节流服务。当客户和服务器彼此交换数据前,必 须先在双方之间建立一个TCP连接,之后才能传输数据。TCP提供超时重发,丢弃重复数据,检验 数据,流量控制等功能,保证数据能从一端传到另一端。 UDP---用户数据报协议,是一个简单的面向数据报的运输层协议。UDP不提供可靠性,它只是把应 用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地。由于UDP在传输数据报前 不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快。
29、理解TCP协议中的三次握手和四次挥手 建立连接的三次握手
TCP是因特网中的传输层协议,使用三次握手协议建立连接。当主动方发出SYN连接请求后, 等待对方回答SYN+ACK[1],并最终对对方的 SYN 执行 ACK确认。这种建立连接的方法可以 防止产生错误的连接。[1]
TCP三次握手的过程如下:
客户端发送SYN(SEQ=x)报文给服务器端,进入SYN_SEND状态。 服务器端收到SYN报文,回应一个SYN (SEQ=y)ACK(ACK=x+1)报文,进入 SYN_RECV状态。 客户端收到服务器端的SYN报文,回应一个ACK(ACK=y+1)报文,进入Established状 态。 三次握手完成,TCP客户端和服务器端成功地建立连接,可以开始传输数据了。
断开连接的四次挥手: 假设Client端发起中断连接请求,也就是发送FIN报文。Server端接到FIN报文后,意思是说"我 Client端没有数据要发给你了",但是如果你还有数据没有发送完成,则不必急着关闭Socket, 可以继续发送数据。所以你先发送ACK,"告诉Client端,你的请求我收到了,但是我还没准备 好,请继续你等我的消息"。这个时候Client端就进入FIN_WAIT状态,继续等待Server端的FIN 报文。当Server端确定数据已发送完成,则向Client端发送FIN报文,"告诉Client端,好了,我 这边数据发完了,准备好关闭连接了"。Client端收到FIN报文后,"就知道可以关闭连接了,但 是他还是不相信网络,怕Server端不知道要关闭,所以发送ACK后进入TIME_WAIT状态,如 果Server端没有收到ACK则可以重传。“,Server端收到ACK后,"就知道可以断开连接了"。 Client端等待了2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,我Client端也可 以关闭连接了。Ok,TCP连接就这样关闭了!
31、TCP协议中在断开连接时,为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时 间)才能返回到CLOSE
状态? 虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假象网络是不 可靠的,有可以最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。
32、接口的幂等性?(是否会造成2次伤害) 一个接口通过1次相同的访问,再对该接口进行N次相同的访问时候,对资源不造影响,那么就认为 接口具有幂等性。
比如:
GET, 第一次获取结果、第二次也是获取结果对资源都不会造成影响,幂等。
POST,第一次新增数据,第二次也会再次新增,非幂等。
PUT, 第一次更新数据,第二次不会再次更新,幂等。
PATCH,第一次更新数据,第二次不会再次更新,非幂等。
DELTE,第一次删除数据,第二次不在再删除,幂等。
2、默认端口号不同,Http是80端口,Https是443端口; 3、Http是超文本传输协议,信息是明文传输,可以自定义证书,在服务端创建一对证书,在客户 端必须携带证书;
Https是需要到ca申请证书(购买),在服务端创建一对证书,在客户端去机构获 取证书,数据加密后发给
咱们的服务单,而证书机构需要把公钥给改机构。
34、路飞学城相关
1)购物车购买数量有限制吗? -- 有,防止有人恶意破坏,把大量商品加入购物车,不购买,占
用redis的负担
2)购物车是否设置超时时间? -- 可以设置,也可以不设置,其实只设置一下购物车购买数量即
可。 (.conn.expire("shopping_car_1_1", 60*30))
3)为什么把购物车信息放入redis?
1.购物车的操作只是个临时状态,不需要放入数据库;
2.需要频繁对购物车进行修改,放入redis的话操作速度快。 4)为什么要开发这个项目?
1.提高在线 教育的 完课率 (学成率) 2.项目的具体内容有:
课程分两种,学位课和专题课 购买课程时,可以选择不同的价格策略来进行学习 闯关式学习,对于学位课来说,我们不会一次性把全部课程的视频给学员,会进行闯关 式学习,
只有当当前阶段通过导师测试后,学员才有资格进行下一步的学习 导师筛选,我们对导师会有很严格的筛选,只有通过我们的严格考核,才有资格做我们 的导师 导师监督,人都有惰性心理,所以我们会对我们导师有严格的要求,导师必须对学员负 责,每天都要有跟进记录,
不管是电话还是QQ、微信,都要有记录上传系统,这些都会和他们的奖惩关 联
导师答疑时间,不同的课程,不同的价位,会有不同的答疑时间限制,在规定时间内, 导师必须给学员进行回复 报名时学员有权利选择导师,在学习期间,学员也有权利提出更换导师 奖惩措施,对于导师来说,每一个导师带领一个学员时,我们都会给导师账户存入一定 的奖金,直到学员毕业后才能提现,在学员学习期间,如果导师有不负责现象,我们会 做出相应的惩罚措施,做一定扣除奖金机制;对于学员来说,学员能够按时或者提前在 规定时间内学习完一个模块,作业和测试成绩优异的话,我们会给一定的奖励。
5)这个项目的开发周期和团队构成 周期:初期开发到上线时间是大约四个月,之后一直在做网站的维护,bug的处理,开展一些 活动,广告之类的,
还有一些小功能的开发 团队构成:
开发
导师后台:stark组件和rbac 大约需要1人
管理后台:stark组件和rbac 大约需要1人
api接口 运维(1人)
测试(1人) 产品经理(1人) UI设计(1人) 运营(1人) 销售(5人) 全职导师(2人) 签约导师(。。。)
6)购买课程流程 加入购物车 去结算中心
去支付 7)路飞的主站业务
课程
课程列表
课程详细
课程大纲、导师、推荐课程
价格策略
加入购物车
课程章节和课时
常见问题
评论
深科技 文章列表
文章详细 收藏 点赞 评论 推荐其他优秀文章
支付
购物车
结算中心 立即支付 知识点:
大约需要1人 2.5个人,组长偶尔会负责一些,不算是整个人力
redis
支付宝支付
微信消息推送
构建数据结构
优惠券+贝里+支付宝支付
个人中心
课程中心 8)播放视频:cc视频
加密 非加密
基于wsgi协议部署的,使用wsgiref模块实现此协议; Flask:微型的小而精的Web框架,可扩展性强,内置的组件很少,需要引入第三方组件实现功能业 务,如果开发简单的项目,
使用Flask比较快速和方便。如果开发大型项目,需要引入大量的第三方组件,这时Flask会越 来越像Django框架。基于wsgi协议部署,使用werkzeug模块实现此协议,模板系统由 Jinja2提 供。
Tornado:是一个轻量级的Web框架,可扩展性强,用于快速开发简单程序,用于强大的异步非阻塞 和内置WebSocket功能。
36、Flask 10大基础知识 1、配置文件
Flask的配置文件全都放在 app.config 里,怎样修改配置文件呢? 首先在项目里创建一个 settings.py 文件,在文件里可以自定义修改配置文件,比如: class Config(object):
DEBUG = False
TESTING = False
DATABASE_URL = 'sqlite://:memory:'
class ProductionConfig(Config):
DATABASE_URL = 'mysql://user@localhost/foo'
class DevelopmentConfig(Config): DEBUG = True
class TestingConfig(Config): TESTING = True
然后在 app.py 文件里引入自定义修改的配置文件
app.config.from_object("settings.DevelopmentConfig")
# 可以根据不同的测试环境,引入不同的配置文件
PS:内部的实现原理知识点 -- 得到一个字符串的路径 "settings.Foo" ,怎样找到类并获取其中
的大写的静态字段。
在 settings.py 文件里设置自定义的 Foo 类
静态属性必须是大写的,
class Foo:
DEBUG = True TEST = True
在 app.py 文件中引入
path = "settings.Foo"
p,c = path.rsplit('.',maxsplit=1) m = importlib.import_module(p) cls = getattr(m,c)
# 如果找到这个类? for key in dir(cls):
if key.isupper(): print(key,getattr(cls,key))
2、路由系统 通常有3种定义路由函数的方法: 1.使用flask.Flask.route() 修饰器。 2.使用flask.Flask.add_url_rule()函数。
def index():
return 'index'
app.add_url_rule('/index', None, index)
3、视图
# 提供Pythonimport语法和(__import__()函数)的实现,提供支
# 相当于实现了 # 相当于实现了
import settings settings.Foo
-- 第一个参数是 url 路由,第二个参数是 endpoint
,第三个参数是 函数名 3.直接访问基于werkzeug路由系统的flask.Flask.url_map。 最常用的@app.route()装饰器
能够加参数:
参数一,url @app.route("/index") -- url 没有参数时
@app.route("/index/<int:nid>") -- url 有参数时,"<>" 这个是格式,int 是 定义参数类型
参数二,methods 默认的是 GET 请求 @app.route("/index/<int:nid>", methods=
['GET','POST'])
参数三,endpoint 定义 url 别名,用于反向生成 url ,不写时默认的是被装饰的函数名
@app.route("/index/<int:nid>", methods=['GET','POST'],endpoint='index') 反向解析时,需要引入 url_for ,使用: url_for('endpoint') 或者 url_for('index',nid=555)
参数: rule,
view_func, endpoint=None, methods=None, strict_slashes=None, redirect_to=None,
defaults=None, {'k':'v'}为函数提供参数
URL规则
视图函数名称
名称,用于反向生成URL,即: url_for('名称')
允许的请求方式,如:["GET","POST"] 对URL最后的 / 符号是否严格要求,
重定向到指定地址 默认值,当URL中无参数,函数需要参数时,使用defaults=
subdomain=None,
子域名访问
import functools
from flask import Flask,views app = Flask(__name__)
def wrapper(func): @functools.wraps(func) def inner(*args,**kwargs):
return func(*args,**kwargs) return inner
class UserView(views.MethodView): methods = ['GET']
decorators = [wrapper,]
def get(self,*args,**kwargs): return 'GET'
def post(self,*args,**kwargs): return 'POST'
app.add_url_rule('/user',None,UserView.as_view('uuuu'))
if __name__ == '__main__': app.run()
自定义路由匹配正则:
from flask import Flask,url_for
app = Flask(__name__)
# 步骤一:定制类
from werkzeug.routing import BaseConverter
class RegexConverter(BaseConverter):
""" 自定义URL匹配正则表达式 """
def __init__(self, map, regex): super(RegexConverter, self).__init__(map) self.regex = regex
def to_python(self, value): """
路由匹配时,匹配成功后传递给视图函数中参数的值
"""
return int(value)
def to_url(self, value): """
使用url_for反向生成URL时,传递的参数经过该方法处理,返回的值用于 生成URL中的参数
:param value: :return:
"""
val = super(RegexConverter, self).to_url(value)
return val
# 步骤二:添加到转换器 app.url_map.converters['reg'] = RegexConverter
"""
1. 用户发送请求
2. flask内部进行正则匹配
3. 调用to_python(正则匹配的结果)方法
4. to_python方法的返回值会交给视图函数的参数
"""
# 步骤三:使用自定义正则 @app.route('/index/<reg("\d+"):nid>') def index(nid):
print(nid,type(nid))
print(url_for('index',nid=987)) return "index"
if __name__ == '__main__': app.run()
4、请求相关 请求相关信息
request.method request.args request.form request.values request.cookies request.headers request.path request.full_path
obj = request.files['the_file_name'] obj.save('/var/www/uploads/' + secure_filename(f.filename))
5、响应 响应体:
1.return "asdfgh" -- 直接返回字符串
2.return jsonify({'k1':'v1'}) -- 返回序列化后的字符串
3.return render_template('index.html') -- 返回一个页面
4.return redirect('www.baidu.com') -- 重定向
定制响应头:
obj = make_response('kkk')
obj.headers['hhhhh'] = '555'
obj.set_cookie('key', 'value')
return obj -- 返回Response对象,make_response()函数可以接受1个,2个或3个参数
问题:怎样实现登陆验证功能 方法一:
@app.route('/index') def index():
if not session.get('user'):
return redirect(url_for('login'))
return render_template('index.html')
session.get('user') 判断,这个方法显得很麻烦,不用
方法二:
import functools
def auth(func): @functools.wraps(func)
def inner(*args,**kwargs):
if not session.get('user'):
return redirect(url_for('login'))
ret = func(*args,**kwargs)
return ret
return inner
@app.route('/index')
@auth
def index():
return render_template('index.html',stu_dic=STUDENT_DICT)
# 在每个需要验证的函数里都加上
版本三:before_request @app.before_request
def auth():
if request.path == '/login':
return None
if session.get('user'): return None
return redirect('/login')
6、模板渲染
基本数据类型:可以执行python语法,如:dict.get() list[0],也可以使用django模板语言的语
法,如:dict.name
传入函数
django 可以后面不加括号,函数自动执行
flask 不会自动执行,和python的语法一样,需要在函数名的后面加上括号才能执行 全局定义函数
@app.template_global() def sb(a1, a2):
# {{sb(1,9)}} return a1 + a2
@app.template_filter() def db(a1, a2, a3):
# {{ 1|db(2,3) }} return a1 + a2 + a3
模板继承:和 django 的模板继承一样
安全过滤器 前端:{{u|safe}}
后端:MarkUp('asdfg')
宏
{% macro info(name, type='text', value='') %}
<h1>宏</h1>
<input type="{{ type }}" name="{{ name }}" value="{{ value }}">
<input type="submit" value="提交">
{% endmacro %} {{ info('n1') }} {{ info('n2') }}
然后就可以在这个html文件中直接使用这个宏函数了
7、session
flask:当请求刚到来时,flask读取 cookie 中 session 对应的值,将该值解密并反序列化成字
典,放入内存以便视图函数使用。
视图函数:
@app.route('/ses') def ses():
session['k1'] = 123 session['k2'] = 456 del session['k1']
return "Session"
session['xxx'] = 123 session['xxx']
当请求结束时,flask 会读取内存中字典的值,进行序列化+加密,写入到用户cookie中。
8、flash -- 闪现,在 session 中存储一个数据,读取时通过 pop 将数据移除。
from flask import Flask,flash,get_flashed_messages
@app.route('/page1')
def page1():
flash('临时数据存储','error') flash('sdfsdf234234','error') flash('adasdfasdf','info')
return "Session"
@app.route('/page2') def page2():
print(get_flashed_messages(category_filter=['error'])) return "Session"
9、中间件 __call__方法什么时候触发?
用户发起请求时,才执行 任务:在执行call方法之前,做一个操作,call方法执行之后做一个操作。 class Middleware(object):
def __init__(self,old): self.old = old
def __call__(self, *args, **kwargs):
if __name__ == '__main__':
app.wsgi_app = Middleware(app.wsgi_app)
app.run()
10、特殊的装饰器 1.before_request
2.after_request 示例:
from flask import Flask app = Flask(__name__)
@app.before_request def x1():
print('before:x1') return '滚'
@app.before_request def xx1():
print('before:xx1')
@app.after_request def x2(response):
print('after:x2') return response
@app.after_request def xx2(response):
print('after:xx2') return response
@app.route('/index') def index():
print('index') return "Index"
@app.route('/order') def order():
print('order') return "order"
3. before_first_request
from flask import Flask app = Flask(__name__)
@app.before_first_request def x1():
print('123123')
@app.route('/index') def index():
print('index') return "Index"
@app.route('/order') def order():
print('order') return "order"
if __name__ == '__main__': app.run()
4. template_global 5. template_filter 6. errorhandler
@app.errorhandler(404) def not_found(arg):
print(arg)
return "没找到"
37、Flask之蓝图 目标:给开发者提供目录结构 其他:
自定义模板、静态文件等 某一类 url 添加前缀
-- 在注册蓝图的时候可以给 url 添加前缀 app.register_blueprint(us, url_prefix='/api')
给一类 url 添加装饰器(例如: before_request)
import threading
from threading import local
import time
obj = local()
def task(i): obj.num = i
obj.sleep(2) print(obj.num, i)
for i in range(10):
t = threading.Thread(target = task, args = (i,))
t.start()
问题:如何获取一个线程的唯一标记?threading.get_ident() 根据字典自定义一个类似于 threading.local功能?
import time import threading
DIC = {}
def task(i):
ident = threading.get_ident()
if ident in DIC :
DIC[ident]['num'] = i else:
DIC[ident] = {'num': i } time.sleep(2)
print(DIC[ident]['num'], i) for i in range(10):
t = threading.Thread(target = task, arg = (i,)) t.start()
根据字典自定义一个为每个协程开辟空间进行存取数据 import time
import threading import greenlet
DIC = {} def task(i):
if ident in DIC: DIC[ident]['xxxxx'] = i
else:
DIC[ident] = {'xxxxx':i }
time.sleep(2)
print(DIC[ident]['xxxxx'],i)
for i in range(10):
t = threading.Thread(target=task,args=(i,))
t.start()
通过 getattr / setattr 构造出来 threading.local 的加强版(协程) imort time
import treading import greenlet try:
get_ident = greenlet.getcurrent except Exception as e:
get_ident = threading.get_ident class Local(object):
DIC = {}
def __getattr__(self, item):
ident = get_ident() if ident in self.DIC:
self.DIC[ident][key] = value else:
self.DIC[ident] = {key:value}
obj = Local()
def task(i): obj.xxxxx = i
time.sleep(2) print(obj.xxxxx,i)
for i in range(10):
t = threading.Thread(target=task,args=(i,)
t.start()
39、Flask的上下文管理
请求到来的时候:
# ctx = RequestContext(self, environ) # self是app对象,environ请求相关的原始数据
# ctx.request = Request(environ) # ctx.session = None
1232:{ctx:ctx对象} 1231:{ctx:ctx对象} 1211:{ctx:ctx对象} 1111:{ctx:ctx对象} 1261:{ctx:ctx对象}
}
视图函数:
from flask import reuqest,session
request.method
请求结束: 根据当前线程的唯一标记,将“空调”上的数据移除。
40、Flask之偏函数 偏函数是从Python2.5引入的一个概念,通过functools模块被用户调用。偏函数是将所要承载的函数作为 partial()函数的第一个参数,原函数的各个参数依次作为partial()函数后续的参数,除非使用关键字参数。 偏函数可以帮助开发者自动传递参数。
例如:
from functools import partial def func(x, y, z):
return x + y + z new_func = partial(func, 10)
print(func(10, 10, 10)) # 30
print(new_func(10, 10)) # 30
应用场景:在Flask的源码里遇过:
请求进来触发 def __call__() 方法 -- > def wsgi_app() --> ctx.push() --> def push() --
> _request_ctx_stack.push(self) --> globals.py文件里 有关于偏函数的使用
request = LocalProxy(partial(_lookup_req_object, 'request')) session = LocalProxy(partial(_lookup_req_object, 'session')) g = LocalProxy(partial(_lookup_app_object, 'g'))
PS:
其实可以看到不管request还是session最后都是通过getattr(top, name)获取的,也就是说肯定有一个上下
文对象同时保持request和session。我们只要一处导入request,在任何视图函数中都可以使用request,关键 是每次的都是不同的request对象,说明获取request对象肯定是一个动态的操作,不然肯定都是相同的 request。这里的魔法就是_lookup_req_object函数和LocalProxy组合完成的。 LocalProxy是werkzeug.local.py中 定义的一个代理对象,它的作用就是将所有的请求都发给内部的_local对象。
当我们调用request.method时会调用_lookup_req_object,对request的任何调用都是对 _lookup_req_object返回对象的调用。既然每次request都不同,要么调用top = _request_ctx_stack.top返回的 top不同,要么top.request属性不同,在flask中每次返回的top是不一样的,所以request的各个属性都是变化 的。
41、面向对象中的 super 方法的用途
一、根据 mro 的顺序执行方法
二、主动执行Base类的方法 42、基于列表实现栈
class Stack(object): def __init__(self):
self.data = []
def push(self,val): self.data.append(val)
def pop(self):
return self.data.pop()
def top(self):
return self.data[-1]
_stack = Stack()
_stack.push('哈哈') _stack.push('呵呵')
print(_stack.pop())
print(_stack.pop()) 43、在类中什么是函数,什么是方法?
class Foo(object):
def func(self): pass
# 执行方式一 obj = Foo() obj.func() # 方法
# 执行方式二
Foo.func(123) # 函数 44、面向对象中双下划线方法
__str__ __init__ __repr__ __new__
__getattr__ __setattr__ __delattr__
单例模型用到
rest framework 序列化的时候用到了
__call__ 在 Flask 的源码请求入口和 Django 的请求入口(WSGIHandler.__call__)都用到了
setitem
getitem
delitem,
class Foo(object):
def __getitem__(self, item): return 1
def __setitem__(self, key, value): pass
def __delitem__(self, key): pass
obj = Foo() obj['k1'] obj['k1'] = 123 del obj['k1']
dict,api封装返回数据时:BaseResponse mro, 显示继承顺序 slots,Local对象,显示类中能调用的方法
45、请求上下文管理(ctx):request,session
- 请求到来之后wsgi会触发__call__方法,由__call__方法再次调用wsgi_app方法
- 在wsgi_app方法中:
- 首先将 请求相关+空session 封装到一个RequestContext对象中,即:ctx。
- 将ctx交给LocalStack对象,再由LocalStack将ctx添加到Local中,Local结构:
__storage__ = {
1231:{stack:[ctx,] } }
- 根据请求中的cookie中提取名称为sessionid对应的值,对cookie进行加密+反序列化,再次赋值给ctx 中的session
-> 视图函数
- 把session中的数据再次写入到cookie中。 - 将ctx删除
- 结果返回给用户浏览器 - 断开socket连接
46、关于Flask中的g
g对象是application context,可以存储任何你想存储的内容,如数据库连接或登录的用户。应用
上下文flask._app_ctx_stack 和 flask.g 差不多, 可以保存一些特定的数据到这两个对象内部, 当然这些数 据只在"一个请求"的生命周期中有效. 不过flask._app_ctx_stack 推荐给extension的开发者使用, flask.g
user=getattr(flask.g,'user',None)
user=flask.g.get('user',None)
g 的生命周期:
g 是面向单个 request 的,一个 request 处理完,g 里的东西就没啦。(注意,这里说的 request
不是指 request context,从 0.10 版本开始,g 已经是面向 app context 了。这里写面向单个 request
是为了帮助提问者理解问题中提到的区别,仅考虑在 web 下的场景。)
g 和 session 的比较:
session 是可以跨 request 使用的,session 的数据是经过加密后存储在 cookie 里的,用户每次
request 都会把这些 cookie 信息发回来,从而实现跨 request 使用。
g是面向单个 request 的,一个 request 处理完,g 里的东西就没啦。(注意,这里说的 request
不是指 request context,从 0.10 版本开始,g 已经是面向 app context 了。这里写面向单个 request是为
了帮助提问者理解问题中提到的区别,仅考虑在 web 下的场景。)
g 和全局变量的比较:
全局变量在项目启动后就会引进,而且全局变量不会改变,不会消失;g 是在有请求的时候才
被设置,一次请求结束后会自动消失。 47、什么是响应式布局?
简而言之,就是一个网站能够兼容多个终端--而不是为每个终端做一个特定的版本。这个概念是为 解决移动互联网浏览而诞生的。 响应式布局可以为不同终端的用户提供更加舒适的界面和更好的用户体验,而且随着目前大屏幕移 动设备的普及,用"大势所趋"来形容也不为过。随着越来越多的设计师采用这个技术,我们不仅看 到很多的创新,还看到了一些成形的模式。
优点:
面对不同分辨率设备灵活性强
能够快捷解决多设备显示适应问题
缺点:
兼容各种设备工作量大,效率低下
代码累赘,会出现隐藏无用的元素,加载时间加长
其实这是一种折中性质的设计解决方案,多方面因素影响而达不到最佳效果
一定程度上改变了网站原有的布局结构,会出现用户混淆的情况
原理:使用了媒介查询:@media属性
48、MySQL 数据库的引擎 1.Innodb引擎
支持事务 支持锁
行锁和表锁:但是当SQL语句没有指定要锁定的具体行范围的话,Innodb 也会锁全表。 支持外键约束
2.MyIASM引擎 MyIASM是MySQL默认的引擎,不支持事务,也不支持行级锁和外键,只支持表级锁。
两种引擎的比较: 大尺寸的数据集趋向于选择InnoDB引擎,因为它支持事务处理和故障恢复。数据库的大小决 定了故障恢复的时间长短,InnoDB可以利用事务日志进行数据恢复,这会比较快。主键查询 在InnoDB引擎下也会相当快,不过需要注意的是如果主键太长也会导致性能问题。大批的 INSERT语句(在每个INSERT语句中写入多行,批量插入)在MyISAM下会快一些,但是 UPDATE语句在InnoDB下则会更快一些,尤其是在并发量大的时候。
49、Flask 上下文管理
主要包含两种:
请求上下文(ctx = RequestContext())里面含有两种信息 request \ session
app 上下文(app_ctx = AppContext())里面含有两种信息 app \ g
详细流程:
1、程序启动后:
启动项目的全局变量,实例化了两个对象,并创建了两个Stack 两个Local:
local1 = {
}
local2 = {
}
两个LocalStack: _request_ctx_stack _app_ctx_stack
2、请求到来后 对数据进行封装:
ctx = RequestContext(request,session)
app_ctx = AppContext(app,g) 保存数据:
将包含了(app,g)数据的app_ctx对象,利用 _app_ctx_stack(贝贝,LocalStack())将 app_ctx添加到Local中
storage = { 1231:{stack:[app_ctx(app,g),]}
} 将包含了request,session数据的ctx对象,利用_request_ctx_stack(刘淞,
LocalStack()),将ctx添加到Local中 storage = {
1231:{stack:[ctx(request,session),]} }
问题: Local是什么?作用?
答:存储数据,在Flask中给每一个协程开辟一个内存空间,保证数据之间的隔离 LocalStack是什么?作用?
答:将Local的数据,字典storage对应的值维护成一个栈(栈里面有 push 和 pop 这两 个方法),然后把数据存储到Local 中
3、视图函数处理:
from flask import Flask,request,session,current_app,g
id name pwd email
数据表中有上面4个字段,如果把 name pwd email 设成联合索引,只有下面的情况能命中
select * from tb where name='x'
select * from tb where name='x' and pwd='123'
select * from tb where name='x' and email='123@qq.com'
select * from tb where name='x' and pwd='123' and email='xs'
当下面两种情况的时候不会命中索引
select * from tb where pwd='123'
app = Flask(__name__)
@app.route('/index') def index():
# 去请求上下文中获取值 _request_ctx_stack request.method # 找小东北获取值 session['xxx'] # 找龙泰获取值
# 去app上下文中获取值:_app_ctx_stack
print(current_app)
print(g)
return "Index"
if __name__ == '__main__': app.run()
app.wsgi_app
通过LocalProxy对象+偏函数,调用LocalStack去Local中获取响应ctx、app_ctx中封装的
值。
问题:
为什么要把 ctx=requestssion app_ctx = app/g ?
答:因为离线脚本需要使用app_ctx。
4、请求结束 _app_ctx_stack.pop()
_request_ctx_stack.pop() 50、MySQL 数据库的索引
索引作用:加速查找+约束。 索引种类:
- 主键索引:加速查找、不重复、非空
- 唯一索引:加速查找、不重复
- 普通索引:加速查找
- 联合索引:加速查找
- 联合唯一索引:加速查找、不重复 PS:联合索引遵循最左前缀原则。
select * from tb where pwd='123' and email='123@qq.com'
- 覆盖索引:在索引文件中就可以把想要的数据得到。
MySQL可以利用索引返回select列表中的字段,而不必根据索引再次读取数据 文件,包函所有满足查询需要的数据的索引称为覆盖索引。 注意:如果使用覆盖索引,一定要注意select列表中只取出需要的列,不可 select * ,因为如果将所有字段一起做索引会导致索引文件过大,查询性能下 降。
select name from tb1;
- 索引合并:使用多个单列索引去查找数据。
1、索引合并是把几个索引的范围扫描合并成一个索引。
2、索引合并的时候,会对索引进行并集,交集或者先交集再并集操作,以便合并 成一个索引。
3、这些需要合并的索引只能是一个表的。不能对多表进行索引合并。
创建索引,但无法命中索引: https://www.cnblogs.com/wupeiqi/articles/5716963.html
51、MySQL 数据库授权
MySQL 赋予用户权限命令的简单格式可概括为:
grant 权限 on 数据库对象 to 用户
详解:https://www.cnblogs.com/bethal/p/5512755.html
52、MySQL 数据库视图 (View)是一种虚拟存在的表。其内容与真实的表相似,包含一系列带有名称的列和行数据。
但是视图并不在数据库中以存储的数据的形式存在。行和列的数据来自定义视图时查询所引用的基
本表,并且在具体引用视图时动态生成。
视图是存储在数据库中的查询的SQL 语句,它主要出于两种原因:安全原因, 视图可以隐藏一些
数据,如:社会保险基金表,可以用视图只显示姓名,地址,而不显示社会保险号和工资数等,另
一原因是可使复杂的查询易于理解和使用。
:
12.12....; MySQL 数据库触发器
CREATE TRIGGER trigger_name
trigger_time
trigger_event ON tbl_name
FOR EACH ROW
trigger_stmt
weiv etaerc图视建创
图视
下:
SHOW TRIGGERS[FROM schema_name]; 和删除数据库、删除表格一样,删除触发器的语法如下:
DROPTRIGGER[IF EXISTS][schema_name.]trigger_name 触发器的执行顺序
我们建立的数据库一般都是 InnoDB 数据库,其上建立的表是事务性表,也就是事务安全的。这 时,若SQL语句或触发器执行失败,MySQL 会回滚事务,有:
1如果 BEFORE 触发器执行失败,SQL 无法正确执行。 2SQL 执行失败时,AFTER 型触发器不会触发。
3AFTER 类型的触发器执行失败,SQL 会回滚。
54、MySQL 数据库之函数 对数据中的值进行操作
MySQL数据库提供了很多函数包括:
数学函数; 字符串函数; 日期和时间函数; 条件判断函数; 系统信息函数; 加密函数; 格式化函数;
具体参考:https://www.cnblogs.com/kissdodog/p/4168721.html 55、MySQL 数据库之存储过程
存储过程简介
SQL语句需要先编译然后执行,而存储过程(Stored Procedure)是一组为了完成特定功能的
SQL语句集,经编译后存储在数据库中,用户通过指定存储过程的名字并给定参数(如果该
存储过程带有参数)来调用执行它。
存储过程是可编程的函数,在数据库中创建并保存,可以由SQL语句和控制结构组成。当想 要在不同的应用程序或平台上执行相同的函数,或者封装特定功能时,存储过程是非常有用 的。数据库中的存储过程可以看做是对编程中面向对象方法的模拟,它允许控制数据的访问 方式。
存储过程的优点:
(1).增强SQL语言的功能和灵活性:存储过程可以用控制语句编写,有很强的灵活性,可以完 成复杂的判断和较复杂的运算。
(2).标准组件式编程:存储过程被创建后,可以在程序中被多次调用,而不必重新编写该存储 过程的SQL语句。而且数据库专业人员可以随时对存储过程进行修改,对应用程序源代码毫 无影响。
,器发触除删
,器发触看查
(4).减少网络流量:针对同一个数据库对象的操作(如查询、修改),如果这一操作所涉及的 Transaction-SQL语句被组织进存储过程,那么当在客户计算机上调用该存储过程时,网络中传 送的只是该调用语句,从而大大减少网络流量并降低了网络负载。
(5).作为一种安全机制来充分利用:通过对执行某一存储过程的权限进行限制,能够实现对相 应的数据的访问权限的限制,避免了非授权用户对数据的访问,保证了数据的安全。
对结果集合返回值进行复杂操作。
DELIMITER //
CREATE PROCEDURE myproc(OUT s int)
BEGIN
SELECT COUNT(*) INTO s FROM students;
END
//
DELIMITER ;
具体可参考:https://www.cnblogs.com/mark-chan/p/5384139.html 56、Flask 中的组件有哪些?
1、flask-session 2、DBUtils 3、wtforms 4、sqlalchemy 5、flask-script 6、flask-migrate
数据库连接池
a. 增加 runserver
b. 位置传参
c.关键字传参
57、使用装饰器,如何不改变函数签名
给装饰器的内层函数加上装饰器 functools.wraps(func) ,会保留原函数的元信息,书写形式例如:
def wrapper(func):
@functools.wraps(func) def inner(*args,**kwargs):
return func(*args,**kwargs) return inner
58、类的私有化 一般情况下,类的私有化对象是不能调用的,如果想调用,就要写成这种形式的 class Foo(object):
def __init__(self, name, age): self.name = name self.__age = age
体程过 ]... 性特[ )]]...型类据数 名数 参 ]TUONI|TUO|NI[,[型类据数 名数参 ]TUONI|TUO|NI[[(名程过 ERUDECORP ETAERC
法语 建创的程过储存LQSyM
obj = Foo('oldboy', 50) print(obj.name) print(obj._Foo__age) obj.func()
# 一定能使用
# 对象调用类的私有属性的方法
注意:私有化,在子类父类之间是不能互相调用的