[Python自學] Flask框架 (1)

oldboy:s9day114html

參考博客:https://www.cnblogs.com/wupeiqi/articles/7552008.htmlpython

1、Flask簡介

1.安裝Flask

pip install flask

Flask:django

  - 短小精悍、可擴展性強的一個Web框架。編程

  - 依賴wsgi:werkzurg(安裝Flask時,這些依賴也會被自動安裝)json

2.Werkzurg服務器

單獨使用Werkzurg服務器:flask

from werkzeug.wrappers import Request, Response
from werkzeug.serving import run_simple


@Request.application
def run(request):
    return Response('Hello World')


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

3.實現簡單頁面請求

from flask import Flask

# 建立一個Flask實例,參數是爲這個實例定義一個名字,通常以__name__來指定
app = Flask(__name__)


# 利用裝飾器,綁定路由
@app.route('/index')
def index():
    return "HelloWorld"


if __name__ == '__main__':
    # 開始運行
    app.run()

2、簡單使用Flask

1.默認模板目錄

Flask默認使用templates做爲模板目錄。當咱們要返回html頁面時,默認去該文件夾下找模板文件:服務器

from flask import Flask, render_template # 建立一個Flask實例,參數是爲這個實例定義一個名字,通常以__name__來指定
app = Flask(__name__)


@app.route('/login')
def login():
    return render_template('login.html') if __name__ == '__main__':
    # 開始運行
    app.run()

2.修改模板目錄

在Django中,咱們是在setting配置文件中去修改。而在Flask中,咱們能夠以如下方式修改:cookie

# 建立一個Flask實例,參數是爲這個實例定義一個名字,通常以__name__來指定
app = Flask(__name__, template_folder='mytempfile')

咱們查看一下Flask類的構造函數:session

def __init__(
    self,
    import_name,
    static_url_path=None,
    static_folder='static',
    static_host=None,
    host_matching=False,
    subdomain_matching=False,
    template_folder='templates',
    instance_path=None,
    instance_relative_config=False,
    root_path=None
):

能夠看到,默認的模板目錄參數就是"templates"。app

3.Flask接受POST請求、獲取請求數據

在Flask中,默認只容許接收GET請求,若是咱們先接收POST請求,則須要設置。

在視圖函數中獲取請求數據時,咱們須要導入request模塊,而不是經過參數傳遞request。這是個Django不一樣的地方。

from flask import Flask, render_template, request

app = Flask(__name__)


@app.route('/login', methods=['GET', 'POST'])
def login():
    # 這裏的請求數據和Django中不同,Flask是經過上下文來管理的,request不是參數,而是導入的模塊
    if request.method == 'GET':
        pass

    if request.method == 'POST':
        pass


if __name__ == '__main__':
    # 開始運行
    app.run()

4.獲取GET數據和POST數據

視圖函數:

from flask import Flask, render_template, request, redirect

app = Flask(__name__)


@app.route('/login', methods=['GET', 'POST'])
def login():
    # 這裏的請求數據和Django中不同,Flask是經過上下文來管理的,request不是參數,而是導入的模塊
    if request.method == 'GET':
        # request.args對應django中的request.GET
        # request.args.get('user)
        return render_template('login.html')
    if request.method == 'POST':
        user = request.form.get('user')
        pwd = request.form.get('pwd')
        if user == 'leokale' and pwd == '123':
            return redirect('/index') else:
            return render_template('login.html', error="用戶名或密碼錯誤")
            # 也能夠傳多個值(使用字典),但和Django不同的地方是要解包
            # return render_template('login.html', **{"error": "用戶名或密碼錯誤"})


@app.route('/index')
def index():
    return '<h2>歡迎登陸</h2>'


if __name__ == '__main__':
    # 開始運行
    app.run()

注意幾個和django框架不同的地方:

1)須要在裝飾器中傳入methods參數,用來容許接收哪些請求

2)request.args和request.form分別對應django中的request.GET和request.POST

3)redirect跳轉和django基本一致

