Python Flask 模块

一、初识Flask

Flask扩展应用扩展包

二、认识werkzurg,Flask最重要的依赖

 # 引用werkzurg的功能模块
from werkzeug.wrappers import Request,Response
from werkzeug.serving import run_simple
 # 底层的用法
def run(environ,start_response):
 return [b'abcdefg'] 
if __name__ == '__main__':
 run_simple('localhost',4000,run) # 监听端口并执行run函数	
 # 另一种用法
@Response.application
def hello(request):
 return Response('Hello World!')
if __name__ == __main__:
 run_simple('localhost',4000,hello)

三、Flask 使用

3.1、简单的登录

from flask import Flask as fk , request,render_template as render,redirect,session
app = fk(__name__)
app.secret_key = 'abckd' # 设置session 的加密值
@app.route('/',methods=['GET','POST'])
def index():
 if request.method == 'POST':
  user = request.form.get('user')
  pwd = request.form['pwd']
  if user == 'admin' and pwd == '123':
   print(user,pwd)
   return '登录成功!'
 return render('index.html',h1='你好')
if __name__ == '__main__':
 app.run() 
'''
执行werkzurg中的run_simple('localhost',4000,hello)
这里只是socket进行了请求监听,当浏览器发起请求后
执行内部的__call__()方法
'''

静态文件的处理方法

<!-- 使用静态文件需要删除 <!DOCTYPE html> -->
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/index.css" type="text/css">
    <!-- 推荐使用这样加载静态文件 -->
    <link rel="stylesheet" href="{{ url_for('static',filename='index.css') }}" type="text/css"> 
</head>
<body>
<h1>index</h1>
<h1>{{ h1 }}</h1>
<form action="/" method="POST">
    <input type="text" name="user" >
    <input type="password" name="pwd">
    <input type="submit" value="提交">
</form>
</body>
</html>

