wsgi
是什么?
全称 web服务器网关接口,是为python语言定义的web服务器和web应用程序活框架之间的一种简单而通用的接口。
自从wsgi被开发出来以后,许多其他语言也出现了类似接口。
发展背景
以前如何选择合适的web应用程序框架成为困扰python初学者的一个问题,
这是因为,一般而言,web应用程序框架的选择将限制可用的web服务器的选择,反之亦然,
那时的python应用程序通常是为 cgi fastcgi mod_python 中的一个设计
甚至是为特定web服务器的自定义的api接口设计的
作用
wsgi是作为服务器与web应用程序或应用框架之间的一种低级别的接口,可提升可移植web应用开发的共同点,wsgi是基于现存的cgi标准而设计的
wsgi相当于webserver 和 applicaion 中间的一个桥梁 主要是用来动态处理页面请求
MVC
组成
model
view 返回内容展示
control 视图函数
控制器 controller 负责转发请求,对请求机型处理
视图 view 界面设计人员进行图形界面设计
模型 model 程序员编写程序应有的功能,实现算法等等
DBA 进行数据管理和数据库设计(以实现具体功能
view ——通过url—->control——解析请求数据——>model———>DB
回显到视图<——-构建响应数据——-control<———返回操作结果—-model
这是一种分层设计思想,解耦合,方便进行调试,扩展
Flask初始化
from flask import Flask,request,render_template
app = Flask(
__name__,
# import_name,
static_url_path=None, # url必须“/”开头,从前端访问static文件的时候
# 告诉用户访问路径,
static_folder="static", # 静态文件存放目录,并不是每次都去访问static的文件 ,浏览器会缓存
static_host=None,
host_matching=False, # 服务器匹配
subdomain_matching=False, # 子域名
template_folder="html_file", # 模板存放目录,这些都是可以设置的属性
instance_path=None,
instance_relative_config=False,
root_path=None,
)
# request 会保存所有页面传来的参数,通过request.args来进行获取
# 为一个字典类型,可以通过get方法,或者键值对进行值获取,推荐使用get方法
# render_template 返回某个页面的时候使用,后续深入讲解
核心函数流程
# 简单实例flask
# 1,初始化 app核心对象 2,添加路由,编写视图函数 3,启动
app = Flask(
__name__,
# 若干属性配置
)
@app.route('/hello')
@app.route('/index') # hello,如果想要多个路径套用同一个视图的情况下,可以直接加注解叠加
@app.route("/") # 添加访问路径
def home(): # 多路径套用同一个视图 但是一个url不会对应多个视图函数
# 视图函数内部要编写的内容
# 1.接收参数
# 2.调用对应的函数去处理数据
# 3.构建响应结果
return render_template('index.html')
# 启动 ,执行的时候将app.run写在main下面,保证导入的时候不会出错
# 其作用是只在本py文件下进行运动
if __name__ == '__main__':
app.run(port=app.config['PORT'],
debug=app.config['DEBUG']
)
Flask中运行服务器的两种方式
app.run()启动
命令行方式启动 set FLASK_APP = 设定的app的py文件 || flask run
给视图函数添加装饰器进行扩展的时候的坑
1,除了@app.route()路由装饰器,其他的装饰器必须放在其下main
原因与app.route()的生命周期相关,
2,装饰器返回值必须是视图函数的返回值
当装饰器编写有误的时候,直接原因是装饰器没有返回对象,而app.route()需要使用装饰器的返回对象并将其包装成一个响应结果
Flask的配置文件
flask中的配置文件是一个flask.config.Config对象(继承字典),默认配置为:
{
'DEBUG': get_debug_flag(default=False), 是否开启Debug模式
'TESTING': False, 是否开启测试模式
'PROPAGATE_EXCEPTIONS': None,
'PRESERVE_CONTEXT_ON_EXCEPTION': None,
'SECRET_KEY': None,
'PERMANENT_SESSION_LIFETIME': timedelta(days=31),
'USE_X_SENDFILE': False,
'LOGGER_NAME': None,
'LOGGER_HANDLER_POLICY': 'always',
'SERVER_NAME': None,
'APPLICATION_ROOT': None,
'SESSION_COOKIE_NAME': 'session',
'SESSION_COOKIE_DOMAIN': None,
'SESSION_COOKIE_PATH': None,
'SESSION_COOKIE_HTTPONLY': True,
'SESSION_COOKIE_SECURE': False,
'SESSION_REFRESH_EACH_REQUEST': True,
'MAX_CONTENT_LENGTH': None,
'SEND_FILE_MAX_AGE_DEFAULT': timedelta(hours=12),
'TRAP_BAD_REQUEST_ERRORS': False,
'TRAP_HTTP_EXCEPTIONS': False,
'EXPLAIN_TEMPLATE_LOADING': False,
'PREFERRED_URL_SCHEME': 'http',
'JSON_AS_ASCII': True,
'JSON_SORT_KEYS': True,
'JSONIFY_PRETTYPRINT_REGULAR': True,
'JSONIFY_MIMETYPE': 'application/json',
'TEMPLATES_AUTO_RELOAD': None,
}
1,通过对象属性赋值配置
app.config['DEBUG'] = True
#由于Config对象本质上是字典,所以还可以使用app.config.update(DEBUG=True)
2,通过py文件设置
#通过py文件配置
app.config.from_pyfile("python文件名称")
如:
settings.py
DEBUG = True
app.config.from_pyfile("settings.py")
#通过环境变量配置
app.config.from_envvar("环境变量名称")
#app.config.from_pyfile(os.environ['YOURAPPLICATION_SETTINGS'])
环境变量的值为python文件名称名称,内部调用from_pyfile方法
app.config.from_json("json文件名称")
JSON文件名称,必须是json格式,因为内部会执行json.loads
app.config.from_mapping({'DEBUG': True})
字典格式
app.config.from_object("python类或类的路径")
app.config.from_object('pro_flask.settings.TestingConfig')
settings.py
class Config(object):
DEBUG = False
TESTING = False
DATABASE_URI = 'sqlite://:memory:'
class ProductionConfig(Config):
DATABASE_URI = 'mysql://user@localhost/foo'
class DevelopmentConfig(Config):
DEBUG = True
class TestingConfig(Config):
TESTING = True
PS: 从sys.path中已经存在路径开始写
PS: settings.py文件默认路径要放在程序root_path目录,如果instance_relative_config为True,
则就是instance_path目录(Flask对象init方法的参数)
多url的路由
一个试图函数可以设置多个url路径去访问
一个url路径只可以又一个试图函数
装饰器与路由注解
视图装饰器应当放在最外层,否则里面的装饰器不会生效
视图函数包裹的装饰器不要return值,否则会被包装成返回对象,视图函数中的返回响应对象就不会生效了
# 写个装饰器
import time
def log_time(func):
def decorator(*args,**kwargs):
print(F"{time.time()}")
return func(*args,**kwargs)
return decorator
# 添加路由 视图函数
@log_time # 放在这里的话装饰器不会被运行
@app.route('/index') # hello,如果想要多个路径套用同一个视图的情况下,可以直接加注解叠加
# 应当将扩展的装饰器放在这里
def home(): # 多路径套用同一个视图 但是一个url不会对应多个视图函数
# 1.接收参数
# 2.调用对应的函数去处理数据
# 3.构建响应结果
return render_template('index.html')
路由参数化
主要有两种方式实现
1,直接从url中获取并传到视图函数中进行处理,并且可以对参数部分进行类型限制
@app.route('/cases/<id>')
def index(id):
return F"{id}hello addicated"
2,第二种,通过request.args的get方法取到值,视图函数中将不再接收形参
@app.route('/cases')
def index1():
id = request.args.get('id')
return F"{id}hello addicated"
路由注册机制
1,集中注册机制
views.py
def index():
reutrn "这是集中注册机制去注册路由"
app.add_url_rule('/index',view_func=views.index)
2,注解注册
@app.route("/index")
def index():
return "这是装饰器注册路由"
app.url_map
flask中专门用来存放url路径与视图函数绑定关系的一个属性
可以体现出路径与视图函数的绑定关系
实例
print(app.url_map)
Map([<Rule '/static/<filename>' (HEAD, GET, OPTIONS) -> static>])
注册之前只有一个static/filename
filename 可变参数,
路由: url 视图函数的绑定关系, static 是端点名称
print(app.url_map)
Map([<Rule '/hello' (HEAD, GET, OPTIONS) -> test>,
test 是端点名称,如果不进行修改,默认为视图函数的名称
# 端点名称可更改,在路由进行注册的时候可以通过属性赋值的方式进行更改
# HEAD,GET,OPTIONS 是允许的请求方式,在路由注册时可以更改
app.rouet()可配置的参数
methods 限制路由的访问方式
@app.route('/hello',methods=['POST'],endpoint='测试命名')
# 注解方式注册路由配置methods
app.add_url_rule('/hello', view_func=test,methods=['POST'])
# 集中式注册机制中对methods设置
endpoint 端点命名
@app.route('/hello',methods=['POST'],endpoint='测试命名')
app.add_url_rule('/hello', view_func=test,
methods=['POST'],endpoint="测试命名")
redirect_to
由编写方式不同成为两种状态
1,直接在 route参数内些重定向地址,配置地址,这种配置方法将直接跳过本视图函数的处理,直接返回给重定向的目标视图函数
@app.route('/hello',methods=['POST'],endpoint='测试命名',redirect_to='/')
2,在视图函数返回值的时候书写,这种方式会在完成本视图的函数处理之后再进行重定向到目标页面
@app.route('/cases')
def index1():
id = request.args.get('id')
return redirect('/')
defaults
默认值设定,在设计路由参数化的案例中,有的时候需要给一个属性赋一个默认值,
同样根据路由注册方式不偶那个,有两种方式,效果一样
1,在route()参数中去设置,字典 kv对应形式
@app.route('/hello',methods=['POST'],endpoint='测试命名',
redirect_to='/',defaults={'id':3})
2,在视图函数中的形参上设置
@app.route('/cases/<id>')
def index(id=3): # 直接在形参处进行配置,简洁明了
return F"{id}hello addicated"
Flask,大型项目作业时候注意的坑
视图函数的分离
背景:随着项目增大,吧视图函数单独放在一起,不将项目启动,注册路由,视图函数都放一个文件中,会出现分层的需要。所以项目结构会发生改变。
启动文件
视图函数
数据处理
views
其他的帮助函数
views.py 视图层
def index():
return 'home'
def case():
return 'cases'
urls.py 路由分发层
from py_day23 import views
from py_day23.py_day23_2 import app
app.add_url_rule('/',view_func=views.index)
app.add_url_rule('/case',view_func=views.case,methods=['POST'])
run_app.py 启动层
from flask import Flask, request
app = Flask(__name__)
if __name__ == '__main__':
app.run(debug=True)
在这样分层的时候,很容出现的一个问题是,循环导入。因为平常的编写习惯,
很容将需要用到的包一股脑的全在项目文件头部进行一次性导入,这样当引用出现交叉时,
极易出现循环导入,而又难以去分析错误成因。
最好的方式是在编写大型项目的时候,用到什么就导入什么,不要偷懒一次性导入
并且在导入的时候不要导入模块,而是用到什么代码就导入模块下的代码,* 也是可以进行使用的