4)模板渲染回傳給頁面的參數,能夠單獨傳一個,也可使用字典拆包的形式傳入多個(django中參數爲字典)

對應html代碼:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Login</title>
</head>
<body>
    <h1>登陸頁面</h1>
    <form  method="POST">
        <input type="text" name="user"/>
        <input type="password" name="pwd"/>
        <input type="submit" value="登陸"/>{{error}}
 </form> </body> </html>

5.靜態文件

Flask中靜態文件默認放在static目錄下,若是要修改,也是修改Flask實例化時的參數。

app = Flask(__name__,static_folder='mystatic')

只須要將目錄名和這個參數值對應上便可。

可是注意,咱們在html中寫圖片請求鏈接時使用的url默認是和static_folder一致的:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>INDEX</title>
</head>
<body>
    <h2>歡迎訪問</h2>
    <img src="/mystatic/111.png">
</body>
</html>

但實際上,咱們也能夠經過一個參數來修改:

app = Flask(__name__, static_folder='mystatic', static_url_path='/vvvvv')

咱們將其修改成'/vvvvv',此時html中也要修改成'/vvvvv':

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>INDEX</title>
</head>
<body>
    <h2>歡迎訪問</h2>
    <img src="/vvvvv/111.png">
</body>
</html>

6.Flask中的session

from flask import Flask, render_template, request, redirect, session

app = Flask(__name__, static_folder='mystatic', static_url_path='/vvvvv')


@app.route('/login', methods=['GET', 'POST'])
def login():
    # 這裏的請求數據和Django中不同,Flask是經過上下文來管理的,request不是參數,而是導入的模塊
    if request.method == 'GET':
        # request.args對應django中的request.GET
        # request.args.get('user)
        return render_template('login.html')
    if request.method == 'POST':
        user = request.form.get('user')
        pwd = request.form.get('pwd')
        if user == 'leokale' and pwd == '123':
            session['user'] = user return redirect('/index')
        else:
            return render_template('login.html', error="用戶名或密碼錯誤")
            # 也能夠傳多個值(使用字典),但和Django不同的地方是要解包
            # return render_template('login.html', **{"error": "用戶名或密碼錯誤"})


@app.route('/index')
def index():
    user = session.get('user') if not user: return redirect('/login') return render_template('index.html')


if __name__ == '__main__':
    # 開始運行
    app.run()

Flask中session的使用和django很相似,但Flask中的session是導入的模塊。

注意,Flask的session默認是保存在加密的cookie中的,能夠參照django相關的介紹:http://www.javashuo.com/article/p-vhnywloy-gy.html

這裏要使用加密的cookie,又須要設置MD5加鹽中的鹽,不然會報錯:

RuntimeError: The session is unavailable because no secret key was set. Set the secret_key on the application to something unique and secret. 127.0.0.1 - - [20/Jan/2020 00:27:46] "POST /login HTTP/1.1" 500 -

咱們須要在代碼中加上加密設置:

app = Flask(__name__, static_folder='mystatic', static_url_path='/vvvvv')
app.secret_key = "sndjkfn"  # MD5加密的鹽

此時就可使用加密cookie來保存session了。

3、Flask的配置文件

1.經過importlib導入指定模塊

在setting.py文件中有以下代碼:

class Foo(object):
    DEBUG = True
    TEST = False

咱們若是在其餘模塊中經過importlib導入Foo類,獲取其中的DEBUG和TEST的值:

import importlib

# 給定類所處模塊
path = 'setting.Foo'

# 分割path,獲取模塊名和類名
(m, c) = path.rsplit('.', maxsplit=1)
# 導入模塊
m = importlib.import_module(m)
# 獲取類
cls = getattr(m, c)

# 遍歷類中的屬性,咱們要獲取的屬性是大寫的,因此使用isupper判斷
for key in dir(cls):
    if key.isupper():
        print(key, getattr(cls, key))

這樣,咱們就使用importlib庫動態的導入模塊,並獲取了其中的Foo類,以及其中的類屬性。