3.2、Flask 简单运用

 from flask import Flask,render_template,request,redirect,session
 app = Flask(__name__) # __name__ 可以修改为任意字符串,并可以传入多个参数
 app.secret_key = 'abckd' # 设置session加密多余字符串
 # 路由装饰器
 @app.route('/login',methods=['GET','POST'])
 # 定义与路由装饰器匹配的执行函数
 def login():
 print(request.method) # request 需要导入,获取请求的信息
 session['key] = value # 设置session值
 session.get('key') # 获取session的值
 return render_template('login.html',**{key:value})
#return render_template('login.html',key=value)
 if __name__ == '__main__':
  app.run(url,prot)

3.4、Flask 的实现基础

3.4.1、Threading.local 多线程

作用:为每个线程创建一个独立的空间,使得线程对自己空间中的数据进行操作(数据隔离)。

 import threading
 from threading import local
 local_obj = local() # 实例化local对象
 def task(i):
  local_obj.xxx = i # 为local对象设置一个参数
  print(local_obj.xxx,i) # 获取local对象中的参数
  # threading.get_ident() 获取线程的唯一标示
  print(threading.get_ident(),i)
 for i in range(10):
  t = threading.Thread(target=task,args(i,)) # 创建线程
  t.start() # 开始执行线程

3.4.2、根据字典自定义类似Threading.localz

 import threading
 import greenlet # 获取协程信息
 DIC = {}
 def task(i):
  # 获取协成的唯一标记
  # indent = greenlet.getcurrent()
  # treading.get_ident() 获取线程的唯一标记
  indent = treading.get_ident()
  if indent in DIC:
   DIC[indent]['xxx'] = i
  else:
   DIC[indent] = {'xxx':i}
  print(DIC[index][xxx],i) # 打印字典中的参数
 for i in range(10):
  t = threading.Thread(target=task,args=(i,)) # 创建线程
  t.start() # 开始执行线程

3.3.3、自定义升级版Threading.localz

此版本中协程与线程可以完全的兼容,完成对请求的数据隔离。
为什么需要给线程和协程数据进行隔离?
 在多个请求的时候,由于每个请求的信息不相同,所以需要对不同的请求信息进行分类管理(数据隔离),从而防止数据混乱;
 这样在需要使用视图函数取得用户请求信息的时候,才能根据不同的信息进行取值和修改。
 import threading
 import time
 
 try:
  import greenlet # 获取协程信息
  get_indent = greenlet.getcurrent
 except Exception as e:
  get_indent = threading.get_ident
 class local(object):
  # DIC={}
  def __init__(self):
   # pass
   object.__setattr__(self,'DIC',{}) # 通过父类的方法设置类属性
  def __getattr__(self,item):
   indent = get_indent()
   if indent in self.DIC:
    return self.DIC[indent].get(item)
   else:
   	return None
    
  def __setattr__(self,key,value):
   indent = get_indent()
   if indent in self.DIC:
    self.DIC[indent][key] = value
   else:
    self.DIC[indent]= {key:value}
    
  obj = local() # 类在实例化的时候运行__init__()方法
  '''
   obj.xx # 对象.方法的时候运行__getattr__()方法,并且把xx当参数传入
   obj.xx = 123 # 对象.方法赋值的时候运行__setattr__()方法,并且把xx和123当参数传入
  '''
  def task(i):
   obj.xxx = i
   time.sleep(2)
   print(obj.xxx,i)
  for i in range(10):
   t = threading.Thread(target=task,args=(i,)) # 创建线程
   t.start() # 开始执行线程

四、Flask中的方法

# Flask 中的模块
from flask import Flask,render_template,request,redirect,session
request.method # 获取请求的方法
request.args # 获取get请求的数据
request.args.get('') # 获取get请求指定的值
request.form # 获取post请求的数据
request.form.get('') # 获取post请求指定的值
files = request.files.get('') # 获取POST上传的文件信息
files.filename # 获取上传的文件名
files.stream # 获取上传文件的内容
files.save('文件路径','上传文件的内容',) # 保存上传文件到本地
'''
	session 的处理 flask 放入的是加密cookie中的,继承了字典的所有功能;
	flask读取cookie中session对应的值,将该值解密并反序列化成为字典,供视图函数使用;
	当请求结束时,flask会读取内存中字典的值,进行序列化并加密,写入到浏览器cookie中。
'''
session['key'] = value # 为session赋值 保存在浏览器的cookie中
session.get('') # 获取session的值
del session['key'] # 删除

# Flask() 配置项 
import_name, # 文件名称
static_url_path=None, # 静态文件别名
static_folder='static', # 静态文件路径
static_host=None,
host_matching=False,
subdomain_matching=False,
template_folder='templates', # 模板文件路径
instance_path=None, # 实例文件路径
instance_relative_config=False, # 实例配置文件相对路径
root_path=None # 根文件路径

# Flask 配置文件 app.config['ENV'] 获取内部的值
'ENV':                                  None,
'DEBUG':                                None,
'TESTING':                              False,
'PROPAGATE_EXCEPTIONS':                 None,
'PRESERVE_CONTEXT_ON_EXCEPTION':        None,
'SECRET_KEY':                           None,
'PERMANENT_SESSION_LIFETIME':           timedelta(days=31), # session保留时间
'USE_X_SENDFILE':                       False,
'SERVER_NAME':                          None,
'APPLICATION_ROOT':                     '/',
'SESSION_COOKIE_NAME':                  'session',
'SESSION_COOKIE_DOMAIN':                None,
'SESSION_COOKIE_PATH':                  None,
'SESSION_COOKIE_HTTPONLY':              True,
'SESSION_COOKIE_SECURE':                False,
'SESSION_COOKIE_SAMESITE':              None,
'SESSION_REFRESH_EACH_REQUEST':         True, # session以什么方式保存保留时间
'MAX_CONTENT_LENGTH':                   None, # 上传文件大小限制
'SEND_FILE_MAX_AGE_DEFAULT':            timedelta(hours=12),
'TRAP_BAD_REQUEST_ERRORS':              None,
'TRAP_HTTP_EXCEPTIONS':                 False,
'EXPLAIN_TEMPLATE_LOADING':             False,
'PREFERRED_URL_SCHEME':                 'http',
'JSON_AS_ASCII':                        True,
'JSON_SORT_KEYS':                       True,
'JSONIFY_PRETTYPRINT_REGULAR':          False,
'JSONIFY_MIMETYPE':                     'application/json',
'TEMPLATES_AUTO_RELOAD':                None,
'MAX_COOKIE_SIZE': 4093,

五、Flask配置文件

5.1、通过字符串获取 Class 并运行(反射)

hasattr、setattr、getattr 解释

hasattr(object, name) 判断一个对象里面是否有name属性或者name方法,返回BOOL值,有name特性返回True, 否则返回False。

class test():
 name="xiaohua"
 def run(self):
  return "HelloWord"
t=test()
hasattr(t, "name") #判断对象有name属性
#输出:True
hasattr(t, "run")  #判断对象有run方法
#输出:True

setattr(object, name, values) 给对象的属性赋值,若属性不存在,先创建再赋值。
getattr(object, name[,default]) 可以取出来某个属性,这个属性如果是一个字段,就得到字段的值了,如果是一个方法,就得到这个方法的指针了,然后可以根据方法的指针来调用方法。

class test():
 name="xiaohua"
 def run(self):
  return "HelloWord"
t=test()
setattr(t,'max',30) #在对象中设置max属性
getattr(t, "name") #获取name属性,存在就打印出来。
#输出:'xiaohua'
getattr(t, "run")  #获取run方法,存在就打印出方法的内存地址。
#输出:<bound method test.run of <__main__.test instance at 0x0269C878>>
getattr(t, "run")()  #获取run方法,后面加括号可以将这个方法运行。
#输出:'HelloWord'
getattr(t, "age")  #获取一个不存在的属性。
'''
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: test instance has no attribute 'age'
'''
getattr(t, "age","18")  #若属性不存在,返回一个默认值。
#输出:'18'

5.2,Flask配置文件的原理

 # settings.py 文件
 class Foo(object):
  DEBUG = True
 # 获取settings.py中类的项目
 import importlib
 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))
 # 输出
 # DEBUG True

