''' pip install 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()
2.1 直接返回字符串 2.2 render_template("html页面") 2.3 redirect 页面跳转 2.4 jsonify 返回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 "你号,我是字符串" # 1 返回一个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()
3.1 直接给app属性赋值 只能给app.debug和app.secret_key配置 3.2 给app.config["DEBUG"]等设置属性 3.3 文件的形式给flask作配置,app.config.from_pyfile("settings.py"),这个和django一样 3.4 以类的形式给flask作配置,app.config.from_object("setting.Test"),表示setting文件中Test为配置。推荐使用这样
# 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(里面传递配文件的路径)这个和django一样 #app.config.from_pyfile("settings.py") # settings.py中代码如下 # DEBUG = True #4 方式4 以类的形式,那为什么推荐大家使用这个呢?因为他可以实现一个文件多个配置,而且减少测试与上线更改的配置项 app.config.from_object("setobj.settings") # setobj.py中的代码如下 # class settings: # DEBUG = True @app.route("/") def index(): return "jason is dsb" if __name__ == '__main__': app.run()
4.1 我们可以有两种方式配置路由,第一种是装饰器的形式@app.route("/"),第二种,app.add_url_rule("/",view_func=响应函数函数对象) 4.2 路由种的参数之rule:就是路由,他作有名分组,@app.route("/<int:nid>"),响应函数的接收这个参数必须和nid一致 4.3 路由参数之endpoint,用于反向解析的,和以不传,如果不传就是用响应的函数名,做为反向的。 4.4 如何做反向解析,url_for(endpoint),如果用"/<int:nid>"那我们写url_for("index",nid=111) 4.5 路由参数之view_func:表示的是响应函数的函数对象 4.6 路由参数之methods,该路由可以允许哪些请求方法请求
from flask import Flask,url_for,redirect app = Flask(__name__) # @app.route("/") def index(nid): # 必须与有名分组<int:nid>中的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,可以控制该方法能有哪些请求方式可以访问 默认为get方法 #app.add_url_rule("/index", endpoint="index1",view_func=index,methods=["POST"]) 等价于@app.route('/login',methods=['POST']) # 路由参数:有名分组,app.add_url_rule("/index/<int:nid>"响应函数必须用nid来接收 # ps:经过测试,当methods=["POST"],若login跳转到index1路径,postman发post方法不被允许。但是若method方法有methods=["GET"],login可以发送get请求 if __name__ == '__main__': app.run() ''' 总结: 1 @app.route("/login") 的本质app.add_url_rule("/login",view_func=login),所以我们就可以用这两个方式来添加路由 2 路由的参数, 2.1 endpoint,做是反向解析,如果上面添加路由的时候,没有传递endpoint就是使用响应函数的函数名,反向解析用url_for(),做解析,这个url_for必须在flask里面导入。url_for(endpoint),如果用"/<int:nid>"那我们写url_for("index",nid=111) 2.2 methods=["POST","GET"],该参数控制路由允许哪些请求方法访问,如果不传,默认只能GET方法 2.3 路由以及路由路由转化器。"/index/<int:nid>",<参数的类型:用哪个变量来接收>,响应函数中的形参的名字必须转化器中一致。 '''
#非重点 #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)+1 def to_url(self, value): """ 使用url_for反向生成URL时,传递的参数经过该方法处理,返回的值用于生成URL中的参数 """ val = super(RegexConverter, self).to_url(value) return val+"222" # 添加到flask中 把定义的类注册到converters app.url_map.converters['regex'] = RegexConverter # regex与RegexConverter类中的构造函数regex一致 # 正则匹配处理结果,要交给to_python,to_python函数可以对匹配处理结果做处理 @app.route('/index/<regex("d+"):nid>') # 名字与上面的regex保持一致 def index(nid): print("index",nid,type(nid)) # index 89 <class 'int'> print(url_for('index', nid='888')) # /index/888222 return 'Index' if __name__ == '__main__': app.run()
总结:
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,进行处理。处理以后,在拼接到路由。
渲染模板语言和django很像,都是用{{}},{%%}, 注意点:1 flask给模板传值,render_template("index.htm",user=user,name=name) 2 flask的模板语言支持对函数的调用。for 循环的循环对象,再py中要加括号的,模板语言中也要加括号。 3 显示原生的html 如果是管道符 html|safe,如果是后端处理,就是用Markup
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':"行车不规范"}, } def func1(arg,tank): return Markup(f"<h1>饼哥正帅,{arg} is sb {tank} is same as {arg}</h1>") @app.route("/") def index(): # data = { # "user" :USERS, # "name": "jason" # } return render_template("index.html",user = USERS,name="jason",ht1 = func1,ht="<h1>饼哥正帅</h1>") #return render_template("index.html",**data) if __name__ == '__main__': app.run()
html文件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1> 我是html</h1> <table> {% for k,v in user.items() %} <!--flask允许内部加括号--> <tr> <td>{{ k }}</td> <td>{{ v.name }}</td> <td>{{ v['name'] }}</td> <td>{{ v.get('name') }}</td> <td>{{url_for("index")}}</td> </tr> {% endfor %} </table> <div>{{name}}</div> {% if name == "jason" %} <h1>is sb</h1> {% else %} <h1>水哥</h1> {% endif %} {{ ht|safe}} {{ht1("jaosn","tank")}} </body> </html>
5.1 请求,所有的请求都要从flask种导入request.所有请求的东西都在request
5.2 响应相关,把flask的四剑客传到make_response(四剑客),得到一个response对象,我们就可以对response设置请求头,设置cookie。
from flask import Flask,request,make_response,render_template,redirect app = Flask(__name__) @app.route("/",methods=["POST","GET"]) 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:500 #关于响应我们已经将了4剑客,如果添加响应头,已经cookie #要添加这些东西必须,导入make_response, response = make_response("ok") #response = make_response(render_template("login.html")) #response = make_response(redirect("/login")) #设置cookie #response.set_cookie("key","val") #如何删除cookie #response.delete_cookie("key") # 如何设置响应头 response.headers["x-somexx"] = "A SB" return response
直接设置加密成cookie返回给前端
7.1 全局导入session,把session,当字典存值,取就当字典取值 7.2 原理之存session 当响应要返回给客户端时候,会调用sesssion_interface中的save_session方法。把全局session字典做加密得到val,然后将这个val设置到cookie中。cookie的键为配置文件中的session_cookie_name,值就是我们session字典加密得到的结果。 7.3 原理之取session 当flask接收到请求的时候,会调用sesssion_interface中的open_session方法,该方法中从cookie中取键为配置文件中session_cookie_name的cookie值,得到这个值以后呢,做解密。然后赋值给全局的session字典。这样我们就可以取到之前flask设置session。 7.4 注意。用session必须配置app.secret_key="随便"
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, @app.route("/",) def index(): #如何设置sessoion # 1 导入session # 2 给sessoion设置值 session['name'] = "egon" session["nam1"] ="sdsd" return "ok" @app.route("/login") def login(): print(session["name"]) return "login" if __name__ == '__main__': app.run()
分析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) # 把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, )
8.1 要用必须导入flash,get_flashed_messages, 8.2 flash 用于存闪现的值。他有两个参数,1 messsage,用来存储信息 2 category ,用于给信息分类,该参数可以不传,不传就是分类为”messsage“ 8.3 get_flashed_messages 用是用来取闪现的值,他也有两个参数:1 with_category,拿到的结果是否需要带上分类名称,2 category_filter 是用来过滤我要取哪个分类下的信息。当然这个两个都是可选的。 注意点: 1 设置flash,必须设置app.secret_key="随便",因为flash是基于session的。 2 闪现的值,不同的请求中只能取一次,当然再同一请求内是可以获取多次的。
from flask import Flask,flash,get_flashed_messages app = Flask(__name__) #app.session_interface app.secret_key ="sdasd" # 什么闪现:就像session一样,也是一个页面设置,另一个页面使用,我不管你在哪个页面调用的 # 只要调用一次,就清空了, # 闪现的作用,一般用信息处理。假设用户,a页面做操作,产生了信息。我希望在b页面内获取。 # 但是我不知道用户在什么时候,访问b页面,但是只要用户一旦访问页面就把信息显示出来。 # 同一页面,同次请求是可以拿多次的 @app.route("/") def index(): #产生信息,message设置消息的,category给消息分类,如果不传默写用”message“ flash("你错过了我") flash(message="你再次错过我",category="渣男") return "index" @app.route("/login") def login(): #(with_categories=True,消息是否要带上分类信息,category_filter=["渣男"]对消息进行过滤,取指定的分类消息 print(get_flashed_messages(with_categories=True,category_filter=["渣男"])) print(get_flashed_messages()) return "login" @app.route("/test") def test(): print(get_flashed_messages()) return "test" if __name__ == '__main__': app.run() # 注意:即使filter过滤取,不取出来,其他网页也不能继续使用