Flask的配置文件也是經過這種方式來加載的(不少框架的配置文件都是經過這種方式加載)。

2.Flask默認的配置信息

Flask默認的配置信息:

from flask import Flask, request, render_template

app = Flask(__name__)
# app.config獲取全部配置信息
print(app.config)
# 能夠經過這種方式修改
app.config['DEBUG'] = True

app.config配置內容:

{
    'ENV': 'development',
    'DEBUG': False,
    'TESTING': False,
    'PROPAGATE_EXCEPTIONS': None,
    'PRESERVE_CONTEXT_ON_EXCEPTION': None,
    'SECRET_KEY': None,
    'PERMANENT_SESSION_LIFETIME': datetime.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': datetime.timedelta(seconds = 43200),
    '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
}

3.使用自定義配置文件

使用如下方式爲其指定一個自定義配置文件:

from flask import Flask, request, render_template

app = Flask(__name__)
# app.config獲取DEBUG,默認爲False
print(app.config['DEBUG'])
# 經過from_object綁定一個類做爲配置對象,其中DEBUG爲True
app.config.from_object("setting.Myconfig") # 再次獲取DEBUG,爲True
print(app.config['DEBUG'])

這裏使用setting模塊中的Myconfig類做爲其配置文件。from_object源碼就是使用的1.中的方式對setting模塊進行導入,而後獲取Myconfig中的屬性值。

setting模塊中,咱們還能夠根據需求,使用多個類來區分不一樣場景下的配置:

class BaseConfig(object):
    # DEV和PROD場景下,相同的配置
    XXX = False


# 開發環境下的配置
class DevConfig(BaseConfig):
    DEBUG = True
    TEST = True


# 生產環境下的配置
class ProdConfig(BaseConfig):
    DEBUG = False
    TEST = False

在使用的時候,咱們只須要根據場景切換便可:

# 開發環境使用DevConfig配置
app.config.from_object("setting.DevConfig")

# 生產部署環境使用ProdConfig配置
app.config.from_object("setting.ProdConfig")

4.其餘配置文件的使用

除了3.中所描述的,使用模塊中的類來做爲配置。還有如下幾種方式:

1)from_pyfile

app.config.from_pyfile("setting.py")

例如:

# settings.py
                
DEBUG = True
TEST = False

2)from_envvar

app.config.from_envvar("FLASK_CONFIG_FILE")

例如在Linux下配置環境變量:

export FLASK_CONFIG_FILE='/path/to/config/file'

環境變量的值爲python文件(不必定是.py結尾的文件,但內容要是 DEBUG = True這種Python代碼格式)名稱,內部調用from_pyfile方法。

3)from_json

app.config.from_json("json文件名稱")

JSON文件名稱,必須是json格式,由於內部會執行json.loads。

4)from_mapping

app.config.from_mapping({'DEBUG':True})

參數爲一個字典。

4、Flask的路由系統

在Flask中,路由是經過裝飾器加到每一個視圖函數的:

from flask import Flask, request, render_template

app = Flask(__name__)


@app.route('/index', methods=['GET', 'POST']) def hello_world():
    if request.method == 'GET':
        return render_template('index.html')


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

1.@app.route中的endpoint

@app.route中的endpoint參數,就至關於django中的name參數,用來反向生成URL。

from flask import Flask, request, render_template, url_for

app = Flask(__name__)


@app.route('/index', methods=['GET', 'POST'], endpoint='n1')
def hello_world():
    print(url_for('n1')) # 打印/index if request.method == 'GET':
        return render_template('index.html')


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

其中的url_for至關於django中的reverse函數,用來利用endpoint反向生成url。

注意:若是咱們不指定endpoint,則endpoint默認等於視圖函數名,這裏即"hello_world"。url_for("hello_world")即爲"/index"。

2.動態路由

Flask的路由中,咱們可使用動態參數。

from flask import Flask, request, render_template

app = Flask(__name__)


@app.route('/index/<int:nid>', methods=['GET', 'POST'])
def hello_world(nid):
    print(nid) if request.method == 'GET':
        return render_template('index.html')


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

