FLASK框架基础知识一
Flask 本是作者 Armin Ronacher在2010年4月1日的一个愚人节玩笑 ,不过后来大受欢迎,进而成为一个正式的python编写的web框架
Flask是一个Python编写的Web 微框架,让我们可以使用Python语言快速实现一个网站或Web服务,在介绍Flask之前首先来聊下它和Django的联系以及区别,django是大而全的web框架,它内置许多模块,flask是一个小而精的轻量级框架,Django功能大而全,Flask只包含基本的配置, Django的一站式解决的思路,能让开发者不用在开发之前就在选择应用的基础设施上花费大量时间。Django有模板,表单,路由,基本的数据库管理等等内建功能。与之相反,Flask只是一个内核,默认依赖于2个外部库: Jinja2 模板引擎和 WSGI工具集--Werkzeug , flask的使用特点是基本所有的工具使用都依赖于导入的形式去扩展,flask只保留了web开发的核心功能。
WSGI(web服务器网关接口)是python中用来规定web服务器如何与python Web服务器如何与Python Web程序进行沟通的标准,本质上就是一个socket服务端。而 Werkzeug模块 就是WSGI一个具体的实现
关键词:一个Python编写微web框架 一个核心两个库( Jinja2 模板引擎 和 WSGI工具集)
wsgiref
最简单的Web应用就是先把HTML用文件保存好,用一个现成的HTTP服务器软件,接收用户请求,从文件中读取HTML,返回。
如果要动态生成HTML,就需要把上述步骤自己来实现。不过,接受HTTP请求、解析HTTP请求、发送HTTP响应都是苦力活,如果我们自己来写这些底层代码,还没开始写动态HTML呢,就得花个把月去读HTTP规范。
正确的做法是底层代码由专门的服务器软件实现,我们用Python专注于生成HTML文档。因为我们不希望接触到TCP连接、HTTP原始请求和响应格式,所以,需要一个统一的接口协议来实现这样的服务器软件,让我们专心用Python编写Web业务。这个接口就是WSGI:Web Server Gateway Interface。而wsgiref模块就是python基于wsgi协议开发的服务模块
from wsgiref.simple_server import make_server
def mya(environ, start_response):
print(environ)
start_response('200 OK', [('Content-Type', 'text/html')])
if environ.get('PATH_INFO') == '/index':
with open('index.html','rb') as f:
data=f.read()
elif environ.get('PATH_INFO') == '/login':
with open('login.html', 'rb') as f:
data = f.read()
else:
data=b'<h1>Hello, web!</h1>'
return [data]
if __name__ == '__main__':
myserver = make_server('', 8011, mya)
print('监听8010')
myserver.serve_forever()
wsgiref简单应用
from werkzeug.wrappers import Request, Response
2 为什么要有flask?
flask性能上基本满足一般web开发的需求, 并且灵活性以及可扩展性上要优于其他web框架, 对各种数据库的契合度都非常高
关键词:
-
性能基本满足需求
-
灵活性可拓展性强
-
对各种数据库的契合度都比较高。
4.在真实的生产环境下,小项目开发快,大项目设计灵活
3.如何启动一个flask项目
# 安装flask
'''
pip install flask
'''
# 1 导入flask,我们要用flask,就必须导入Flask
from flask import Flask
# 2 生成一个Flask对象,__name__表示当前文件的名字
app = Flask(__name__)
# 3 添加路由,flask用的是装饰器的模式
#注册路由,并写响应函数index
# 点击app = Flask(__name__),可以看到源码中flask实例化时需要的参数
def __init__(
self,
# 表示入口文件的名字,即__name__
import_name,
# 静态路由
static_url_path=None,
# 静态文件夹
static_folder="static",
static_host=None,
host_matching=False,
subdomain_matching=False,
# HTML文件夹
template_folder="templates",
instance_path=None,
instance_relative_config=False,
root_path=None,
):
3.flask四剑客 (返回字符串,返回html,跳转路由,返回json)
# 1 如何响应一个字符串
# 2 如何响应一个html页面
# 3 如何跳转页面
# 4 如何返回一个json字符串
from flask import Flask,render_template,redirect,jsonify
app = Flask(__name__)
4.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,
}
# 4中方法给flask做配置
# 1直接给app对象赋值属性
# 2 以字典的形式,给flask做配置
# 3 以文件的形式,给flask做配置(django就是用这种)
# 4 以类的形式,给flask做配置(如果用flask,推荐是使用第4中)
from flask import Flask
app = Flask(__name__)
# 1方式1(不推荐),因为他只能配置两个配置项,一个是debug 一个是secret_key
# app.debug = True
# 2 方式2 字典的形式,这个里面就可以对所有的flask配置项做配置
#app.config["DEBUG"] = True
#3 方式3 以文件的形式,在form_pyfile(里面传递配文件的路径)
#app.config.from_pyfile("settings.py")
#4 方式4 以类的形式,那为什么推荐大家使用这个呢?因为他可以实现一个文件多个配置,而且减少测试与上线更改的配置项(通过开发和上线的配置类来继承基础配置类实现)
app.config.from_object("setobj.settings")
#5.通过环境变量配置
app.config.from_envvar("环境变量名称")
#app.config.from_pyfile(os.environ['YOURAPPLICATION_SETTINGS'])
环境变量的值为python文件名称名称,内部调用from_pyfile方法
# 6.通过json文件
app.config.from_json("json文件名称")
JSON文件名称,必须是json格式,因为内部会执行json.loads
5.路由本质以及参数
from flask import Flask,url_for,redirect
app = Flask(__name__)
# @app.route("/")
def index(nid):
print(nid,type(nid))
return "ojbk"
# 根据下面源码的分析@app.route可以化成下面这句话
#add_url_rule(“/”, view_function = index )
'''
源码:
def decorator(f): # 这个f指的就是上面的index函数对象
endpoint = options.pop("endpoint", None)
self.add_url_rule(rule, endpoint, f, **options)
return f
'''
'''
@setupmethod
def add_url_rule(
self,
rule,
endpoint=None,
view_func=None,
provide_automatic_options=None,
**options
):
'''
#@app.route的本质就在执行add_url_rule这个中的rule是路由,endpoint是路由别名,view_func是响应函数
#如果endpoint不传就是响应的函数名
app.add_url_rule("/index/<int:nid>", endpoint="index1",view_func=index,methods=["POST","GET"])
# methods既可以在上面这句里面写也可以在下面这句里面写
6.自定义转化器
# 非重点
# 1 写类,继承BaseConverter
# 2 注册:app.url_map.converters['regex'] = RegexConverter
# 3 使用:@app.route('/index/<regex("\d+"):nid>') 正则表达式会当作第二个参数传递到类中
from flask import Flask, url_for
from werkzeug.routing import BaseConverter
app = Flask(import_name=__name__)
# 1.自定义一个类,继承BaseConverter
class RegexConverter(BaseConverter):
"""
自定义URL匹配正则表达式
"""
# 2.继承父类的init和重写to_python和to_user方法
def __init__(self, map, regex):
super(RegexConverter, self).__init__(map)
self.regex = regex
def to_python(self, value):
"""
路由匹配时,匹配成功后传递给视图函数中参数的值
"""
print("to_python", value, type(value))
return int(value) + 1
def to_url(self, value):
"""
使用url_for反向生成URL时,传递的参数经过该方法处理,返回的值用于生成URL中的参数
"""
# 因为父类的此方法做了处理,因此需要继承父类的方法
val = super(RegexConverter, self).to_url(value)
return val + "222"
# 3.注册添加到flask中,regex要和init中的regex名字一样,RegexConverter和类名一样
app.url_map.converters['regex'] = RegexConverter
# 4.regex这个名字也要与上面的一致,然后就可以写自定义正则了,正则匹配处理结果,要交给to_python,
# to_python函数可以对匹配处理结果做处理,返回的就是下面index接收的nid
总结:
1 导入from werkzeug.routing import BaseConverter
2 我写个继承BaseConverter。实现3个方法,def __init__ , def to_python , def to_url
3 将上面的类注册到app.url_map.converters['regex'] = RegexConverter中,注意regex必须上下名字一致,RegexConverter和自定义类名相同
4 然后就可以在路由转化器中使用3中的regex("传正则")
5 当路由被访问以后。regex("传正则")会匹配结果,把结果传递to_python,我们可以进行再次处理,to_python处理好的结果,会传递给响应函数的形参
6 当用url做反向解析的时候,传递给路由转化器的参数,会经过to_url,进行处理。处理以后,在拼接到路由。
7.flask的模板渲染
from flask import Flask,render_template,Markup
app = Flask(__name__)
app.debug = True
USERS = {
1:{'name':'张三','age':18,'gender':'男','text':"道路千万条"},
2:{'name':'李四','age':28,'gender':'男','text':"安全第一条"},
3:{'name':'王五','age':18,'gender':'女','text':"行车不规范"},
}
# 传递html语句
def func1(arg,tank):
# 需要导入Markup
return Markup(f"<h1>饼哥正帅,{arg} is sb {tank} is same as {arg}</h1>")
html文件
8.flask的请求与响应
from flask import Flask,request,make_response,render_template,redirect
app = Flask(__name__)
9.flask的session
from flask import Flask,session
app = Flask(__name__)
# 要用session,必须app配置一个密钥
app.secret_key = "asdasdihasdiuh"
app.config['SESSION_COOKIE_NAME']="python13session"
# app.session_interface
#app.session_interface实现了两个方法,一个叫save_session,一个open_session,
分析session的原理
class SecureCookieSessionInterface(SessionInterface):
salt = "cookie-session"
digest_method = staticmethod(hashlib.sha1)
key_derivation = "hmac"
serializer = session_json_serializer
session_class = SecureCookieSession
def get_signing_serializer(self, app):
if not app.secret_key:
return None
signer_kwargs = dict(
key_derivation=self.key_derivation, digest_method=self.digest_method
)
return URLSafeTimedSerializer(
app.secret_key,
salt=self.salt,
serializer=self.serializer,
signer_kwargs=signer_kwargs,
)
# 取session的时候执行的
def open_session(self, app, request):
s = self.get_signing_serializer(app)
if s is None:
return None
##cookie键是SESSION_COOKIE_NAME"=session
val = request.cookies.get(app.session_cookie_name)
print("open_session.session_cookie_name,", app.session_cookie_name, )
if not val:
return self.session_class()
max_age = total_seconds(app.permanent_session_lifetime)
try:
data = s.loads(val, max_age=max_age)
print("self.session_class(data)", self.session_class(data) )
return self.session_class(data)
except BadSignature:
return self.session_class()
#存session的时候执行的
def save_session(self, app, session, response):
domain = self.get_cookie_domain(app)
path = self.get_cookie_path(app)
# If the session is modified to be empty, remove the cookie.
# If the session is empty, return without setting the cookie.
if not session:
if session.modified:
response.delete_cookie(
app.session_cookie_name, domain=domain, path=path
)
return
# Add a "Vary: Cookie" header if the session was accessed at all.
if session.accessed:
response.vary.add("Cookie")
if not self.should_set_cookie(app, session):
return
httponly = self.get_cookie_httponly(app)
secure = self.get_cookie_secure(app)
samesite = self.get_cookie_samesite(app)
expires = self.get_expiration_time(app, session)