5.3、配置文件

'''
 app.config # 获取配置文件对象
 app.config['ENV'],app.config['ENV'] = ‘aaa'  # 获取和设置配置文件的值
 app.config.from_object(‘settings.Foo’)  # 通过外部文件引入配置文件
'''
 # settings.py 文件
 class 类名:
  配置文件key = 配置文件value
 # 使用 settings.py 文件
 app.config.from_object(‘settings.类名’) 

六、路由系统

6.1、endpoint 的使用

 from flask import Flask, url_for 
 app = Flask()
 # endpoint 根据名字反向生成URL '/index' 。如果不设置默认为函数名 url_for( 'index')
 @app.route('/index',methods=['GET'],endpoint='n1')
 def index():
  print(url_for('n1')) # 反向生成的URL
  return 'Index'

6.2、路由传参

 '''
  动态路由:
  	/index/<int:nid> 只接收整数
  	/index/<float:nid> 接收浮点数
  	/index/默认为字符串 
 '''
from flask import Flask,request,render_template as render,redirect,session,make_response as response,url_for

app = Flask(__name__,static_url_path='/static/')

@app.route('/<int:nid>',methods=['GET','POST'])
def index(nid):
 print(url_for('index',nid=777))
 if request.method == 'POST':
  user = request.form.get('user')
  pwd = request.form.get('pwd')
  if user == 'admin' and pwd == '123':
   return render('index.html',success='提交成功!')
  return render('index.html',h1='首页')
if __name__ == '__main__':
 app.run()
 '''
 输出:/index/777
 '''

6.3 route() 参数

rule # URL规则
view_func # 视图函数名称
methods=['GET'] # 请求的方式
endpoint=None # 名称用于反向生成URL,即:url_for('名称')
strict_slashes=None # 对URL最后的 ‘/’ 符号是否严格要求 True:严格要求 False:不严格要求
 redirct_to='/new/<int:nid>' # 重定向
 
 app.config['SERVER_NAME'] = ‘主域名’
 subdomain='url' # 配置主域名下子域名访问地址,写入'<username>'表示任意子域名