Flask是django使用動態路由的方式不一樣,在django中,動態路由是這樣的:'/index/(?P<nid>\d+)'。

注意:<int:nid>中,int表示動態參數的類型,咱們的視圖函數接收的參數nid爲整形,若是不寫,則默認爲字符串。

動態參數的類型除了有int之外,還有如下幾種類型:

@app.route('/user/<username>')  # 字符串
@app.route('/post/<int:post_id>')  # 整數
@app.route('/post/<float:post_id>')  # 浮點數
@app.route('/post/<path:path>')  # 路徑類型
@app.route('/post/<uuid:uuid>')  # UUID

在使用動態路由的時候,也可使用方向生成url:

url_for("hello_world",nid=777)  # 生成/index/777

5、Flask的請求數據

在Flask中,視圖函數沒有request參數。Flask中的request是直接導入使用的(上下文管理)。

from flask import Flask, request

在視圖函數中,能夠直接使用request:

#例如請求: http://127.0.0.1:5000/index/2019-03?name=leo
#如下request的屬性值爲:
request.method  # 請求類型
request.args  # GET請求的數據
request.args.get('user')
request.form  # POST請求的數據
request.form.get('user')
request.values
request.cookies  # Cookie數據
request.headers
"""
Accept: text/html, application/xhtml+xml, image/jxr, */*
...
Connection: Keep-Alive
"""
request.path  # 請求的url "/index/2019-12"
request.full_path  # "/index/2019-12?name=leo" 包含query
request.script_root  # ""
request.url  # "http://127.0.0.1:5000/index/2019-12?name=leo"
request.base_url  # "http://127.0.0.1:5000/index/2019-12"
request.url_root  #"http://127.0.0.1:5000/"
request.host_url  # "http://127.0.0.1:5000/"
request.host  # "127.0.0.1:5000"
request.files
obj = request.files['file_name']
obj.save('/var/www/uploads/' + secure_filename(f.filename))  #上傳文件

6、Flask的響應

1.Flask的幾種經常使用響應

在Flask中,經常使用的響應方式有如下幾種:

1)響應字符串

return "HelloWorld"  # 至關於django中的HttpResponse

2)響應json數據

from flask import jsonify
return jsonify({'k':'v'})
# 或 return json.dumps({'k':'v'})

3)響應渲染模板

from flask import render_template
return render_template('login.html')  # 至關於django中的render

4)響應重定向

from flask import redirect
return redirect('/index')

2.設置響應頭

在Flask中如何設置響應頭,特別是響應字符串的時候,沒法設置響應頭。

from flask import make_response

# 使用響應對象來封裝要返回的字符串
obj=make_response("hello world")
# obj=make_response(render_template('index.html'))

# 設置響應頭的字段xxx
obj.headers['xxx']='123'
# 設置cookie
obj.set_cookie('key','value')

# 返回obj
return obj

7、jinjia2模板語言

Jinjia2模板語言和django自帶的模板語言很類似,但功能更增強大。語法比較接近於Python。

視圖函數代碼:

from flask import Flask, request, render_template

app = Flask(__name__)

USER_INFO = { 1: {'name': 'Leo', 'age': 32, 'gender': 'male'}, 2: {'name': 'Jane', 'age': 33, 'gender': 'female'}, 3: {'name': 'Jake', 'age': 12, 'gender': 'male'}, 4: {'name': 'Alex', 'age': 45, 'gender': 'male'}, 5: {'name': 'Lilei', 'age': 77, 'gender': 'female'} }


@app.route('/users', methods=['GET', 'POST'])
def user_list():
    if request.method == 'GET':
        return render_template('users.html', user_list=USER_INFO)


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

咱們建立了一個字典,並返回給模板進行渲染。

