Flask是一個基於Python開發而且依賴jinja2模板和Werkzeug WSGI服務的一個微型框架,對於Werkzeug本質是Socket服務端,其用於接收http請求並對請求進行預處理,而後觸發Flask框架,開發人員基於Flask框架提供的功能對請求進行相應的處理,並返回給用戶,若是要返回給用戶複雜的內容時,須要藉助jinja2模板來實現對模板的處理,即:將模板和數據進行渲染,將渲染後的字符串返回給用戶瀏覽器。html
flask 是輕量級的,而且可擴展性強,可定製性強 的框架。適用於開發小型的網站。開發大型的網站也行,由於flask提供了不少第三方的組件,咱們把flask與第三方的組件結合起來,也能夠搭建一個與Django相似的,集成了不少功能的一個框架。前端
from flask import Flask app = Flask(__name__) @app.route('/index') def index(): return 'index' if __name__ == '__main__': app.run()
2、配置文件python
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, }
修改配置文件的方式:mysql
方式一: app.config['DEBUG'] = True PS: 因爲Config對象本質上是字典,因此還可使用app.config.update(...) 方式二: app.config.from_object("python類或類的路徑")
# 按照不一樣的狀況 ;修改配置 app.config.from_object('settings.DevelopmentConfig') #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
PS: settings.py文件默認路徑要放在程序root_path目錄,若是instance_relative_config爲True,則就是instance_path目錄
寫一個配置文件的字符串怎麼找到配置信息的原理ajax
# 'settings.DevelopmentConfig'經過切割,在importlib 加載模塊,而後反射獲得這個類的對象。dir(obj) 拿到這個類下面的全部屬性和方法 # 再判斷是大寫的,這樣就能夠拿到這個配置下面的配置信息了。
原理的代碼正則表達式
import importlib path = "settings.Foo" p,c = path.rsplit('.',maxsplit=1) m = importlib.import_module(p) cls = getattr(m,c) # 若是找到這個類? #dir(cls)找到這個類下面的全部屬性的字符串,不包含__init__下面的對象屬性 for key in dir(cls): if key.isupper(): print(key,getattr(cls,key)) #類.字符串
3、路由系統(帶參數的裝飾器)redis
@app.route('/index/<int:nid>',methods=['GET','POST']) def index(nid): print(nid) return "Index"
DEFAULT_CONVERTERS = { 'default': UnicodeConverter, 'string': UnicodeConverter, 'any': AnyConverter, 'path': PathConverter, 'int': IntegerConverter, 'float': FloatConverter, 'uuid': UUIDConverter, }
路由系統原理(帶參數的裝飾器原理)sql
''' 帶參數的裝飾器的原理 def route(self, rule, **options): def decorator(f): endpoint = options.pop("endpoint", None) self.add_url_rule(rule, endpoint, f, **options) return f return decorator ''' ''' @app.route('/index', methods=["GET", "POST"]) 執行的過程: 1.先執行 decorator = app.route('/index', methods=["GET", "POST"]) 2. @decorator def index(): pass decorator(index) '''
路由系統裏面的參數:django
@app.route和app.add_url_rule參數: rule, URL規則 view_func, 視圖函數名稱 defaults=None, 默認值,當URL中無參數,函數須要參數時,使用defaults={'k':'v'}爲函數提供參數 endpoint=None, 名稱,用於反向生成URL,即: url_for('名稱') methods=None, 容許的請求方式,如:["GET","POST"] strict_slashes=None, 對URL最後的 / 符號是否嚴格要求, 如: @app.route('/index',strict_slashes=False), 訪問 http://www.xx.com/index/ 或 http://www.xx.com/index都可 @app.route('/index',strict_slashes=True) 僅訪問 http://www.xx.com/index redirect_to=None, 重定向到指定地址 如: @app.route('/index/<int:nid>', redirect_to='/home/<nid>') 或 def func(adapter, nid): return "/home/888" @app.route('/index/<int:nid>', redirect_to=func)
subdomain=None, 子域名訪問 from flask import Flask, views, url_for app = Flask(import_name=__name__) app.config['SERVER_NAME'] = 'wupeiqi.com:5000' @app.route("/", subdomain="admin") def static_index(): """Flask supports static subdomains This is available at static.your-domain.tld""" return "static.your-domain.tld" @app.route("/dynamic", subdomain="<username>") def username_index(username): """Dynamic subdomains are also supported Try going to user1.your-domain.tld/dynamic""" return username + ".your-domain.tld" if __name__ == '__main__': app.run()
#子域名 from flask import Blueprint public = Blueprint('public', __name__) @public.route('/') def home(): return 'hello flask' app.py: app = Flask(__name__) app.config['SERVER_NAME'] = 'example.com' from modules import public app.register_blueprint(public, subdomain='public') 如今能夠經過public.example.com/來訪問public模塊了。
自定製正則路由匹配json
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() b. 自定製正則路由匹配
# -*- coding: utf-8 -*- # @Author: 曾輝 #1.定製類 from werkzeug.routing import BaseConverter from flask import Flask,request,url_for,session from flask_session import Session from flask_session import RedisSessionInterface import redis app = Flask(__name__) #在session數據存放在redis中, 存放的方式 和 Django 中session 是差很少的 , 把隨機生成的特殊字符串放在cookies中, #下次訪問那着這個字符串 找redis裏面去取值。 app.config['SESSION_TYPE'] = 'redis' app.config['SESSION_REDIS'] = redis.Redis(host='127.0.0.1',port=6379,password="zh4350697") Session(app) ''' 自定義url匹配正則表達式 ''' # BaseConverter 父類裏面有 to_python ;to_url class MyurlRegex(BaseConverter): def __init__(self,map,regex): super().__init__(map) self.regex = regex # def to_python(self, value): # return value # def to_url(self, value): # # value = super().to_url(value) # # return value #2.添加到轉換器中 app.url_map.converters["reg"] = MyurlRegex ''' 執行的過程: 1.用戶發送請求 2.flask內部進行正則匹配 3.調用to_python(返回匹配正則表達的結果)的方法 4. to_python方法的返回值會交給視圖函數做爲參數傳入 5. to_url 方法是反向生成url的時候調用的,返回的就是url ''' #3.使用自定義正則 @app.route("/index/<reg('\d+'):nid>",methods=["GET"]) def index(nid): #6 <class 'str'> # nid =int(nid) # print(nid) # #傳入參數的反向生成url # url = url_for("index", nid=987) # print(url) # <SecureCookieSession {'xxx': 123}> ---> session session["xxx"]=123 print(session["xxx"]) return "..." if __name__ == "__main__": # app.__call__ #self.wsgi_app(environ, start_response) app.run()
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 url的參數(GET請求的數據) # request.form form表單(以字典的形式),ajax (POST請求的數據) # request.values 返回GET和POST請求數據 # request.cookies # request.headers # request.path /index ;是 不包含 ip 和 端口 # 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 對象。它容許你在不一樣請求間存儲特定用戶的信息。它是在 Cookies 的基礎上實現的,而且對 Cookies 進行密鑰簽名要使用會話,你須要設置一個密鑰。
# 設置session,是創建在cookies上的,存放到瀏覽器上的,所以要對其加密後在存放。 # 設置session值時的加密 # app.secret_key = "asdasdsad" # session["user"] = "123"
設置:session['username'] = 'xxx' 刪除:session.pop('username', None)
flask-session組件,把session放入redis裏面
#!/usr/bin/env python # -*- coding:utf-8 -*- """ pip3 install redis pip3 install flask-session """ from flask import Flask, session, redirect from flask.ext.session import Session app = Flask(__name__) app.debug = True app.secret_key = 'asdfasdfasd' app.config['SESSION_TYPE'] = 'redis' from redis import Redis app.config['SESSION_REDIS'] = Redis(host='192.168.0.94',port='6379') Session(app) @app.route('/login') def login(): session['username'] = 'alex' return redirect('/index') @app.route('/index') def index(): name = session['username'] return name if __name__ == '__main__': app.run()
-閃現
- 基於session.pop 實現的,實現了 只能取一次的效果
-視圖
-FBV
-CBV
from flask import Flask, redirect, request, url_for,views app = Flask(__name__) import functools def authwarppe(func): @functools.wraps(func) def innder(*args,**kwargs): #執行原函數 ret = func(*args,**kwargs) return ret return innder # CBV class UserView(views.MethodView): #限制訪問的方式 methods = ["GET"] #批量的加裝飾器 decorators = [authwarppe] def get(self,*args,**kwargs): # self.dispatch_request return "get" def post(self, *args, **kwargs): return "post" #路由與視圖的關係 app.add_url_rule("/user",None,UserView.as_view("user")) #反向生成url -- user #UserView.as_view -- view --- self.dispatch_request if __name__ == "__main__": app.run()
一、模板的使用
Flask使用的是Jinja2模板,因此其語法和Django無差異
- 基本數據類型:能夠執行python語法,如:dict.get() list['xx'] - 在模板中傳入函數 - django,自動執行 {{func}} - flask,不自動執行 {{func()}} 必須本身加括號纔會執行函數,否則返回的是函數名字 - 全局定義函數 @app.template_global() def sb(a1, a2): # 在模板裏面調用的方式 {{sb(1,9)}} return a1 + a2 @app.template_filter() def db(a1, a2, a3): # 在模板裏面調用的方式 {{ 1|db(2,3) }} return a1 + a2 + a3 - 模板繼承 layout.html <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>Title</title> <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> <h1>模板</h1> {% block content %}{% endblock %} </body> </html> tpl.html {% extends "layout.html"%} {% block content %} {{users.0}} {% endblock %} - include {% include "form.html" %} form.html <form> asdfasdf asdfasdf asdf asdf </form> - 宏 {% macro ccccc(name, type='text', value='') %} <h1>宏</h1> <input type="{{ type }}" name="{{ name }}" value="{{ value }}"> <input type="submit" value="提交"> {% endmacro %} {{ ccccc('n1') }} {{ ccccc('n2') }} #之後在模板能夠屢次執行; 每調用一次就 執行一次; - 安全 - 前端: {{u|safe}} - 後端: MarkUp("asdf")
#全局的模板渲染 #全局定義函數 @app.template_global() def add(x,y): # 使用{{add(3,7)}} return x*y @app.template_global() def template(): # 使用{{add(3,7)}} return Markup('<input type="text">') # Markup 就至關於 Django 中的 make_safe @app.template_filter() def pow(a,b,c,d): # 使用{{6|pow(3,7,10)}} return a+b+c+d
藍圖用於爲應用提供目錄劃分:
小型應用程序:示例
大型應用程序:示例
from flask import Blueprint
ac = Blueprint("ac",__name__)
其餘: - 自定義的模板,靜態文件 - 給某一類的Url添加前綴 app.register_blueprint(ac,url_prefix='/api') - 給某一類的url添加before_request @ac.before_request def xxx(): print("ac.before_request") @ac.route('/login') def login(): return render_template("login.html")
1. before_request 2. after_request 示例: from flask import Flask app = Flask(__name__) @app.before_request def x1(): print('before:x1') return '滾' @app.before_request def xx1(): print('before:xx1') @app.after_request def x2(response): print('after:x2') return response @app.after_request def xx2(response): print('after:xx2') return response @app.route('/index') def index(): print('index') return "Index" @app.route('/order') def order(): print('order') return "order" if __name__ == '__main__': app.run() 3. before_first_request from flask import Flask app = Flask(__name__) @app.before_first_request def x1(): print('123123') @app.route('/index') def index(): print('index') return "Index" @app.route('/order') def order(): print('order') return "order" if __name__ == '__main__': app.run() 4. template_global 5. template_filter 6. errorhandler 自定製報錯 @app.errorhandler(404) def not_found(arg): print(arg) return "沒找到"
#!/usr/bin/env python # -*- coding:utf-8 -*- 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(a1, a2, a3): return a1 + a2 + a3 @app.route('/') def hello_world(): return render_template('hello.html') if __name__ == '__main__': app.run()
from flask import Flask app = Flask(__name__) @app.route('/index') def index(): print('index') return "Index" class Middleware(object): def __init__(self,old): self.old = old def __call__(self, *args, **kwargs): print('請求前') ret = self.old(*args, **kwargs) print('請求後') return ret # return self.wsgi_app(environ, start_response) if __name__ == '__main__': app.wsgi_app = Middleware(app.wsgi_app) app.run() app.__call__ #當請求進來 第三個參數自動的幫你加括號;run_simple(host, port, self, **options) # 對象() 執行 ---> 對象.__call__方法 (類裏面的__call__);
總結:
視圖函數中使用:request/session/g/current_app
注意:請求上下文和應用上下文須要先放入Local中,才能獲取到。
from flask import Flask,current_app,request,session,g app = Flask(__name__) # 錯誤 # print(current_app.config) @app.route('/index') def index(): # 正確 #在視圖函數中才把ctx,app_ctx放入對應的local中,才能取到。 print(current_app.config) return "Index" if __name__ == '__main__': app.run()
爲何要把上下文管理分紅:
- 請求上下文: request_ctx (request/session)
- 應用上下文: app_ctx(app/g)
主要是由於在寫離線腳本的時候,沒有請求,所以沒有請求上下文,只有應用上下文。全部爲了方便,要拆開。
正常狀況下,stack的值的棧只有一個ctx
__restage__ = {
123:{"stack":[ctx]}
}
可是在多app離線腳本的時候,stack的值的棧 可能有多個app1_ctx,app2_ctx。
離線腳本:
#要應用到app上文管理 from crm import db,create_app app = create_app() app_ctx = app.app_context() with app_ctx: # with 對象 的時候就會自動的觸發類的__enter__ 方法,而後執行下面的代碼,最後執行__exit__ #__enter__是將app_ctx經過 LocalStack放入Local中, db.create_all() #會調用LocalStack 從Local中獲取app,而後再從app中獲取配置。 #__exit__ 是將當前的app_ctx對象從Local中移除掉
多app離線腳本;棧有多個app_ctx
from crm import db,create_app app = create_app() app_ctx = app.app_context() app_ctx2 = app.app_context() with app_ctx: # with 對象 的時候就會自動的觸發類的__enter__ 方法,而後執行下面的代碼,最後執行__exit__ with app_ctx2: #__enter__是將app_ctx經過 LocalStack放入Local中, #裏面就是先用的app_ctx2。由於是同時把app_ctx2和app_ctx放入棧中,後進先出。 db.create_all() #會調用LocalStack 從Local中獲取app,而後再從app中獲取配置。 #__exit__ 是將當前的app_ctx對象從Local中移除掉