6.4、自定义正则

from flask import Flask,render_template as render,url_for
from werkzeug.routing import BaseConverter

app = Flask(__name__,static_url_path='/static',static_folder='static')

# 自定义正则转换器
class RegexConverter(BaseConverter):
    def __init__(self, map, *args):# map 路由传入的路径集合
        super(RegexConverter, self).__init__(map)
        # 将接受的第1个参数当作匹配规则进行保存
        self.regex = args[0]

    def to_python(self, value):# 正则匹配URL时触发
        return int(value) # value为匹配到的值

    def to_url(self, value): # 方向生成URL触发
        val = super(RegexConverter, self).to_url(value)
        return val # url_for() nid 的值

# 将自定义转换器添加到转换器字典中,并指定转换器使用时名字为: reg
app.url_map.converters['reg'] = RegexConverter

@app.route('/<reg("\d+"):nid>',methods=['GET','POST'])
def index(nid):
    print(url_for('index',nid=222))
    return render('index.html',h1='index')


if __name__ == '__main__':
    app.run()

七、FBV 视图

FBV 视图:利用函数与路由建立关联,实现请求响应的控制。
CBV 视图:通过定义类的方式来建立视图与路由的关联。

# CBV视图
from flask import Flask,views
app = Flask(__name__,static_url_path='/static',static_folder='static')
class UserViews(views.MethodView):
 methods = ['GET'] # 设置请求方式
 decorators = [] # 设置装饰器(全部) 
 def get(self,*args,**kwargs):
  return 'GET'
 def POST(self,*args,**kwargs):
  return 'POST'
app.add_url_rule('/index',None,UserViews.as_view('uuu'))

if __name__ == '__main__':
 app.run()

7.1、请求相关request

下面是request可使用的属性,其中'*'是比较常用的。
*form 
一个从POST和PUT请求解析的 MultiDict(一键多值字典)。

*args 
MultiDict,要操作 URL (如 ?key=value )中提交的参数可以使用 args 属性:
searchword = request.args.get('key', '')

*values 
CombinedMultiDict,内容是form和args。 
可以使用values替代form和args。

*cookies 
顾名思义,请求的cookies,类型是dict。

stream 
在可知的mimetype下,如果进来的表单数据无法解码,会没有任何改动的保存到这个·stream·以供使用。
很多时候,当请求的数据转换为string时,使用data是最好的方式。这个stream只返回数据一次。

*headers 
请求头,字典类型。

*data 
包含了请求的数据,并转换为字符串,除非是一个Flask无法处理的mimetype。

*files 
MultiDict,带有通过POST或PUT请求上传的文件。

environ 
WSGI隐含的环境配置。

*method 
请求方法,比如POST、GET。

path
.script_root
.url
.base_url
.url_root 
如果用户请求如下URL: 
http://www.example.com/myapplication/page.html?x=y 

以上的参数内容如下:

名称 内容
path /page.html
script_root /myapplication
base_url http://www.example.com/myapplication/page.html
url http://www.example.com/myapplication/page.html?x=y
url_root http://www.example.com/myapplication/

is_xhr 
如果请求是一个来自JavaScript XMLHttpRequest的触发,则返回True,这个只工作在支持
X-Requested-With头的库并且设置了XMLHttpRequest。

blueprint 
蓝本名字。

endpoint 
endpoint匹配请求,这个与view_args相结合,可是用于重构相同或修改URL。当匹配的时候发生异常,会返回None。

get_json(force=False, silent=False, cache=True)

json 
如果mimetype是application/json,这个参数将会解析JSON数据,如果不是则返回None。 
可以使用这个替代get_json()方法。

max_content_length 
只读,返回MAX_CONTENT_LENGTH的配置键。

 module
如果请求是发送到一个实际的模块,则该参数返回当前模块的名称。这是弃用的功能,使用blueprints替代。

on_json_loading_failed(e)
routing_exception = None 
如果匹配URL失败,这个异常将会/已经抛出作为请求处理的一部分。这通常用于NotFound异常或类似的情况。

