flask基础
flask基础
1、初识flask
1、什么是flask
Flask是一个Python编写的Web 微框架,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一个具体的实现
2、为什么要flask
flask性能上基本满足一般web开发的需求, 并且灵活性以及可扩展性上要优于其他web框架, 对各种数据库的契合度都非常高
2、如何启动一个flask项目
1、下载flask模块
pip install flask
2、启动flask项目
# 1 导入flask,我们要用flask,就必须导入Flask from flask import Flask # 2 生成一个Flask对象,__name__表示当前文件的名字 app = Flask(__name__) # 3 添加路由,flask用的是装饰器的模式 #注册路由,并写响应函数index @app.route("/") def index(): return "Hello flask" if __name__ == '__main__': #4 启动flask #run里面是执行了run_simple(host,port,self=app,也就是flask对象) app.run()
3、flask四剑客
响应函数返回的结果四种方式:返回字符串、返回HTML、跳转路由、返回json
# 1 如何响应一个字符串 # 2 如何响应一个html页面 # 3 如何跳转页面 # 4 如何返回一个json字符串 from flask import Flask,render_template,redirect,jsonify app = Flask(__name__) @app.route("/index") def index(): #1 返回字符串 #return "你号,我是字符串" # 2 返回一个html, # 1 从flask里面导入render_template,2 在同级目录中添加templates文件夹,将html页面这个文件夹底下 #return render_template("index.html") # 3 跳转路由,1 从flask里面导入redirect #return redirect("/login") # 4 返回数据转json返回,要从flask中导入jsonify data = {'name': "jason", "name1": "owen"} return jsonify(data) @app.route("/login") def login(): return "我是login页面" if __name__ == '__main__': app.run()
4、flask配置
默认的配置:
default_config = ImmutableDict( { "ENV": None, "DEBUG": None, "TESTING": False, "PROPAGATE_EXCEPTIONS": None, "PRESERVE_CONTEXT_ON_EXCEPTION": None, "SECRET_KEY": None, "PERMANENT_SESSION_LIFETIME": timedelta(days=31), "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, "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, }
自定义配置的几种方式:
1、直接给app对象加属性
2、以字典的形式,给flask加配置
3、以文件的形式给flask加配置(Django中采用此方式)
4、以类的形式(推荐使用):在一个配置文件中多个配置,为了减少测试与线上更改配置
from flask import Flask app = Flask(__name__) # 在app的下方配置flask配置 # 方式一(不推荐):直接配置app属性,但是只能配置两种属性,debug和secret_key # app.debug = True # 方式二:字典的形式,这个里面就可以配置所有的配置 # app.config['DEBUG'] = True # 方式三:以文件的形式,在from_pyfile中传递文件的路径 # app.config.from_pyfile('settings.py') # 方式四:以类的形式(推荐使用),在一个配置文件中多个配置,为了减少测试与线上更改配置 app.config.from_object('settings_obj.Setting') @app.route('/') def index(): return "index 页面" if __name__ == '__main__': app.run()
5、路由本质以及参数
@app.route("/index") 的本质是app.add_url_rule("/index", view_func=index)所以我们以后可以使用该中方式集中管理路由
路由的参数:
- view_func 是赋值响应函数名
- endpoint 是做反向解析的,如果没有传递就默认是响应函数名,反向解析用 url_for(),做解析,这个url_for必须在flask里面导入
- methods=["POST", "GET"],该参数控制路由允许哪些请求方法访问,如果不传默认是GET方法
- "/index/<int:nid>"是路由以及路由转换器,尖括号过滤类型<参数的类型:用什么变量接收>响应函数中形参的名字要与接收的变量名一致
主要有这几种类型过滤:
string
: 默认的数据类型,接收没有任何斜杠" /"的字符串
int
: 整型
float
: 浮点型
path
: 和string类型相似,但是接受斜杠,如:可以接受参数/aa/bb/cc/多条放在一起
uuid
: 只接受uuid格式的字符串字符串,
from flask import Flask,url_for,redirect app = Flask(__name__) # @app.route("/") def index(nid): print(nid,type(nid)) return "ojbk" #@pp.route的本质就在执行add_url_rule这个中的rule是路由,endpoint是路由别名,view_func是响应函数 #如果endpoint不传就是响应的函数名 app.add_url_rule("/index/<int:nid>", endpoint="index1",view_func=index,methods=["POST","GET"]) @app.route("/login",methods=["POST"]) def login(): #用endpoint取路由要用url_for 在flask中导入,也就是反向解析 print(url_for("index1")) # return redirect(url_for("index1")) #路由参数;methods,可以控制该方法能有哪些请求方式可以访问 #app.add_url_rule("/index", endpoint="index1",view_func=index,methods=["POST"]) # 路由参数:有名分组,app.add_url_rule("/index/<int:nid>"响应函数必须用nid来接收 if __name__ == '__main__': app.run()
6、自定义转换器
1、导入from werkzeug.routing import BaseConverter
2、写个类继承 BaseConverter ,实现3个方法,def __init__(), def to_python(), def to_url()
3、将上面的类注册到app.url_map.converters['regex'] = RegexConverter中
4、然后就可以在路由转化器中使用3中的regex("传正则")
5、当路由被访问以后regex("传正则")会匹配结果,把结果传递给to_python,我们可以进行再次处理,to_python处理好的结果,会传递给响应函数的形参
6、当用url做反向解析的时候,传递给路由转换器的参数,会经过to_url,进行处理,处理以后再拼接到路由
#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__) class RegexConverter(BaseConverter): """ 自定义URL匹配正则表达式 """ 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) def to_url(self, value): """ 使用url_for反向生成URL时,传递的参数经过该方法处理,返回的值用于生成URL中的参数 """ val = super(RegexConverter, self).to_url(value) return val # 添加到flask中 app.url_map.converters['regex'] = RegexConverter # 正则匹配处理结果,要交给to_python,to_python函数可以对匹配处理结果做处理 @app.route('/index/<regex("\d+"):nid>') def index(nid): print("index",nid,type(nid)) print(url_for('index', nid='888')) return 'Index' if __name__ == '__main__': app.run()
7、flask模板渲染
from flask import Flask, render_template, Markup app = Flask(__name__) USERS = { 1:{'name':'张三','age':18,'gender':'男','text':"道路千万条"}, 2:{'name':'李四','age':28,'gender':'男','text':"安全第一条"}, 3:{'name':'王五','age':18,'gender':'女','text':"行车不规范"}, } def func(name): return Markup(f'<h1>{name}真美</h1>') def index(): data = { 'user': USERS, 'name': 'vicky' } # return render_template('index.html',**data) # 或者使用直接传参数给html页面, # 可以传递html标签但是html页面需要用safe处理,或者在后端直接用Markup处理html是安全的前端可以直接使用, # 也可以传递一个函数给html return render_template('index.html',user=USERS, name='vicky', ht='<h1>vicky真美</h1>',htc = func) app.add_url_rule('/', view_func=index) if __name__ == '__main__': app.run()
<body> <h1>index页面</h1> <table> 循环取出后端传递user数据 {%for k,v in user.items() %} <tr> <td>{{k}}</td> <td>{{v.name}}</td> <!-- 可以使用[]取值--> <td>{{v["age"]}}</td> <!-- 可以使用get取值--> <td>{{v.get('text')}}</td> <!-- 可以控制路由跳转--> <td>{{url_for('index')}}</td> </tr> {%endfor%} </table> <!--可以直接拿到传递过来的name--> <div>{{name}}</div> <!--可以做判断--> {% if name=='vicky'%} <h1>美女</h1> {%else%} <h1>哈哈</h1> {%endif%} <!--在后端写的html标签,前端解析--> {{ht|safe}} <!--可以使用函数--> {{htc("vicky")}} </body>
8、请求与响应
from flask import Flask, request, make_response, render_template, redirect, jsonify app = Flask(__name__) @app.route('/') def index(): # 请求相关的信息 # print("请求方法",request.method)#请求方法 # print("get请求的参数",request.args)# get请求的参数 # print("post请求的参数",request.form) #post请求的参数 # print("post,与get的所有参数",request.values)#post,与get的所有参数 # print("请求的cookies",request.cookies)#请求的cookies # 请求相关信息 # request.method 提交的方法 # request.args get请求提及的数据 # request.form post请求提交的数据 # request.values post和get提交的数据总和 # request.cookies 客户端所带的cookie # request.headers 请求头 # request.path 不带域名,请求路径 # request.full_path 不带域名,带参数的请求路径 # request.script_root # request.url 带域名带参数的请求路径 # request.base_url 带域名请求路径 # request.url_root 域名 # request.host_url 域名 # request.host 127.0.0.1:5000 # 响应相关信息 #关于响应我们已经将了4剑客,如果添加响应头,已经cookie #要添加这些东西必须,导入make_response, response = make_response("ok") # response = make_response(render_template('index.html')) # response = make_response(redirect('/login')) # response = make_response(jsonify({"name": "zack"})) # 设置cookie response.set_cookie("k", "v") # 删除cookie response.delete_cookie('k') print("请求cookies", request.cookies) # 设置响应头 response.headers['xxx']="uuu" return response if __name__ == '__main__': app.run()
9、session设置与用法
from flask import Flask, session # 1 导入session app=Flask(__name__) # 2 要用session必须要配置一个secret_key秘钥 app.secret_key='swqe12213kewdn' @app.route('/') def index(): # 3 设置session session['name'] = 'vicky' return 'ok' @app.route('/login') def login(): # 4 取session值 print(session['name']) return 'login' if __name__ == '__main__': app.run()
session原理:
session_interface实现两个方法设置session和取session,save_session和open_session
save_session:在返回之前通过set_cookie将session加密后存入字典中返回
response.set_cookie( app.config['SESSION_COOKIE_NAME' ,加密(session))
open_session:在取session时先通过cookie获取大字典再对其解密得到session
加密(session)= request.cookie("app.config['SESSION_COOKIE_NAME'")
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) # 把session做了一个加密,把整个session的key--》val,全部加密,的到一个value值, #session是一个大字典, val = self.get_signing_serializer(app).dumps(dict(session)) # 他把session加密后得到的val存到cookie里面了 #cookie键是SESSION_COOKIE_NAME"=session print("源码中的session",dict(session)) print("app.session_cookie_name,",app.session_cookie_name,) response.set_cookie( app.session_cookie_name, val, expires=expires, httponly=httponly, domain=domain, path=path, secure=secure, samesite=samesite, )
10、闪现
1、什么是闪现
就像session一样,也是一个页面设置,另一个页面使用,我不管你在哪个页面调用,只能请求一次就清空了
2、为什么要用闪现
一般做信息的处理,假设用户在a页面操作了产生了信息,希望在b页面获取,不管什么时候获取,但只允许拿一次,但是同一个页面同一次请求是可以拿多次的
3、使用闪现
使用闪现前需要设置秘钥 app.secret_key,导入flash与get_flashed_messages
from flask import Flask, flash, get_flashed_messages app = Flask(__name__) app.secret_key = 'asdA' @app.route('/') def index(): # 设置闪现值 # flash('闪现值') # message设置消息,不写默认传给message,category给消息分类,在获取的时候使用 flash(message='闪现值', category='分类1') return 'index' @app.route('/login') def login(): # 只能在同一次请求中获取多次,其他请求都将获取不到 print(get_flashed_messages()) # 获取所有的闪现值 ['闪现值'] # with_categories=True,消息是否要带上分类信息,category_filter=["分类1"]对消息进行过滤,取指定的分类消息 # print(get_flashed_messages(with_categories=True, category_filter=['分类1'])) # [('分类1', '闪现值')] return 'login' if __name__ == '__main__': app.run()