Flask

Flask介紹

Flask是一個基於python依賴jinjia2模板和Werkzeug WSGI 服務的一個微型框架.  werkzeug和Django中的wsgiref模塊同樣其本質就是一個socket服務端,,其用於接收http請求並對請求進行預處理,而後觸發Flask框架,開發人員基於Flask框架提供的功能對請求進行相應的處理,並返回給用戶,若是要返回給用戶複雜的內容時,須要藉助jinja2模板來實現對模板的處理,即:將模板和數據進行渲染,將渲染後的字符串返回給用戶瀏覽器。css

Werkzeug 並非 一個框架,它是一個 WSGI 工具集的庫,你能夠經過它來建立你本身的框架或 Web 應用html

Flask 的特色: 短小精悍,易於擴展第三方組件豐富.前端

Flask是一個微框架,python

微」(micro) 並不表示你須要把整個 Web 應用塞進單個 Python 文件(雖然確實能夠 ),也不意味着 Flask 在功能上有所欠缺。微框架中的「微」意味着 Flask 旨在保持核心簡單而易於擴展。Flask 不會替你作出太多決策——好比使用何種數據庫。而那些 Flask 所選擇的——好比使用何種模板引擎——則很容易替換。除此以外的一切都由可由你掌握。如此,Flask 能夠與您珠聯璧合。mysql

默認狀況下,Flask 不包含數據庫抽象層、表單驗證,或是其它任何已有多種庫能夠勝任的功能。然而,Flask 支持用擴展來給應用添加這些功能,如同是 Flask 自己實現的同樣。衆多的擴展提供了數據庫集成、表單驗證、上傳處理、各類各樣的開放認證技術等功能。Flask 也許是「微小」的,但它已準備好在需求繁雜的生產環境中投入使用。jquery

flask和djano的區別?web

djano大而全,無socket,藉助第三方模塊wsgiref模塊,內部提供: ORM admin 中間件,Form,ModelForm,session,緩存,信號CSRF
Flask ,短小精悍,易於擴展,無socket,藉助第三方庫Werkzeug.第三方組件豐富

flask官網https://flask.palletsprojects.com/en/1.1.x/ 1.1版本正則表達式

Flask的簡單應用sql

#一個基於werkzeug庫的Wsgi應用應該是這樣的
from werkzeug.wrappers import Request,Response
from werkzeug.serving import run_simple

@Request.application
def hello(request):
    return Response('Hello,werkzeug')

if __name__ == '__main__':
    run_simple('localhost',4000,hello)

 Flask就是基於werkzeug來開發應用程序的數據庫

#簡單的Flask應用 hello
from flask import Flask#導入Flask類
app=Flask(__name__)#實例化一個對象,這個對象就是咱們的W基於WSGI的應用程序,括號裏的參數是給對象起的名字,隨便一個字符串就能夠,__name__表示的是模塊名字的字符串
@app.route('/index') #router函數把url和視圖函數捆綁起來
def index():
    return 'Hello Flask'

if __name__ == '__main__':
    print(type(__name__))
    app.run(debug=True)#至關於run_simple('localhost',5000,index),

啓動方式

方法一:app.run(),或者在命令行中運行該項目的啓動文件python xxx.py

方法二,在命令行中,啓動輸入flask run 就啓動了.Flask經過依賴包Click內置了一個命令行交互系統,當咱們按照flask後,會自動添加一個flask命令腳本,咱們能夠經過執行flask命令來執行該腳本.注意這個啓動文件必須是app.py才能夠用

 

調式模式

雖然 run() 方法適用於啓動本地的開發服務器,可是你每次修改代碼後都要手動重啓它。這樣並不夠優雅,注意不能用在生產環境中.緣由爲:

#在服務上開啓調試模式會有很大的安全隱患,攻擊裝會經過你的調試信息來獲取你的數據庫結構.另外一方面調試頁面顯示的錯誤內容會讓咱們用戶很反感

在開發模式下flask會自動激活Wergzurg內置的調試器和重載器.

 

1.是debug=True使用了flask自帶的服務器,性能差,

2.這樣會把咱們的錯誤信息打印到前端,因此在生產環境中要刪除掉debug=True

有兩種方法來改變這個弊端

方法一: 直接在應用對象上設置

if __name__ == '__main__':
    app.debug=True
    app.run()#至關於run_simple('localhost',5000,index),

方法二: 在run函數中設置

app.run(debug=True)