url_rule = None 
内部规则匹配请求的URL。这可用于在URL之前/之后检查方法是否允许(request.url_rule.methods) 等等。 
默认情况下,在处理请求函数中写下 print('request.url_rule.methods', request.url_rule.methods) 
会打印:
request.url_rule.methods {‘GET’, ‘OPTIONS’, ‘HEAD’}

view_args = None 
一个匹配请求的view参数的字典,当匹配的时候发生异常,会返回None。

7.2、响应相关

from flask import Flask,make_response as response,jsonify,render_template as render,redirect
'''
    make_response 封装所有形式的返回响应体,并可以设置返回响应头
    res = response(render('index.html',**{'k1':'v1'}))
    return res
    返回响应体
    return '' 返回字符串
    return jsonify{'k1':'v1'} 返回json字符串
    return render('index.html',**{'k1':'v1'}) 返回一个文件
    return redirect('http://www.baidu.com') 返回重定向URL
'''
app = Flask(__name__,static_url_path='/static/')
app.secret_key = 'abck'
@app.route('/',methods=['GET','POST'])
def index():
 res = response('index.html')  # 封装了返回字符串响应体
 res.headers['xxx'] = 123 # 设置返回响应头
 res.set_cookie = 321 # 设置cookies
 return res # 返回请求体,请求头,
if __name__ == '__main__':
 app.run()

7.3、before_request 装饰器

from flask import Flask,make_response as response,jsonify,render_template as render,redirect,request
app = Flask(__name__,static_url_path='/static/')

# app.before_request后面的函数,在所有函数执行前执行。
@app.before_request
def func():
 if request.path == '/index':
   return None
 return '返回值'
 	# 返回值不是None,不再往后执行,直接返回return的值
 	# 返回None,才往后执行其他,这里执行index函数
 	
@app.route('/',methods=['GET','POST'])
def index():
 res = response('index.html')  # 封装了返回字符串响应体
 res.headers['xxx'] = 123 # 设置返回响应头
 res.set_cookie = 321 # 设置cookies
 return res # 返回请求体,请求头,
 
if __name__ == '__main__':
 app.run()

八、模板渲染

8.1、基本语句的使用

 # 字符串,字典,列表等基本数据类型,都可以通过python本来的方法操作。
 {{ 属性名.0 }}
 {{ 属性名[0] }}
 {{ 属性名() }}
 		
 # html标签转换
 {{ html标签|safe }}
 from flask import Markup
 Markup('<h1>h1标签</h1>')

8.3、设置全局模板函数

 # 设置全局所用的函数
 @app.template_global()
 def sb(a1,a2):
  return a1 + a2
 # 使用:{{ sb(a1,a2) }}	
 
 # 过滤filter 的使用
 @app.template_filter()
 def sb(a1,a2,a3):
  return a1 + a2 + a3
 # 使用:{{ a1|sb(a2,a3) }}

8.3、模板的继承

 模块:{% block content %}{% endblock %}
 模板文件:
 <html lang="en">
	<head>
	    <meta charset="UTF-8">
	    <title>首页</title>
	    <link rel="stylesheet" href="{{ url_for('static',filename='index.css') }}" type="text/css">
	</head>
	<body>
	    <div>
	        {% block content %}{% endblock %}
	    </div>
	</body>
 </html>
----------------------------------------------------------------------------------------
 <!-- 导入模板文件 -->
 {% extends './template.html' %}
 {% block content %}
 	<h1> 你好 </h1>
    {{ h1 }}
 {% endblock %}	
 调用模块:
	需要使用模块文件的引入: 
		{% extents '模板文件' %} 
	模块代码:
		{% block content %}
			<h1> 你好 </h1>
			{{ h1 }}
		{% endblock %}
-------------------------------------------------------------------------------------
 导入模板:{% include '模板文件' %}
 
 宏定义:{% macro ccc(name,type='text',value='') %} # 相当于定义def 函数
		<input type="{{ type }}" name="{{ name }}"/>
		<input type="submit" value="提交"/>
	{% endmacro %}
 使用宏:{{ ccc('n1') }}

九、Flash和特殊装饰器

