Flask基礎 Flask基礎
Flask基礎
Flask初識
Flask是一個基於Python開發並且依賴jinja2模板和Werkzeug WSGI服務的一個微型框架,
對於Werkzeug本質是Socket服務端,其用於接收http請求並對請求進行預處理,然後觸發Flask框架,
開發人員基於Flask框架提供的功能對請求進行相應的處理,並返回給使用者,如果要返回給使用者複雜的內容時,
需要藉助jinja2模板來實現對模板的處理,即:將模板和資料進行渲染,將渲染後的字串返回給使用者瀏覽器。
下載:
pip install flask
werkzeug
werkzeug類比django的wsgiref模組,封裝了sorket。
from werkzeug.wrappers import Request, Response from werkzeug.serving import run_simple @Request.application def hello(request): return Response('Hello World!') if __name__ == '__main__': run_simple('localhostwerkzeug', 4000, hello)
基本的使用
一 FlaskDemo
from flask import Flask # 例項化一個Flask物件 給它指定一個名字 app = Flask(__name__) @app.route("/index") def index(): return "hello~~" if __name__ == '__main__': app.run()
# by gaoxin from flask import Flask, render_template, request, redirect app = Flask(__name__) @app.route("/login", methods=["GET", "POST"]) def login(): if request.method == "GET": return render_template("login.html") user = request.form.get("user") pwd = request.form.get("pwd") if user == "gaoxin" and pwd == "123": return redirect("/index") else: return render_template("login.html", error="使用者名稱或密碼錯誤") # return render_template("login.html", **{"error": "使用者名稱或密碼錯誤"}) @app.route("/index") def index(): return render_template("index.html") if __name__ == '__main__': app.run() # 注意tempaltes以及static的配置登入demo
二 配置檔案
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, }配置檔案資訊
我們可以在例項化Flask物件之後~列印一下app.config來檢視Flask的配置資訊。
我們也可以通過app.config.xxx = xxx來更改配置資訊,但是通常我們不這樣去做~~
配置檔案的實現方式~~
app.config.from_object("python類或類的路徑") app.config.from_object('專案根路徑.settings.TestingConfig') # settings.py class Config(object): DEBUG = False TESTING = False DATABASE_URI = 'sqlite://:memory:' class ProductionConfig(Config): DATABASE_URI = 'mysql://[email protected]/foo' class DevelopmentConfig(Config): DEBUG = True class TestingConfig(Config): TESTING = True
三 路由系統
路由的引數
@app.route('/user/<username>') @app.route('/post/<int:post_id>') @app.route('/post/<float:post_id>') @app.route('/post/<path:path>') # 路由預設支援的引數 DEFAULT_CONVERTERS = { 'default': UnicodeConverter, 'string': UnicodeConverter, 'any': AnyConverter, 'path': PathConverter, 'int': IntegerConverter, 'float': FloatConverter, 'uuid': UUIDConverter, }
路由的命名
@app.route('/index.html',methods=['GET','POST'],endpoint='index') # 預設是函式名字
命名路由的反向解析
url_for("路由的名字", nid=32425)
四 模板
Flask模板的使用的是JinJa2模板,語法跟django無差別
細節不一樣的地方就是,模板中函式執行要加括號,更貼近python語法。
支援建立一個函式通過render頁面的形式傳遞到模板中~~
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <body> <h1>自定義函式</h1> {{ww()|safe}} </body> </html>html
from flask import Flask,render_template app = Flask(__name__) def my_func(): return '<h1>自定義函式返回內容</h1>' @app.route('/index', methods=['GET', 'POST']) def index(): return render_template('index.html', ww=my_func)app.py
五 請求和響應
from flask import Flask from flask import request from flask import render_template from flask import redirect from flask import make_response app = Flask(__name__) @app.route('/login.html', methods=['GET', "POST"]) def login(): # 請求相關資訊 # request.method # request.args # request.form # request.values # request.cookies # request.headers # request.path # request.full_path # request.script_root # request.url # request.base_url # request.url_root # request.host_url # request.host # request.files # obj = request.files['the_file_name'] # obj.save('/var/www/uploads/' + secure_filename(f.filename)) # 響應相關資訊 # return "字串" # return render_template('html模板路徑',**{}) # return redirect('/index.html') # response = make_response(render_template('index.html')) # response是flask.wrappers.Response型別 # response.delete_cookie('key') # response.set_cookie('key', 'value') # response.headers['X-Something'] = 'A value' # return response return "內容" if __name__ == '__main__': app.run()
六 Session
除了請求物件外,還有一個session物件,它允許你在不同請求間儲存特定使用者的資訊。
它是在 Cookies 的基礎上實現的,並且對 Cookies 進行加密,你需要設定一個金鑰。
設定session
session['xxxxxx'] = 'xxx'
刪除session
session.pop('xxxx', None)
基本使用
from flask import Flask, session, redirect, url_for, escape, request app = Flask(__name__) app.secret_key = 'lajsdgjasdg' @app.route('/') def index(): if 'username' in session: return "已經登入" return "未登入" @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': session['username'] = request.form['username'] return redirect(url_for('index')) return render_template('login.html'i) @app.route('/logout') def logout(): session.pop('username', None) return redirect(url_for('index'))基本使用
七 flash
flash是一個基於session實現的用於儲存資料的集合,特點是使用一次就刪除.
# by gaoxin from flask import Flask, flash, get_flashed_messages app = Flask(__name__) app.config["SECRET_KEY"]= "xxxxxxxx" @app.route("/set") def index1(): flash("hi") # flash("hello", "msg") return "設定flash值" @app.route("/get") def index2(): msg = get_flashed_messages() # msg2 = get_flashed_messages(category_filter=["msg"]) # print(msg2) print(msg) return "獲取flash內容" if __name__ == '__main__': app.run()flash
八 中介軟體
Flask的中介軟體跟Django的不太一樣~我們需要從原始碼開始理解~開始理解前~我們先來複習點知識點~~
類加括號以及物件加括號分別執行什麼~~
class A(): def __init__(self): print("init") def __call__(self, *args, **kwargs): print("__call__") a = A() a()__call__
現在我們開始走原始碼~~我們說過werkzeug是我們封裝sorket的地方~原始碼是從run_simple方法開始走的~~
那我們看下app.run()做了什麼~~
呼叫了werkzueg的run_simple方法~ run_simple(host, port, self, **options)
這個方法中是self是我們的app, 我們知道werkzeug會執行app()
這是我們程式的入口~~會執行我們app.__call__方法~~
那現在如果我想在請求進來之前做一些事情,以及請求結束以後做一些事情~~
下面我們看實現中間價效果的兩種方式~
def __call__(self, environ, start_response): """The WSGI server calls the Flask application object as the WSGI application. This calls :meth:`wsgi_app` which can be wrapped to applying middleware.""" # return self.wsgi_app(environ, start_response) print("開始之前") ret = self.wsgi_app(environ, start_response) print("請求之後") return ret修改原始碼 不建議!!
class MiddleWare(object): def __init__(self, old): self.old = old def __call__(self, *args, **kwargs): print("start") ret = self.old(*args, **kwargs) print("end") return ret if __name__ == '__main__': app.wsgi_app = MiddleWare(app.wsgi_app) app.run() app.__call__ app.wsgi_app中介軟體類
通常我們不會這樣去實現中介軟體~Flask中有一些特殊的裝飾器能夠幫我們實現中介軟體的功能~~
九 特殊的裝飾器
需求~我們很多訪問需要進行登入認證~~怎麼實現這個登入認證的功能~
先來複習點裝飾器的知識~被裝飾器裝飾的函式名問題~~
def wrapper(func): def inner(*args, **kwargs): ret = func(*args, **kwargs) return ret return inner @wrapper def index(): return "1111" print(index.__name__) # 被裝飾器裝飾的函式名字會變成內部的函式名 # 我們需要給inner函式加個裝飾器來修改inner函式的資訊 import functools @functools.wraps(func) def inner()......被裝飾器裝飾的函式名
自定義裝飾器實現認證
# by gaoxin from flask import Flask, session, redirect, url_for import functools app = Flask(__name__) app.secret_key = "xxxxxxxx" # 實現登入 我們不可能每個檢視函式都去獲取session然後判斷 # 我們可以利用裝飾器 # 注意裝飾器裝飾完函式名的問題 # 注意裝飾器執行順序問題 def auth(func): @functools.wraps(func) def inner(*args, **kwargs): if not session["userinfo"]: return redirect(url_for("login")) ret = func(*args, **kwargs) return ret return inner @app.route("/login") def login(): session["userinfo"] = "gaoxin" return "登入成功" @app.route("/user") @auth def user(): return "使用者管理頁面" @app.route("/book") @auth def book(): return "圖書管理頁面" if __name__ == '__main__': app.run()裝飾器認證
特殊裝飾器before_request
@app.before_request def auth2(): if request.path == "/login": return None if session.get("userinfo"): return None return redirect("/login")before_request
其他特殊裝飾器
@app.template_global()
@app.template_filter()
@app.before_request
@app.after_request
@app.before_firse_request
@app.errorhandler(404)
from flask import Flask, Request, render_template app = Flask(__name__, template_folder='templates') app.debug = True @app.before_first_request def before_first_request1(): print('before_first_request1') @app.before_first_request def before_first_request2(): print('before_first_request2') @app.before_request def before_request1(): Request.nnn = 123 print('before_request1') @app.before_request def before_request2(): print('before_request2') @app.after_request def after_request1(response): print('before_request1', response) return response @app.after_request def after_request2(response): print('before_request2', response) return response @app.errorhandler(404) def page_not_found(error): return 'This page does not exist', 404 @app.template_global() def sb(a1, a2): return a1 + a2 @app.template_filter() def db(data): return data[::2] @app.route('/') def hello_world(): return render_template('hello.html') if __name__ == '__main__': app.run() # by gaoxin例子
!!!注意這裡執行書序問題
如果我在before_request返回一直值,會怎麼樣呢~~~還會不會走after_request呢~~
這個大家可以試一下~~
在django的<=1.9版本的跟Flask是一樣的,就算before_request返回值了也會走所有響應~~
當時Django1.0版本以後就直接返回了~~
十 檢視
之前我們的檢視都是FBV,那麼我們的CBV要怎麼實現呢~~
實現CBV之前~我們要看下路由的實現原理~~
我們看下@app.route("/xx")做了什麼~~
首先這是一個帶引數的裝飾器~那麼帶引數的裝飾器執行順序是什麼樣的~~
1,先去掉@ 執行route("/xx")得到返回值
2, 再拿返回值加上@符號去裝飾接下來的函式
我們走進原始碼會發現~呼叫了add_url_rule方法~~
那麼我們在實現路由的時候就可以這麼做~~
# by gaoxin from flask import Flask app = Flask(__name__) def index(): return "index" app.add_url_rule("/", endpoint="index", view_func=index) if __name__ == '__main__': app.run()
那麼我們的CBV就可以寫了~~
# by gaoxin from flask import Flask, views, session, redirect, url_for import functools app = Flask(__name__) def auth(func): @functools.wraps(func) def inner(*args, **kwargs): if not session["userinfo"]: return redirect(url_for("login")) ret = func(*args, **kwargs) return ret return inner class MyView(views.MethodView): methods = ["GET", "POST"] decorators = [auth, ] def get(self): return "GET" def post(self): return "POST" # name == endpoint app.add_url_rule("/", view_func=MyView.as_view(name="index")) if __name__ == '__main__': app.run()CBV程式設計
拓展~~
from flask import Flask, views, 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): """ 路由匹配時,匹配成功後傳遞給檢視函式中引數的值 :param value: :return: """ return int(value) def to_url(self, value): """ 使用url_for反向生成URL時,傳遞的引數經過該方法處理,返回的值用於生成URL中的引數 :param value: :return: """ val = super(RegexConverter, self).to_url(value) return val # 新增到flask中 app.url_map.converters['regex'] = RegexConverter @app.route('/index/<regex("\d+"):nid>') def index(nid): print(url_for('index', nid='888')) return 'Index' if __name__ == '__main__': app.run()自定義路由的正則匹配
十一 藍圖
為我們提供目錄的劃分,就是解耦用的~~
我沒看下藍圖的目錄結構~~
1,建立一個專案~ 專案下建立一個同名的python的包~~
2,建立一個manage.py 啟動檔案 執行app.run()
3, 在python包下的init檔案下 例項化Flask 得到app
4,下Python包下面建立views資料夾,用藍圖註冊檢視和路由~
5,把檢視中的藍圖物件註冊到APP中~~
from flask imort Blueprint userBlue = Blueprint("userBlue", __name__) @userBlue.route("/user") def user(): return "USER"生成藍圖物件
from .view.user import userBlue from flask import Flask def create_app(): app = Flask(__name) app.register_blueprint(userBlue) return appapp中註冊藍圖
每個藍圖可以指定自己的模板以及靜態檔案~
還可以在app中註冊藍圖的時候指定路由字首~~
還可以給藍圖加before_request~~
Flask初識
Flask是一個基於Python開發並且依賴jinja2模板和Werkzeug WSGI服務的一個微型框架,
對於Werkzeug本質是Socket服務端,其用於接收http請求並對請求進行預處理,然後觸發Flask框架,
開發人員基於Flask框架提供的功能對請求進行相應的處理,並返回給使用者,如果要返回給使用者複雜的內容時,
需要藉助jinja2模板來實現對模板的處理,即:將模板和資料進行渲染,將渲染後的字串返回給使用者瀏覽器。
下載:
pip install flask
werkzeug
werkzeug類比django的wsgiref模組,封裝了sorket。
from werkzeug.wrappers import Request, Response from werkzeug.serving import run_simple @Request.application def hello(request): return Response('Hello World!') if __name__ == '__main__': run_simple('localhost', 4000, hello)werkzeug
基本的使用
一 FlaskDemo
from flask import Flask # 例項化一個Flask物件 給它指定一個名字 app = Flask(__name__) @app.route("/index") def index(): return "hello~~" if __name__ == '__main__': app.run()
# by gaoxin from flask import Flask, render_template, request, redirect app = Flask(__name__) @app.route("/login", methods=["GET", "POST"]) def login(): if request.method == "GET": return render_template("login.html") user = request.form.get("user") pwd = request.form.get("pwd") if user == "gaoxin" and pwd == "123": return redirect("/index") else: return render_template("login.html", error="使用者名稱或密碼錯誤") # return render_template("login.html", **{"error": "使用者名稱或密碼錯誤"}) @app.route("/index") def index(): return render_template("index.html") if __name__ == '__main__': app.run() # 注意tempaltes以及static的配置登入demo
二 配置檔案
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, }配置檔案資訊
我們可以在例項化Flask物件之後~列印一下app.config來檢視Flask的配置資訊。
我們也可以通過app.config.xxx = xxx來更改配置資訊,但是通常我們不這樣去做~~
配置檔案的實現方式~~
app.config.from_object("python類或類的路徑") app.config.from_object('專案根路徑.settings.TestingConfig') # settings.py class Config(object): DEBUG = False TESTING = False DATABASE_URI = 'sqlite://:memory:' class ProductionConfig(Config): DATABASE_URI = 'mysql://[email protected]/foo' class DevelopmentConfig(Config): DEBUG = True class TestingConfig(Config): TESTING = True
三 路由系統
路由的引數
@app.route('/user/<username>') @app.route('/post/<int:post_id>') @app.route('/post/<float:post_id>') @app.route('/post/<path:path>') # 路由預設支援的引數 DEFAULT_CONVERTERS = { 'default': UnicodeConverter, 'string': UnicodeConverter, 'any': AnyConverter, 'path': PathConverter, 'int': IntegerConverter, 'float': FloatConverter, 'uuid': UUIDConverter, }
路由的命名
@app.route('/index.html',methods=['GET','POST'],endpoint='index') # 預設是函式名字
命名路由的反向解析
url_for("路由的名字", nid=32425)
四 模板
Flask模板的使用的是JinJa2模板,語法跟django無差別
細節不一樣的地方就是,模板中函式執行要加括號,更貼近python語法。
支援建立一個函式通過render頁面的形式傳遞到模板中~~
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <body> <h1>自定義函式</h1> {{ww()|safe}} </body> </html>html
from flask import Flask,render_template app = Flask(__name__) def my_func(): return '<h1>自定義函式返回內容</h1>' @app.route('/index', methods=['GET', 'POST']) def index(): return render_template('index.html', ww=my_func)app.py
五 請求和響應
from flask import Flask from flask import request from flask import render_template from flask import redirect from flask import make_response app = Flask(__name__) @app.route('/login.html', methods=['GET', "POST"]) def login(): # 請求相關資訊 # request.method # request.args