配置文件

 flask中的配置文件是一個flask.config對象(繼承字典)

 flask中的配置文件是一個flask.config.Config對象(繼承字典),默認配置爲:
 2     {
 3         'DEBUG':                                get_debug_flag(default=False),  是否開啓Debug模式
 4         'TESTING':                              False,                          是否開啓測試模式
 5         'PROPAGATE_EXCEPTIONS':                 None,                          
 6         'PRESERVE_CONTEXT_ON_EXCEPTION':        None,
 7         'SECRET_KEY':                           None,
 8         'PERMANENT_SESSION_LIFETIME':           timedelta(days=31),
 9         'USE_X_SENDFILE':                       False,
10         'LOGGER_NAME':                          None,
11         'LOGGER_HANDLER_POLICY':               'always',
12         'SERVER_NAME':                          None,
13         'APPLICATION_ROOT':                     None,
14         'SESSION_COOKIE_NAME':                  'session',
15         'SESSION_COOKIE_DOMAIN':                None,
16         'SESSION_COOKIE_PATH':                  None,
17         'SESSION_COOKIE_HTTPONLY':              True,
18         'SESSION_COOKIE_SECURE':                False,
19         'SESSION_REFRESH_EACH_REQUEST':         True,
20         'MAX_CONTENT_LENGTH':                   None,
21         'SEND_FILE_MAX_AGE_DEFAULT':            timedelta(hours=12),
22         'TRAP_BAD_REQUEST_ERRORS':              False,
23         'TRAP_HTTP_EXCEPTIONS':                 False,
24         'EXPLAIN_TEMPLATE_LOADING':             False,
25         'PREFERRED_URL_SCHEME':                 'http',
26         'JSON_AS_ASCII':                        True,
27         'JSON_SORT_KEYS':                       True,
28         'JSONIFY_PRETTYPRINT_REGULAR':          True,
29         'JSONIFY_MIMETYPE':                     'application/json',
30         'TEMPLATES_AUTO_RELOAD':                None,
31     }
View Code

若是咱們要更改配置文件的話用這種方法

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

class TestingConfig(Config):
    """
    測試環境
    """
    TESTING = True

#PS: 從sys.path中已經存在路徑開始寫


#PS: settings.py文件默認路徑要放在程序root_path目錄,若是instance_relative_config爲True,則就是instance_path目錄
seetings.py

導入配置文件使用app.config.from_object(),注意這裏使用的是模塊的導入而不是路徑的導入

flask_demo.py

from flask import Flask
app=Flask(__name__)
app.config.from_object('seetings.DevelopmentConfig') #直接導入配置文件中的類就能夠了
@app.route('/index')
def index():
    return 'Hello Flask'

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

flask_demo

使用配置文件中的數據

咱們知道flask中的app.comfig是一個字典對象,注意配置文件中的變量名要大寫,小寫不加載。源碼中規定的

因此配置文件有這麼幾種狀況

狀況一:配置文件爲這樣

#config.py
DEBUG = TRUE
from flask import Flask

app = Flask(__name__)
app.config.from_object('config')  # 這樣導入


def hello():
    return 'hello'


app.add_url_rule('/hello', view_func=hello)

if __name__ == '__main__':
    app.run(debug=app.config['DEBUG'])

狀況二:配置文件爲這樣

# config.py
class Development(object):
    DEBUG = True


CONFIG_DICT = {
    'development': Development
}
from flask import Flask

app = Flask(__name__)
app.config.from_object('config')  # 這樣導入


def hello():
    return 'hello'


app.add_url_rule('/hello', view_func=hello)

if __name__ == '__main__':
    app.run(debug=app.config["CONFIG_DICT"]["development"].DEBUG)

還能夠這樣導入,這個是從同事那裏偷學來的。這種形式我感受比較好

from flask import Flask
from config import CONFIG_DICT

app = Flask(__name__)
app.config.from_object(CONFIG_DICT["development"])  # 這樣導入就能夠直接用你類中定義的變量了


def hello():
    return 'hello'


app.add_url_rule('/hello', view_func=hello)

if __name__ == '__main__':
    # print(app.config["DEBUG"])
    app.run(debug=app.config["DEBUG"])

 

路由

route() 裝飾器把一個函數綁定到對應的 URL 上。

靜態路由

添加路由的兩種方式:

# 路由方式一(*):裝飾器
            @app.route('/index',methods=['GET','POST']) #當有post提交時
            def index():
                return "Index"
#路由方式二 路由方式一種的裝飾器也是用的這種方法
    def order():
    return 'Order'
app.add_url_rule('/order',view_func=order)#view_func綁定那個函數

動態路由

