乾貨來啦。Flask框架看這一篇就夠了,關注不迷路,Jeff帶你看源碼。開發技術時時更新

1、初識Flask

1.1 什麼是flask?

img

Flask 本是做者 Armin Ronacher在2010年4月1日的一個愚人節玩笑 ,不事後來大受歡迎,進而成爲一個正式的python編寫的web框架python

Flask是一個Python編寫的Web 微框架,讓咱們可使用Python語言快速實現一個網站或Web服務,在介紹Flask以前首先來聊下它和Django的聯繫以及區別,django個大而全的web框架,它內置許多模塊,flask是一個小而精的輕量級框架,Django功能大而全,Flask只包含基本的配置, Django的一站式解決的思路,能讓開發者不用在開發以前就在選擇應用的基礎設施上花費大量時間。Django有模板,表單,路由,基本的數據庫管理等等內建功能。與之相反,Flask只是一個內核,默認依賴於2個外部庫: Jinja2 模板引擎和 WSGI工具集--Werkzeug , flask的使用特色是基本全部的工具使用都依賴於導入的形式去擴展,flask只保留了web開發的核心功能。web

img

WSGI(web服務器網關接口)是python中用來規定web服務器如何與python Web服務器如何與Python Web程序進行溝通的標準,本質上就是一個socket服務端。而 Werkzeug模塊 就是WSGI一個具體的實現正則表達式

關鍵詞:一個Python編寫微web框架 一個核心兩個庫( Jinja2 模板引擎 和 WSGI工具集)算法

1.2 爲何要有flask?

flask性能上基本知足通常web開發的需求, 而且靈活性以及可擴展性上要優於其餘web框架, 對各類數據庫的契合度都很是高數據庫

關鍵詞:1. 性能基本知足需求 2 .靈活性可拓展性強 3. 對各類數據庫的契合度都比較高。django

4.在真實的生產環境下,小項目開發快,大項目設計靈活json

2、Flask快速啓動

'''
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()

3、Flask四劍客

'''
響應字符串
響應html頁面
跳轉頁面
返回json字符串
'''

from flask import Flask, render_template, redirect, jsonify

app = Flask(__name__)


@app.route("/index")
def index():
    # 1.返回字符串
    # return "hello word  啊啊啊"

    # 2.返回html頁面
    # 返回html,從flask裏面導入render_template
    # return render_template("index.html")

    # 3.跳轉路由
    # return redirect('/login')

    # 4.返回數據轉json返回,從flask中導入jsonify
    # data = {"name": "jeff", "age": "18"}
    # return jsonify(data)
    pass


@app.route("/login")
def login():
    return "我是login頁面"


if __name__ == '__main__':
    app.run()

3、flask的配置文件

'''
四種配置flask方法配置
1.直接給app對象賦值屬性
2.以字典的形式,給flask配置文件作配置
3.以文件的形式給flask作配置(django就是這種)
4.以類的形式,給flask作配置(推薦使用)
'''


from flask import Flask

app = Flask(__name__)

# 方式1(不推薦),由於只能配置兩項。debug和secret_key
# app.debug = True  # 默認false,自動重啓

# 方式2 字典的形式
# app.config["DEBUG"] = True


# 方式3 以文件的形式,在from_pyfile裏傳遞路徑
# app.config.from_pyfile("settings.py")


# 方式4 以類的形式,一個文件多個套配置,減小測試與更改(推薦使用)
# app.config.from_object("setobj.settings")

@app.route("/")
def index():
    return "json 是炮王"


if __name__ == '__main__':
    app.run()

方式三的配置文件:flask

相似Django同樣,一個專門的配置文件後端

DEBUG = True

方式四的配置類:

優勢:一個文件,多套配置。不一樣的類不一樣的配置,減小了測試與上線的更改的配置項

class settings():
    DEBUG = True

能夠配置的屬性

flask中的配置文件是一個flask.config.Config對象(繼承字典),默認配置爲:
'''
 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,
        }
    )
'''

4、flask路由

4.1 源碼分析

# 源碼分析:
'''
self.add_url_rule(rule, endpoint, f, **options)