9.1、flash(闪现)临时session操作

 from flask import flash,get_flashed_messages
 flash('','数据分类属性') # 设置
 get_flash_messages() # 获取
 get_flash_messages(category_filter=['数据分类属性']) # 通过分类获取
 '''
  flash 实现原理:
  	flash基于session来处理内容,先将flash('')中的值存入session中,再通过session的pop移除,达到用一次就消失的效果。
 '''
 # session自己实现
 session.pop('属性名')

9.2、Flask中间件

9.2.1、特殊装饰器

 #设置全局模板函数
 @template_global()
 @template_filter()

 # app.route视图函数执行前执行,只运行第一次
 @app.before_first_request
 def x1():
  print('before_first')

 # app.route视图函数执行前执行,多个谁先定义谁先执行
 @app.before_requesr
 def x2():
  print('before')

  # app.route视图函数执行后执行,必须有参数和返回值,多个谁后定义谁先执行
 @app.after_request
 def x3(response):
  print('after')
  return response

  #定义404
 @app.errorhandler(404) 
 def not_fourd(arg):
  return '404'

9.2.2、中间件

 '''
  Flask的中间件需要通过自定义方法,去替换源码中的相关方法,利用在程序获得请求时才运行
  __call__()的特性,来创建中间件,从而完成中间件的功能。
 '''
class Middleware(object):
 def __init__(self, old):
     self.old = old
 def __call__(self, *args, **kwargs):
     print('前')
     ret = self.old(*args, **kwargs)
     print('后')
     return ret
  		
if __name__ == '__main__':
 app.wsgi_app = Middleware(app.wsgi_app)

十、蓝图

为开发者提供一个目录结构,让开发更规范,可以将特殊装饰器加到蓝图中,并根据不同的返回内容设置不同的域名前缀。

10.1、蓝图的基本模型

蓝图的基本模型

10.1.1、主路由视图函数

__init__.py

from flask import Flask
# 导入定义的蓝图模块
from .views.index import n

def app_view():
    app = Flask(__name__,)
    # 注册
    app.register_blueprint(n,url_prefix='/index')
    return app

app.py

from flask03 import app_view

app = app_view()

print(app)
if __name__ == '__main__':
    app.run()

10.1.2、分路由视图函数

index.py

#coding:utf8
from flask import Blueprint
# ps:蓝图名不能与视图函数名一致
n = Blueprint('index',__name__,template_folder='../xxx',static_folder='../statics',static_url_path='/statics')

@n.route('/',methods=['GET'])
def index():
    return 'index'

10.2、大项目蓝图目录结构

大项目蓝图目录结构

十一、Flask上下文管理

11.1、Flask执行顺序

 上下文管理的内容:
  request、session、app、g
 1、客户端请求到来时,会将request/session封装到一个对象 RequestContext(self,environ)
  中(其中self为app对象,environ为客户端请求的原始数据);
  ctx=RequestContext(self,environ) 封装了 request/session 
 2、将含有request/session的对象打包,根据线程/协程的唯一标识,作为字典存放在一个
 “空间中”(线程/协程唯一标识作为Key,RequestContext对象作为Value),
 {
  线程/协程唯一标识:{ctx:RequestContext对象}
  ....
 }
 3、视图函数运行过程
 from flask import request,session
 request.method
 当有请求过来时,通过线程/协程唯一标识找到,存放在空间中的RequestContext对象,
 再通过对象找到封装其中的request/session,最后找到属性.method。
 4、请求结束
 session保存到浏览器cookie中,并移除字典中的数据,从而结束请求。

11.2、偏函数

import functools
def index(a1,a2):
 return a1+a2
# 函数常规的用法
a = index(12,20)
print(a)
'''
 利用 functools 模块实现偏函数;
 偏函数只要传入一个值,其他的值自动传入
'''
new_func = functools.partial(index,666)
a1 = new_func(34)
print(a1)

11.3、上下文:request请求流程

request请求流程

11.4、上下文session

lask session 的请求流程

猜你喜欢

转载自blog.csdn.net/u011146423/article/details/83383055