html模板代碼:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Users</title>
</head>
<body>
<h2>用戶列表</h2>
<table>
    <thead>
    <tr>
        <th>ID</th>
        <th>名字</th>
        <th>歲數</th>
        <th>性別</th>
    </tr>
    </thead>
    <tbody>
    {% for k,v in user_list.items() %}
        <tr>
            <td>{{ k }}</td>
            <td>{{ v.name }}</td>
            <td>{{ v.age }}</td>
            <td>{{ v.gender }}</td>
        </tr>
    {% endfor %}
    </tbody>
</table>
</body>
</html>

能夠看到,使用for循環的方法和django模板語言是同樣的。

注意,user_list.items()這個括號必定要有,這是和python不同的地方。

在取值的時候,使用{{ v.name }}這種形式能夠獲取字典中字段的值。在Jinjia2中,還可使用{{ v.get('name','默認') }}以及{{ v['name'] }}的形式來取值,和Python基本一致。

8、使用裝飾器給視圖函數添加驗證功能

1.簡單裝飾器的問題

# 最簡單的裝飾器
def auth(func):
    def inner(*args, **kwargs):
        ret = func(*args, **kwargs)
        return ret
    return inner


# index函數使用了auth裝飾器
@auth
def index():
    print('index')

# index2函數沒有使用auth裝飾器
def index2():
    print('index2')


if __name__ == '__main__':
    print(index.__name__)  # 打印inner
    print(index2.__name__)  # 打印index2

能夠看到,當一個函數使用了簡單裝飾器後,打印其函數名,編程了裝飾器中的inner。

在這種狀況下,會影響咱們使用反向生成url的功能,即url_for(視圖函數名)。

2.使用functools.wraps

# 導入functools中的wraps
from functools import wraps def auth(func):
    # 使用wraps裝飾器
    @wraps(func)
    def inner(*args, **kwargs):
        ret = func(*args, **kwargs)
        return ret

    return inner


@auth
def index():
    print('index')


if __name__ == '__main__':
    print(index.__name__)  # 打印index

使用了functools的wraps裝飾器,實際上運行index函數時,仍是調用的是inner,可是wraps幫咱們將index函數的元信息封裝到了inner函數中。

3.使用auth裝飾器實現登陸驗證

from flask import Flask, request, render_template, redirect, session, url_for

app = Flask(__name__)
# 使用密碼鹽(session默認保存在加密cookie中,必須設置密碼鹽)
app.secret_key = 'snjdkfnjsk'

USER_INFO = {
    1: {'name': 'Leo', 'age': 32, 'gender': 'male'},
    2: {'name': 'Jane', 'age': 33, 'gender': 'female'},
    3: {'name': 'Jake', 'age': 12, 'gender': 'male'},
    4: {'name': 'Alex', 'age': 45, 'gender': 'male'},
    5: {'name': 'Lilei', 'age': 77, 'gender': 'female'}
}

from functools import wraps


# 認證用戶是否登陸
def auth(func): @wraps(func) def inner(*args, **kwargs): if not session.get('user'): return redirect(url_for('login')) ret = func(*args, **kwargs) return ret return inner # 登陸頁面
@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'GET':
        return render_template('login.html')
    user = request.form.get('username')
    pwd = request.form.get('password')
    # 用戶名密碼正確,則將用戶名寫到session中(默認保存在加密cookie中,默認cookie過時時間爲31天)
    if user == 'leokale' and pwd == '123456':
        session['user'] = user # 登陸成功,跳轉到users頁面
        return redirect('/users') # 登陸失敗,返回login頁面,並顯示用戶名或密碼錯誤
    return render_template('login.html', error='用戶名或密碼錯誤')


# users頁面,使用auth裝飾器進行登陸驗證
@app.route('/users', methods=['GET', 'POST'])
@auth def user_list():
    if request.method == 'GET':
        return render_template('users.html', user_list=USER_INFO)


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

1)實現驗證裝飾器,必須使用functools.wraps裝飾器,保證視圖函數名不變

2)在auth裝飾器中實現驗證登陸功能(檢查session中user是否存在)

3)將auth裝飾器應用到須要驗證的頁面

 

 

 

 

 

相關文章
相關標籤/搜索