def add_url_rule(
        self,  # app對象
        rule,  # url路由
        endpoint=None,  # 路由別名
        view_func=None,  # 響應的函數名
        provide_automatic_options=None,
        **options
    ):

methods :['POST','GET'] —》控制請求方法,不傳默認只能GET方法
'''

# @app.route的本質就在執行add_url_rule
        # rule是路由,endpoint是路由別名,view_func是響應函數
        # 若是endpoint不傳就是響應的函數名

4.2 使用1:起別名

from flask import Flask , url_for, redirect

app = Flask(__name__)


def index():
    print()
    return "我是index"

# @app.route('/detail/<int:nid>',methods=['GET','POST'],endpoint='detail')  # 典型寫法
@app.route('/login')
def login():
    print(url_for("index1"))   # 走的別名,若是別名沒有寫,默認函數名
    return redirect(url_for("index1")) # 走的別名,若是別名沒有寫,默認函數名

# url_for:
    # 用endpoint獲取路由要用url_for 在flask中導入,也就是反向解析

# 路由
app.add_url_rule('/', endpoint='index1', view_func=index)

# endpoint 別名
# view_func 響應的函數名


if __name__ == '__main__':
    app.run()

4.3 使用2:有名分組

from flask import Flask , url_for, redirect

app = Flask(__name__)

# 必須接收分組名字同樣的  nid
def index(nid):
    print(nid)
    return "我是index"

# 路由:有名分組
app.add_url_rule('/index/<string:nid>', endpoint='index1', view_func=index, methods=['POST','GET'])

app.add_url_rule('/index/<int:nid>', endpoint='index', view_func=index, methods=['POST','GET'])

# 瀏覽器:http://127.0.0.1:5000/index/asdfas
# string、int 規定接收的類型


if __name__ == '__main__':
    app.run()

4.4 路由小總結

總結:
 1 @app.route("/login") 的本質app.add_url_rule("/login",view_func=login),因此咱們就能夠用這兩個方式來添加路由
 2 路由的參數,
    2.1 endpoint,作是反向解析,若是上面添加路由的時候,沒有傳遞endpoint就是使用響應函數的函數名,反向解析用url_for(),作解析,這個url_for必須在flask裏面導入
    2.2 methods=["POST","GET"],該參數控制路由容許哪些請求方法訪問,若是不傳,默認只能GET方法
    2.3 路由以及路由路由轉化器。"/index/<int:nid>",<參數的類型:用哪一個變量來接收>,響應函數中的形參的名字必須轉化器中一致。

4.5 默認轉化器

DEFAULT_CONVERTERS = {
    'default':          UnicodeConverter,
    'string':           UnicodeConverter,
    'any':              AnyConverter,
    'path':             PathConverter,
    'int':              IntegerConverter,
    'float':            FloatConverter,
    'uuid':             UUIDConverter,
}

4.6 自定義轉換器

#1 寫類,繼承BaseConverter
#2 註冊:app.url_map.converters['regex'] = RegexConverter
# 3 使用:@app.route('/index/<regex("\d+"):nid>')  正則表達式會看成第二個參數傳遞到類中
from flask import Flask, views, url_for
from werkzeug.routing import BaseConverter

app = Flask(import_name=__name__)
# 1.寫類
class RegexConverter(BaseConverter):
    """
    自定義URL匹配正則表達式
    """
    def __init__(self, map, regex):
        super(RegexConverter, self).__init__(map)
        self.regex = regex

    def to_python(self, value):
        """
        路由匹配時,匹配成功後傳遞給視圖函數中參數的值
        """
        return int(value)

    def to_url(self, value):
        """
        使用url_for反向生成URL時,傳遞的參數通過該方法處理,返回的值用於生成URL中的參數
        """
        val = super(RegexConverter, self).to_url(value)
        return val
# 2.註冊:添加到flask中
app.url_map.converters['regex'] = RegexConverter
# 正則匹配處理結果,要交給to_python,to_python函數能夠對匹配處理結果作處理
# 3.使用:
@app.route('/index/<regex("\d+"):nid>')
def index(nid):
    print(url_for('index', nid='888'))
    return 'Index'

if __name__ == '__main__':
    app.run()

4.7 自定義轉換器總結

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,進行處理。處理之後,在拼接到路由。

5、flask模板渲染

py文件:

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()

1.變量的循環

<!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() %}
   <tr>
       <td>{{ k }}</td>
       <td>{{ v.name }}</td>
       <td>{{ v['name'] }}</td>
       <td>{{ v.get('name') }}</td>
       <td>{{url_for("index")}}</td>
   </tr>
{% endfor %}
</body>
</html>

2.邏輯判斷

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>用戶列表</h1>
    <table>
        {% if name %}
          <h1>Hello {{ name }}!</h1>
        {% else %}
          <h1>Hello World!</h1>
        {% endif %}
    </table>
</body>
</html>

3.執行函數,傳參數

比django中多能夠加括號,執行函數,傳參數

from flask import Flask,render_template,Markup
app = Flask(__name__)
app.debug = True



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",ht1 = func1,ht="<h1>餅哥正帥</h1>")



if __name__ == '__main__':
    app.run()

html文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

{{ ht|safe}}
{{ht1("jaosn","tank")}}  // 傳參數
</body>
</html>

6、flask的請求與響應

from flask import Flask, request, make_response

app = Flask(__name__)
app.debug = True


@app.route('/', methods=['POST', 'GET'])
def index():
    # print('請求方法',request.method)  # 請求方法
    # print('get請求的參數',request.args)  # get 請求的參數
    # print('post請求的參數',request.form)  # post請求的參數
    # print('請求的cookies',request.cookies)  # 請求的cookies
    # print('post與get的全部參數',request.values)  # post與get的全部參數

    # 響應頭,添加make_response
    response = make_response('ok')
    #response = make_response(render_template("login.html"))
    # 設置cookie
    response.set_cookie("key","val")
    return 'OK'


if __name__ == '__main__':
    app.run()

請求相關的信息

請求相關的信息
    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

7、設置cookies

from flask import Flask, make_response

app = Flask(__name__)
app.debug = True


@app.route('/', methods=['POST', 'GET'])
def index():
    # 響應頭,添加make_response
    response = make_response('ok')
    
    # 設置cookies
    response.set_cookie('key', 'val')  
    
    # 刪除cookies
    response.delete_cookie("key")
    
    # 設置響應頭
    response.headers["x-somexx"] = "A SB"
    
    return response


if __name__ == '__main__':
    app.run()

設置cookie的參數

key, 鍵
value='', 值
max_age=None, 超時時間 cookie須要延續的時間(以秒爲單位)若是參數是\ None`` ,這個cookie會延續到瀏覽器關閉爲止

expires=None, 超時時間(IE requires expires, so set it if hasn't been already.)
                   
path='/', Cookie生效的路徑,/ 表示根路徑,特殊的:根路徑的cookie能夠被任何url的頁面訪問,瀏覽器只會把cookie回傳給帶有該路徑的頁面,這樣能夠避免將cookie傳給站點中的其餘的應用。
                   
domain=None, Cookie生效的域名 你可用這個參數來構造一個跨站cookie。如, domain=".example.com"所構造的cookie對下面這些站點都是可讀的:www.example.com 、 www2.example.com 和an.other.sub.domain.example.com 。若是該參數設置爲 None ,cookie只能由設置它的站點讀取
                   
secure=False, 瀏覽器將經過HTTPS來回傳cookie
                   
httponly=False 只能http協議傳輸,沒法被JavaScript獲取(不是絕對,底層抓包能夠獲取到也能夠被覆蓋)

8、flask的session

cookie:存放在客戶端的鍵值對
session:存放在客戶端的鍵值對
token:存放在客戶端,經過算法來校驗

8.1 設置session(使用版)

在使用session以前必須如今設置一下密鑰

app.secret_key="asdas" # 值隨便
# app.config['SECRET_KEY'] = os.urandom(24) # 配置session使用的祕鑰
設置:session['username'] = 'xxx'
# 在django中發什麼三件事,1,生成一個隨機的字符串 2 往數據庫存 3 寫入cookie返回瀏覽器
# 在flask中他沒有數據庫,但session是怎樣實現的?
    # 生成一個密鑰寫入這個cookie,而後下次請求的時候,經過這個cookie解密,而後賦值給session
    # 咱們經過app.session_interface來查看
    
刪除:session.pop('username', None)

Flask提供了session對象用來將cookie加密儲存,session經過祕鑰對數據進行簽名以加密數據。

from flask import Flask, session
import os

app = Flask(__name__)
app.config['SECRET_KEY'] = os.urandom(24) # 配置session使用的祕鑰

@app.route('/')
def set_session_info():
    session['username'] = 'mark' # 使用用戶信息配置sesion信息做爲cookie,並添加到響應體中
    
    return '設置session信息'

session對象像能夠字典同樣操做,內部是把字典的信息進行加密操做而後添加到相應體中做爲cookie,響應的時候會自動返回給瀏覽器。

session['username'] = 'mark'
 session['userphone'] = '123456'  # 能夠指定多條session信息,統一放到響應的cookie中返回給瀏覽器

8.2 設置session(分析版)

from flask import Flask,session

app = Flask(__name__)
# 要用session,必須app配置一個密鑰
app.secret_key  =  "asdasdihasdiuh"

# 設置session的名字,默認配置文件中爲:session
app.config['SESSION_COOKIE_NAME']="python13session" 

# app.session_interface

#app.session_interface實現了兩個方法,一個叫save_session,一個open_session,
# save_session 存session執行,加密設置cookies
# open_session 取session執行,解密大字典,拿到session
@app.route("/",)
def index():
    #如何設置sessoion
    # 1 導入session
    # 2 給sessoion設置值
    session['name'] = "egon"
    return "ok"

@app.route("/login")
def login():
    print(session["name"])
    return "login"

if __name__ == '__main__':
    app.run()

8.3 設置session有效期

後端Flask跟瀏覽器交互默認狀況下,session cookie會在用戶關閉瀏覽器時清除。經過將session.permanent屬性設爲True能夠將session的有效期延長爲31天,也能夠經過操做app的配置PERMANENT_SESSION_LIFETIME來設置session過時時間。

案例 3.3.2.1:開啓指定session過時時間模式

from flask import Flask, session
import os

app = Flask(__name__)
app.config['SECRET_KEY'] = os.urandom(24)


@app.route('/')
def set_session_info():
    session['username'] = 'mark'
    session['userphone'] = '123456'
    session.permanent = True # 開啓設置有效期,默認爲31天后過時
    return 'Hello World!'
...

設置自定義過時時間

# 經過設置PERMANENT_SESSION_LIFETIME指定具體的過時時間

from datetime import timedelta
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(hours=1) # 設置爲1小時候過時

8.4 獲取sessoin

在Flask中獲取設置的session信息經過session對象獲取,session對象是繼承了字典類,因此獲取的時候是字典的取值方式。其內部會把瀏覽器傳過來的session信息解密。

@app.route('/get_session/')
def get_session():
    username = session.get('username')
    userphone = session.get('userphone')
    if username or userphone:
        return "{},{}".format(username, userphone)
    return "session爲空"

1550930691065

8.5 刪除session

session對象調用pop()能夠根據具體的session的key清除掉指定的session信息。

session對象調用clear()能夠清除這次請求的瀏覽器關於本域名的全部session信息

@app.route('/del_session/')
def del_session():
    session.pop('username')
    # session.clear()
    return '刪除成功'

8.6 源碼分析

session源碼的執行流程

-save_seesion
    -響應的時候,把session中的值加密序列化放大到了cookie中,返回到瀏覽器中
-open_session
    -請求來了,從cookie中取出值,反解,生成session對象,之後再視圖函數中直接用sessoin就能夠了。

源碼分析

# app.session_interface  點進去

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,
        )

9、閃現

9.1 什麼是閃現?

1554289488442

-設置:flash('aaa')
-取值:get_flashed_message()
-
-假設在a頁面操做出錯,跳轉到b頁面,在b頁面顯示a頁面的錯誤信息
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()
相關文章
相關標籤/搜索