咱們不只能夠爲視圖函數綁定多個url,還能夠在URL規則中添加變量部分,使用"<變量名>"的形式表示.Flask會把變量傳入到視圖函數中.

格式爲: <轉換器的類型:參數名稱>  轉換器(其實就是要求參數的格式類型)。

from flask import Flask
app=Flask(__name__)
@app.route('/index/<int:id>/') #參數id要求爲int類型
def index(id):#別忘記傳參
    return 'Hello Flask%s'%(id)

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

當url規則中包含變量時,若是用戶訪問的URL沒有添加變量,好比/index,那麼服務器會報一個404錯誤,一般狀況下咱們會在app.router裝飾器中使用defaults參數設置url變量的默認值,這個參數做爲字典來輸入.

from flask import Flask
app=Flask(__name__)

@app.route("/index,defaults={'id':1}
@app.route('/index/<int:id>/') 
def index(id):
    return 'Hello Flask%s'%(id)

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

 

 

轉換器有下面幾種:

@app.route('/user/<username>')   #經常使用的   不加參數的時候默認是字符串形式的
@app.route('/post/<int:post_id>')  #經常使用的   #指定int,說明是整型的
@app.route('/post/<float:post_id>') #浮點
                                                                       @app.route(
'/post/<path:path>') #路徑,能夠拼接路徑 @app.route('/login', methods=['GET', 'POST'])

經常使用路由系統有以上五種,全部的路由系統都是基於一下對應關係來處理:

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

 

router中的url一個/和兩個/的區別?

惟一 URL / 重定向行爲
Flask 的 URL 規則基於 Werkzeug 的路由模塊。這個模塊背後的思想是基於 Apache 以及更早的 HTTP 服務器主張的先例,保證優雅且惟一的 URL。

以這兩個規則爲例:

@app.route('/projects/')
def projects():
    return 'The project page'

@app.route('/about')
def about():
    return 'The about page'
雖然它們看起來着實類似,但它們結尾斜線的使用在 URL 定義 中不一樣。 第一種狀況中,指向 projects 的規範 URL 尾端有一個斜線。這種感受很像在文件系統中的文件夾。
訪問一個結尾不帶斜線的 URL 會被 Flask 重定向到帶斜線的規範 URL 去。 然而,第二種狀況的 URL 結尾不帶斜線,相似 UNIX
-like 系統下的文件的路徑名。訪問結尾帶斜線的 URL 會產生一個 404 「Not Found」 錯誤。 這個行爲使得在遺忘尾斜線時,容許關聯的 URL 接任工做,與 Apache 和其它的服務器的行爲並沒有二異。此外,也保證了 URL 的惟一,有助於避免搜索引擎索引同一個頁面兩次。

 構造url(反向解析)

 web程序中的URL無處不在,若是程序中的url都是以硬編碼的方式(即完整的url)寫出,那麼將會大大的下降代碼的易用性,好比你修改了某一個路由規則,那麼在視圖中對應的url都要修改一遍,爲解決這個問題,url_for函數就出現了,你在視圖中直接用url_for(函數名),就能夠獲取真正的URL了.注意這裏得到是相對路徑只能在程序內部使用.

注意:endpoint不寫,別名默認視圖函數名字

from flask import Flask,url_for#反向解析須要導入url_for 至關於Django中的reverse
app=Flask(__name__)
@app.route('/aa/',endpoint='index') #endpoint至關於django中的name,主要是起別名的做用
def index():
    v=url_for('login')#反向解析
    print(v)#打印
    return '這是index頁面'

@app.route('/login/',endpoint='login')
def login():
    return '這是登陸頁面'


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

結果:

/login/

自定義正則轉換器

當工做中遇到url中傳遞正則表達式的狀況,咱們能夠自定義正則表達式轉換器

首先咱們要繼承BaseConverter這個正則類,咱們來看一下源碼

class BaseConverter(object):

    """Base class for all converters."""
    regex = '[^/]+'  #這裏就是正則表達式,若是咱們不寫就用它的
    weight = 100  #這裏沒看懂

    def __init__(self, map):  
        self.map = map

    def to_python(self, value): #這裏對用正則取到的數據是否進行修飾,默認沒有修飾
        return value

    def to_url(self, value): #這個方法就是咱們用url_for方法時,所調用的方法
        return url_quote(value, charset=self.map.charset)

 

咱們如今的項目需求是,用戶傳過來的參數,uid前綴返回給視圖函數

class RegexConverter(BaseConverter):
    def __init__(self, url_map, regex):
        super(RegexConverter, self).__init__(url_map)  # 繼承map
        self.regex = regex  # 輸入咱們本身的正則表達式,用來替代基類中的regex

    def to_python(self, value):
        return "uid" + value

    def to_url(self, value):
        "這裏沒動"
        val = super(RegexConverter, self).to_url(value)
        return val


app.url_map.converters['regs'] = RegexConverter #在app.route裝飾器中咱們定義正則時前綴要使用regs,不然它會匹配不到


@app.route('/index/<regs("\d+"):nid>', methods=["GET"])
def index(nid):
    print("nid的結果:", nid)
    return "這是index頁面"

咱們這樣在前端輸入

 

 後端的結果爲:

nid的結果爲: uid22

注意這裏有一個坑:咱們定義了正則前端輸入的參數就必須是咱們正則中要求的,不然會報錯,不能輸入127.0.0.1:3000/index/22aa  這樣是匹配不出來的

 

這是另外的一個例子

from flask import Flask ,render_template,redirect,url_for
from werkzeug.routing import BaseConverter #自定義正則表達式就要繼承BaseConverter這個類

app=Flask(__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['regs']=RegexConverter
@app.route(
'/index/<regs("\d+"):nid>',methods=['GET','POST']) #使用自定義的轉換器 def index(nid): print(nid,type(nid)) v=url_for("index",nid=555)#至關於/index/555 print(v) return "這是Index頁面" if __name__ == '__main__':

重定向

當須要重定向時.能夠用rediret_to參數

from flask import Flask,render_template,redirect
                app = Flask(__name__)

                @app.route('/index',methods=['GET','POST'],redirect_to='/new')
                def index():
                    return "老功能"

                @app.route('/new',methods=['GET','POST'])
                def new():
                    return '新功能'


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

 獲取子域名

首先咱們要知道:域名是由子域名和頂級域名構成的.

一級域名:又叫頂級域名,一串字符串中間一個點隔開,例如baidu.com,這裏說明一下,www.baidu.com不是一級域名!!而是二級域名!

二級域名:實際上就是一個一級域名如下的主機名,一串字符串中間兩個「.」隔開,例如pan.baidu.com("pan"就是主機名)。

三級域名:二級域名的子域名,特徵是包含三個「.」,通常來講三級域名都是免費的。

#這是頂級域名
https://baidu.com/
#這是頂級域名的子域名
https://www.baidu.com/
https://pan.baidu.com
from flask import Flask,url_for#反向解析須要導入url_for 至關於Django中的reverse
app=Flask(__name__)
@app.route('/aa/',subdomain="admin")
def index():
    v=url_for('login')#反向解析
    print(v)
    return '這是index頁面'

@app.router中的參數

 

@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'] = 'haiyan.com:5000'


                                                @app.route("/", subdomain="admin")
                                                def static_index():
                                                    """Flask supports static subdomains
                                                    This is available at static.your-domain.tld"""
                                                    return "admin.xxx.com"

                            #動態生成
                                                @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()
@app.route和app.add_url_rule參數

Flask中的FBV

其實Flask咱們用的最多的就是FBV,CBV用的不多,

這裏簡單介紹下CBV

from flask import Flask, views
from functools import wraps
import time

app = Flask(__name__)


def cal_time(func):
    """
    計算請求所用的時間
    :param func:
    :return:
    """
    wraps(func)

    def inner(*args, **kwargs):
        start_time = time.time()
        result = func()
        end_time = time.time()
        print(f'該函數執行了{end_time - start_time}s秒')
        return result

    return inner


class Login(views.MethodView):
    methods = ['POST', 'GET']
    # 若是須要在CBV中加裝飾器的話,括號裏就是裝飾器的名字,能夠傳多個,當有一個的時候別忘記了加逗號(,)
    decorators = (cal_time,)

    def get(self):
        time.sleep(1)
        print('get 請求')
        return 'login get'

    def post(self):
        time.sleep(2)
        print('post 請求')
        return 'login post'


app.add_url_rule('/login', view_func=Login.as_view(name='login'))  # name=endpoint

if __name__ == "__main__":
    app.run(debug=True, host='127.0.0.1', port=5001)

 

視圖中添加裝飾器

當咱們在Flask中的視圖中添加裝飾器時.咱們要添加的裝飾器和路由裝飾器會出現衝突,這時候應該辦

首先咱們要肯定的路由裝飾器必定要先放到咱們的裝飾器的上邊,由於它要先執行,才能再執行咱們的裝飾器,

當咱們有多個視圖函數須要裝飾時候,好比這樣

from flask import Flask#導入Flask類
app=Flask(__name__)
def wrapper(f):
    def inner(*args,**kwargs):
        print('before')
        return f()
    return inner
@app.route('/index1') 
@wrapper
def index():
    return 'Hello Flask'

@app.route('/login') 
@wrapper
def login():
    return 'Hello Flask'
if __name__ == '__main__':
    print(type(__name__))
    app.run(debug=True)

 

這時候就會出現錯誤

ssertionError: View function mapping is overwriting an existing endpoint function: inner

由於 endpoint默認反向生成函數名,然而,被裝飾的函數的真正的函數名都是inner,因此endpoint這時候就出錯了,這時候咱們就須要真正的函數名,因而就用到了wraps裝飾器修復

from flask import Flask
from functools import wraps  #導入wraps
app=Flask(__name__)
def wrapper(f):
    @wraps(f)
    def inner(*args,**kwargs):
        print('before')
        return f()
    return inner
@app.route('/index1')
@wrapper
def index():
    return 'Hello Flask'

@app.route('/login')
@wrapper
def login():
    return 'Hello Flask'
if __name__ == '__main__':
    print(type(__name__))
    app.run(debug=True)
加wraps後

HTTP方法

HTTP有許多訪問url的方法,在默認狀況下,只支持GET方法,可是,能夠定義router中的參數來實現多種HTTP方法

好比當時post請求的時候

@app.route('/login',methods=['GET','POST']) #這裏的括號裏就要有method參數了

請求對象(request)和響應對象

首先咱們應該知道,djano中的請求數據是經過參數request傳遞給視圖函數的,可是在Flask不是經過參數傳遞進來的,而是經過上下文管理

首先從 flask 模塊裏導入它:

from flask import request

當前請求的 HTTP 方法可經過 method 屬性來訪問。經過:attr:~flask.request.form 屬性來訪問表單數據( POST 或 PUT 請求提交的數據)。

當訪問form表單數據(POST提交)時

request.form.get()

注意:不存在時,獲得的值爲None.

當訪問url參數(get提交時)

request.args.get()

注意:不存在時,獲得的值爲None.

request方法 

請求相關信息
        # request.method    請求方法
        # request.args      當get請求,這裏獲得的是不可變的字典,也就是不可改變該字典裏的數據,若是要變爲可變字典,能夠用to_dict()
        # request.form      當post請求時
        # request.get_json() #當contentType只能是json時,還能夠經過設置參數force=True 將忽視contentType
        # 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
     # request.remote_add 獲取用戶ip
# obj = request.files['the_file_name'] # obj.save('/var/www/uploads/' + secure_filename(f.filename))

重點來介紹一下

1.request.values() 

此方法能夠獲取get請求和post請求的全部的參數

print(request.values)  
# CombinedMultiDict([ImmutableMultiDict([('id', '1'), ('age', '20')]), ImmutableMultiDict([('user', 'Oldboy'), ('pwd', 'DragonFire')])])
print(request.values.get("id"))  # 1
print(request.values["user"])  # Oldboy
# 這回喜歡直接操做字典的小夥伴們有驚喜了! to_dict() 方法能夠直接將咱們的參數所有轉爲字典形式
print(request.values.to_dict()) # {'user': 'Oldboy', 'pwd': 'DragonFire', 'id': '1', 'age': '20'}

# 注意這裏的坑來啦! 坑來啦!
# 若是url和form中的Key重名的話,form中的同名的key中value會被url中的value覆蓋
# http://127.0.0.1:5000/req?id=1&user=20
print(request.values.to_dict())  # {'user': 20 'pwd': 'DragonFire', 'id': '1'}

 

 

 response相關的方法

 # 響應相關信息 return "字符串" #返回字符串 return render_template('html模板路徑',**{}) #渲染模板 return redirect('/index.html') #重定向 return json.dumps() #返回json數據,和他同樣的就是jsonify()
    

設置響應頭和cookie

  #####如何設置響應頭和cookie*******
        response = make_response(render_template('index.html') ) #生成對象
        #response是flask.wrappers.Response類型
        response.set_cookie('key', 'value')  #設置cookie
        response.delete_cookie('key')   #刪除cookie
 response.headers['X-Something'] = 'A value'  #設置響應頭
        return response   #返回respon對象

補充

from urllib.parse import urlencode,quote,unquote

val = "%E6%8A%8A%E5%87%A0%E4%B8%AA"
print(unquote(val))   #ba上面這樣的數據轉換成中文
vall='我是中國人'
print(quote(vall))
vdd={"name":'小紅'}
print(urlencode(vdd))

模板引擎

大多數和djano的模板渲染差很少

首先要創建一個名爲templates的文件在根目錄下,文件夾中放html

from flask import Flask,render_template
app=Flask(__name__)
@app.route('/xxx') 
def xxx():
    return render_template('/index.html',msg='fdsfd')

if __name__ == '__main__':
    print(type(__name__))
    app.run(debug=True)#至關於run_simple('localhost',5000,index),

一、爲了防止xss攻擊,加了驗證,因此頁面上顯示字符串的形式,解決辦法,有兩種方式

  - 方法一在後端Markup

 v5 = Markup("<input type='text' />")

  -方法二 在前端

  {{ v4|safe }}

二、自定義方法

複製代碼
def test(a,b):
    return a+b

@app.route('/index')
def index():
    return render_template("index2.html",test=test)


index2.html
<h1>{{ test(1,2) }}</h1>
複製代碼

三、寫一個函數在全部的頁面都使用

template_global和template_filter

複製代碼
@app.template_global()
def sb(a1, a2):
    return a1 + a2


@app.template_filter()
def db(a1, a2, a3):
    return a1 + a2 + a3
複製代碼
調用方式:{{sb(1,2)}}  {{ 1|db(2,3)}}

四、模板繼承:和django的同樣。extents

5.後端傳函數,前端使用模板語言的時候須要加括號然而djano不須要

def sb(a1, a2):
    content={
        'k1':lambda x:5
    }

    return render_template('index.html',**content)
#前端
{{k1()}}

 

模板其餘問題參考django 

session

它容許你在不一樣請求間存儲特定用戶的信息。

Flask中的session和djano中的session是不一樣的,Flask中的session放到cookie中,它是在 Cookies 的基礎上實現的,而且對 Cookies 進行密鑰簽名。這意味着用戶能夠查看你 Cookie 的內容,但卻不能修改它,除非用戶知道簽名的密鑰。

一個完整的範例

要求 登錄後把信息添加session中

flask.py

from flask import Flask,render_template,request,redirect,session
app=Flask(__name__)
app.secret_key='wefjldsaj' #先要進行密鑰簽名
USER_DICT={
    '1':{'name':'小明'},
    '2':{'name':'小紅'},
    '3':{'name':'36'}
}#模擬一個數據庫
@app.route('/login',methods=['GET','POST'])
def login():
    print('請求來了')
    if request.method=="POST":
        username=request.form.get('username')#從form表單中取數據
        password=request.form.get('password')
        #request.args #對應get從url中取數據
        if username=='alex' and password=="123":
            #登陸成功後把用戶信息放入session中
            session['user_info']=username
            return redirect('/index')
        else:
            return render_template('/login.html',msg="用戶名和密碼錯誤") #還能夠這樣傳值return render_template('/login.html',**{'msg':"用戶名和密碼錯誤"})
    return render_template('/login.html')
@app.route('/index')
def index():
    """
    主頁
    :return:
    """
    username=session.get('user_info')
    if not username:
        return redirect('/login')
    print(USER_DICT)
    return render_template('/index.html',user_dict=USER_DICT)
@app.route('/logout')
def logout():
    session.pop('user_info',None)
    return '已經退出'
if __name__ == '__main__':
    app.run(debug=True)

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">  <!--IE瀏覽器最高渲染-->
    <meta name="viewport" content="width=device-width, initial-scale=1"> <!--爲了確保適當的繪製和縮放-->
    <title>主頁</title>
    <link rel="stylesheet" href="../bootstrap-3.3.7-dist/css/bootstrap.min.css">
</head>
<body>
<ul>
    {% for k,v in user_dict.items() %}
    <li>{{v.name}}</li>
    {% endfor%}
</ul>
<script src="../jquery-3.2.1.min.js"></script>

</body>
</html>

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">  <!--IE瀏覽器最高渲染-->
    <meta name="viewport" content="width=device-width, initial-scale=1"> <!--爲了確保適當的繪製和縮放-->
    <title>用戶登陸</title>
    <link rel="stylesheet" href="../bootstrap-3.3.7-dist/css/bootstrap.min.css">
</head>
<body>
<h1>請登陸</h1>
<form method="post">
    用戶名:<input type="text" name="username">
    密碼: <input type="password" name="password">{{msg}}
    <input type="submit" value="提交">
</form>
<script src="../jquery-3.2.1.min.js"></script>

</body>
</html>

 

 

from flask import Flask,url_for,session

app = Flask(__name__)
app.secret_key = "sdsfdgdgdgd"
app.config['SESSION_COOKIE_NAME'] = 'session_lvning'  #設置session的名字

@app.route('/index/')
def index(nid):
    #session本質上操做的是字典, session是否還有其餘方法?與字典方法相同
    #session的原理:若是下一次訪問的時候帶着隨機字符串,會把session裏面對應的
    # 值拿到內存,假設session保存在數據庫,每執行一次連接一次數據庫,每次都要時時更新的話
    # 會很是損耗內存
    session["xxx"] = 123
    session["xxx2"] = 123
    session["xxx3"] = 123
    session["xxx4"] = 123
    del session["xxx2"]  #在這刪除了,真正存儲的時候是沒有xxx2的
    return "ddsf"

if __name__ == '__main__':

 

消息閃現

通常一個用戶操做完成後,咱們要告訴用戶操做的結果,用戶看完後這個消息就不必存在了,咱們就能夠刪除它.

Flask的消息閃現系統就是提供了這個功能.也就是反饋

flask()它能夠將要閃現的內容存儲起來.

get_flashed_messages():它能夠把儲存的內容取出來顯示,而後自動刪掉.

本質:閃現是基於session建立的,flash支持往裏邊放值,只要你取一下就沒有了,至關於pop了一下。不只把值取走,並且吧session裏的東西去掉,能夠用session直接寫

from flask import Flask,flash,get_flashed_messages
app = Flask(__name__)
app.secret_key = 'asdfasdfasdf'
@app.route('/x1',methods=['GET','POST'])
def login():
    flash('我要上向龍',category='x1') #把內容放入flash中,category就是一個別名 
    return '視圖函數x1'
@app.route('/x2',methods=['GET','POST'])
def index():
    data = get_flashed_messages(category_filter=['x1']) #取數據
    print(data)
    return "視圖函數x2"

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

先執行一次/x1

執行兩次/X2

結果

['我要上向龍']
[] #第二次就取不出值來了

用的場景: 閱後即焚

登陸場景:

flash.py

rom flask import render_template, request, session, url_for, redirect, flash
 
@app.route('/')
def index():
    if 'user' in session:
        return render_template('hello.html', name=session['user'])
    else:
        return redirect(url_for('login'), 302)
 
@app.route('/login', methods=['POST', 'GET'])
def login():
    if request.method == 'POST':
        session['user'] = request.form['user']
        flash('Login successfully!')
        return redirect(url_for('index'))
    else:
        return '''
        <form name="login" action="/login" method="post">
            Username: <input type="text" name="user" />
        </form>

login.html

!doctype html>
<title>Hello Sample</title>
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='style.css') }}">
{% with messages = get_flashed_messages() %}
  {% if messages %}
    <ul class="flash">
    {% for message in messages %}
      <li>{{ message }}</li>
    {% endfor %}
    </ul>
  {% endif %}
{% endwith %}
<div class="page">
    {% block body %}
    {% endblock %}
</div>

 全局裝飾器

@app.before_request和@app.after_request

app.before_request和app.after_request至關於djano中的中間件函數process_request和process_response,服務於全局,

不一樣點:當before_request有返回值時.它會直接執行全部的after_request,而不是像djano中執行對應的after_request函數

#after_request
若是沒有拋出錯誤,在每次請求後執行    
接受一個參數:視圖函數做出的響應
在此函數中能夠對響應值在返回以前作最後一步修改處理
須要將參數中的響應在此參數中進行返回

@app.after_request
    def after_request(response):
        return response
————————————————
版權聲明:本文爲CSDN博主「csdn-gdj」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連接及本聲明。
原文連接:https://blog.csdn.net/guodejie/article/details/80795770

 

項目中遇到的知識點: 在項目中咱們用after_request全局裝飾器,裝飾了一個跨域函數

from flask import Flask,session

app=Flask(__name__)
app.secret_key="dfsadsfds"
@app.route('/login')
def login():
    session['login_info']=123
    return '123'

@app.route('/index3')
def index():
    ret=session.pop('login_info')
    print('ret',ret)
    return '這是主頁'

@app.before_request
def before():
    print('brefore')

@app.after_request
def after(response): #注意這裏的參數和返回值
    print('after')
    return response

if __name__ == '__main__':
    app.run(debug=True)
    app.__call__
View Code

結果:

brefore
after

還有其餘的一些全局裝飾器

#!/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()
View Code

中間件

這個中間件是發生在request造成以前,也就是在執行app.wsgi_app()以前添加一些操做,經過對象的__call__方法

沒多大用處,通常咱們用@app.before_request

from flask import Flask
from flask import session, request
app = Flask(__name__)
app.secret_key = 'asdfasdfasdf'

@app.route('/x2',methods=['GET','POST'])
def index():
    print('我是index')
    return "我是x2"


class Middleware(object):
    def __init__(self,old_wsgi_app):
        """
        服務端啓動時,自動執行
        :param old_wsgi_app:
        """
        self.old_wsgi_app =old_wsgi_app

    def __call__(self, environ, start_response):
        """
        每次有用戶請求道來時
        :param args:
        :param kwargs:
        :return:
        """
        print('before')#添加在造成request以前的操做

        obj = self.old_wsgi_app(environ, start_response)
        print('after') #添加在造成request以後的操做
        return obj

if __name__ == '__main__':
    app.wsgi_app = Middleware(app.wsgi_app)
    app.run()
    """
    執行app.run()時候
    1.先執行app.__call__,
    2.再調用app.wsgi_app(),由於我本身有wsgi_app屬性,加括號執行個人__call__方法,而後就把老的app.wsgi_app傳進來,這樣咱們就能夠在老的app.wsgi_app執行前,和執行後作一些操做
"""
middleware

結果:

before
我是index
after

 藍圖blueprint

詳情見flask組件中的數據庫鏈接池

藍圖是什麼?

第一個功能:若是代碼不少要進行歸類,blueprint就是對flask的目錄結構進行劃分(應用於小中型程序),使目錄結構更清晰。

#這個怎麼理解呢?
好比咱們有兩個頁面一個是金融管理,一個會員管理,一個是美食管理。
每一個頁面都有好多的函數,這些函數若是放在一塊兒的話,會有不少代碼,顯得特別的亂。若是咱們能分類這些代碼,把不一樣的代碼放到不一樣py文件中就行了。blueprint就是爲了解決這個問題的。
在每一個py中實例化一個藍圖對象,一共生成三個藍圖對象就能夠了。而後再把這些對象註冊到app中,這樣就方便管理了。

第二個功能:當一個py文件中有多個函數時,爲了使url容易區分能夠給url添加前綴.

第三個功能: 咱們知道@app.before_rerequest對全局生效,然而藍圖可讓它只對某一個藍圖實例生效

咱們爲何要用藍圖?

咱們的應用常常會區分用戶站點和管理員後臺 二者雖然都在同一個應用中,可是風格迥異。把它們分紅兩個應用吧,總有些代碼咱們想重用;放在一塊兒嘛,耦合度過高,代碼不便於管理。因此Flask提供了藍圖(Blueprint)功能。
藍圖使用起來就像應用當中的子應用同樣,能夠有本身的模板,靜態目錄,有本身的視圖函數和URL規則,藍圖之間互相不影響。可是它們又屬於應用中,能夠共享應用的配置。
對於大型應用來講,
咱們能夠經過添加藍圖來擴展應用功能,而不至於影響原來的程序。不過有一點要注意,目前Flask藍圖的註冊是靜態的,不支持可插拔。 轉載於http:
//www.bjhee.com/flask-ad6.html

例子

首先這樣建立目錄

首先,manage.py文件是咱們的啓動文件

from blue_flask import app

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

admin.py文件中建立一個藍圖實例

from flask import Blueprint

ad=Blueprint('ad',__name__,url_prefix='/admin') #實例化一個藍圖對象,給url添加前綴

@ad.route('/ad1')
def ad1():
    return '這是ad1頁面'
@ad.route('/ad2')
def ad2():
    return '這是ad2頁面'

acount.py文件中咱們再建立一個藍圖實例,並添加一個before_request裝飾器

from flask import Blueprint

act=Blueprint('act',__name__) #實例化一個藍圖對象,裏邊的名字隨便起,可是不要和url相同

@act.route('/acount')
def login():
    return '這是acount'
@act.before_request
def f ():
    print('我只對act藍圖實例有效')

__init__.py文件是咱們的初始化文件,在這裏咱們導入藍圖,並註冊藍圖

from flask import Flask
#建立一個app對象
app=Flask(__name__)

from blue_flask.views.admin import ad
from blue_flask.views.acount import act

#給新藍圖註冊
app.register_blueprint(ad)
app.register_blueprint(act)

這樣咱們就可能夠執行manage.py文件了,

在url中輸入/acount,咱們能夠在前端看到"這是account",在後臺打印出"我只對act藍圖實例有效"

當咱們在url這樣輸入/admin/ad1,在前端會顯示這是ad1頁面,在後臺不會打印出f裝飾器的內容

相關文章
相關標籤/搜索