1. 程式人生 > >Flask 學習筆記

Flask 學習筆記

資源 第一條 log utf8 sel val time ESS 窗口


title: Flask筆記

環境安裝與搭建(Windows7 up)

1.python3.6或者2.7的安裝請參考百度資料
2.通過在cmd中鍵入下行命令安裝虛擬環境 pip install virtualenv
3.激活虛擬環境
?cmd下鍵入
??mkdir Virtualenv (創建一個名為Virtualenv的文件夾)
??cd Virtualenv (進入該文件夾)
??virtualenv flask-env (創建一個虛擬環境,安裝一個名為flask-env的軟件)
??cd flask-env
??cd Scripts
??activate
然後出現
(flask-env) C:\Users\UserName\Virtualenv\flask-env\Scripts>
則激活成功
4.在 cmd 下鍵入
?pip install flask==0.12.2(安裝特定版本(0.12.2)的flask)
?python
?import flask
?print(flask.__version__)
出現版本號則說明安裝成功

編程(編譯器——PyCharm專業版)

新建項目

打開PyCharm ——> 新建項目,位置隨意,名稱最好不要出現中文
左側選擇Flask(PyCharm專業版,社區版無此功能,其他版本我不太了解)
解釋器(interpreter)設置有兩種情況
?1.Project interpreter New Virtualenv environment
??·展開Project interpreter New Virtualenv environment
??·在Base interpreter中點擊...(右側省略號)
??·選擇C:\Users\UserName\Virtualenv\flask-env\Scripts\python.exe
?2.Interpreter
??·在interpreter右側點擊...(右側省略號)
??·選擇C:\Users\UserName\Virtualenv\flask-env\Scripts\python.exe
點擊create按鈕

Hellow World

新建項目過後,如果你使用的是python2.x,請在代碼段中的第一行添加

#encoding utf-8

因為 python2.x 使用的是ASKII編碼,需要手動改成utf-8
如果你使用的是python3.x,則無需擔心

運行該項目,運行結果如下圖
技術分享圖片
它表示當前的這個項目運行在 http://127.0.0.1:5000/ 這個網站上
其中 127.0.0.1 表示本機地址,5000代表端口
打開後會發現,網頁出現了"Hello World"字樣
表明 return 之後的字符串表示的就是網頁中顯示的字樣
在運行後,修改完程序,點擊右上角紅色按鈕停止運行,之後重新運行代碼方可使修改後的代碼產生效果
代碼解釋

# 從 flask 框架中導入 Flask 這個類
from flask import Flask
# 初始化一個Flask對象
# 書寫這個類的原因
# 需要傳一個參數
# 1. 方便flask框架去尋找資源
# 2. 方便flask插件,例如Flask-Sqlalchemy 出現錯誤的時候,好去尋找問題所在的位置
app = Flask(__name__)

# @app.route是一個裝飾器
# @開頭為裝飾器
# 裝飾器的作用是做一個url與視圖函數的映射
# 這句話的意思是以後你如果訪問 127.0.0.1:5000/
# 到斜杠的時候,他就會去請求執行 hello_world()這個函數 讓後將結果返回給瀏覽器
@app.route(‘/‘)    # url
def hello_world(): # 視圖函數
    return ‘Hello‘


# 如果當前這個文件是作為入口程序運行
# 那麽就執行app.run()
if __name__ == ‘__main__‘:
    # app.run()
    # 啟動一個應用服務器接受用戶的請求
    # 它相當於是一個 while True
    # 會一直監聽用戶的請求,對於用戶的請求進行處理
    app.run()

debug模式(教程中為Flask0.12.2版本,Flaks不支持)

需要註意,開啟調試模式會成為一個巨大的安全隱患,因此他絕對不能用於生產環境中。
Debug 模式有兩個功能

  1. 重新回到我們的代碼段
    如果你這樣書寫代碼
from flask import Flask

app = Flask(__name__)

@app.route(‘/‘)
def hello_world():
    a = 1
    b = 0
    c = a / b
    return ‘Hello‘

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

運行且打開網頁,會出現如下圖所示的錯誤
技術分享圖片
如圖,瀏覽器並不會告訴你是除法出了錯誤,只有編譯器下方的窗口中有該錯誤信息
我們將代碼中的 app.run() 修改為

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

之後停止原先運行的程序,重新運行。
如果運行時未出現下圖提示信息(多出現於 PyCharm 2018 2018.5.8寫)
技術分享圖片
則點擊編譯器右上側運行符號旁邊的項目名稱處,選擇Edit Configuration
勾選下圖選項
技術分享圖片
重新打開網站便可以看到錯誤信息了

  1. 在修改你的代碼後,保存文件的同時,編譯器會在下方提示你你的信息被改變了,同時網頁也會重新加載變更後的代碼信息。
    提示信息如下圖
    技術分享圖片

    使用配置文件

    在項目中新建python file,添加下行代碼
DEBUG = True

回到原先的py文件中,刪除app.run(debug = True)括號中的debug = True
修改為

from flask import Flask
# 被添加的代碼
import config    
app = Flask(__name__)
# 被添加的代碼
app.config.from_object(config)
@app.route(‘/‘)
def hello_world():
    return ‘wol‘

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

這同樣可以為該文件設置debug模式

url傳參

新建項目url_params
在項目中添加一行代碼,重載視圖函數

@app.route(‘/article/<id>‘)
def article(id):
    return ‘您請求的參數是:%s‘ % id

Tips:
?1. 在python2.x中,需要這樣書寫

    return u‘您請求的參數是:%s‘ % id

?在字符串之前加 u 表示將字符串進行Unicode編碼
?2. 參數需要放進兩個尖括號之間
?3. 視圖函數中需要放置和url中參數同名的參數

然後運行項目,打開鏈接,在 http://127.0.0.1:5000/ 後面添加article/aabbcc
點擊回車,頁面會顯示您請求的參數是:aabbcc

PS:
如果只能通過勾選debug項來改變debug模式的啟動或關閉,那麽不論是在run()中更改debug
模式,還是通過配置文件更改,都是無效的

反轉URL

正轉URL的概念:通過URL取得視圖函數的內容
反轉則是反過來的意思,就是知道視圖函數的名稱,可以反轉得到視圖函數當前的url
新建項目url_reverse,在項目中添加下列代碼

from flask import Flask, url_for

app = Flask(__name__)

@app.route(‘/‘)
def index():
    print(url_for(‘my_list‘))
    print(url_for(‘article‘, id = ‘abc‘))
    return ‘Hello World!‘

@app.route(‘/list/‘)
def my_list():
    return ‘list‘

@app.route(‘/article/<id>‘)
def article(id):
    return ‘您請求的參數為:%s‘ % id

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

在執行後,控制臺會打印如下圖所示信息
技術分享圖片
反轉URL的用處:
?·在頁面重定向的時候會用到
?·在模板中也會使用到

頁內跳轉和重定向

重定向例子:
例如在某論壇中,用戶在未登錄的情況下,點擊論壇中的評論,當然此時是沒有辦法評論的,因為用戶
沒有登陸,此時,頁面應當跳轉至登陸界面;如果你登陸過了,那麽能夠跳轉至評論頁面。
這個過程稱之為重定向

新建項目redirect,添加代碼

from flask import Flask, redirect, url_for

app = Flask(__name__)


@app.route(‘\‘)
def index():
    return ‘這是首頁‘

@app.route(‘\login\‘)
def login():
    return ‘這是登陸頁面‘

if __name__ == ‘__main__‘
    app.run(debug=True)

執行程序後打開鏈接會發現,頁面中出現“這是首頁”的字樣
然後修改代碼如下

from flask import Flask, redirect, url_for

app = Flask(__name__)


@app.route(‘/‘)
def index():
    return redirect(‘/login/‘)
    return ‘這是首頁‘

@app.route(‘/login/‘)
def login():
    return ‘這是登陸頁‘

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

運行後刷新頁面,發現頁面直接跳入http://127.0.0.1:5000/login/

當然比較正確的寫法為

from flask import Flask, redirect, url_for

app = Flask(__name__)

@app.route(‘\‘)
def index():
    # 博主本人理解為通過url_for找到名稱為login的函數
    # 然後找到這個函數裝飾器中的url參數
    # 將這個url參數傳遞給login_url,達到了url反轉的功能
    login_url = url_for(‘login‘) 
    return redirect(login_url)
    return ‘這是首頁‘

@app.route(‘\login\‘)
def login():
    return ‘這是登陸頁‘

if __ name__ == ‘__main__‘:
    app.run(debug = True)   

這樣書寫的好處是無論你的裝飾器@app.route(‘/login/‘)中的url參數如何改變,
都不會導致login_url在傳遞參數給redirect()時出現問題。

下面我們實現一個小案例:未登錄用戶點擊評論進入登陸頁面,已登錄用戶進入評論頁面
用“1”表示已登陸,其他表示未登錄

from flask import Flask, redirect, url_for

app = Flask(__name__)


@app.route(‘/‘)
def index():
    login_url = url_for(‘login‘)
    return redirect(login_url)
    return ‘這是首頁‘

@app.route(‘/login/‘)
def login():
    return ‘這是登陸界面‘

@app.route(‘/commitment/<is_login>/‘)
def commitment(is_login):
    if is_login == ‘1‘:
        return ‘這是評論頁面‘
    else:
        return redirect(url_for(‘login‘))



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

執行程序後,首先跳轉到登陸頁面,刪除login/,添加commitment/1,點擊回車,顯示這是評論頁面,輸入其他數字則顯示登陸頁面

模板渲染和參數

新建項目template01,添加代碼

from flask import Flask

app = Flask(__name__)


@app.route(‘/‘)
def index():
    return ‘index‘


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

在下圖位置可以看見
技術分享圖片
static和templates文件夾
static:用於存放一些靜態資源,例如css, gs, img文件
templates:專門用於存放html文件
所以,我們在templates上右鍵->New file->HTML file,取名為index.html
在<//body>之間添加一段文字(博主不知道怎麽處理Hexo下的轉移QAQ),達到如下效果

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    這是HTML文件中出現的文字
</body>
</html>

然後回到app.py(或者你建立的主文件)
由於你添加了一個HTML文件,所以這時你就不能只渲染‘index‘,除此之外還要渲染HTML
這時候需要import render_template

from flask import Flask, render_template

接著刪除return ‘index‘,添加 return render_template(‘index.html‘),括號內為剛剛新建的HTML文件
代碼如下

from flask import Flask, render_templale
app =  Flask(__name__)

@app.route(‘\‘)
def index():
    return render_template(‘index.html‘)

if __name__ == __main__:
    app.run(debug = True)   
‘‘‘
    在render_template()中,是不需要傳文件夾templates的名稱的當調用render_template()時,
編譯器會自動在templates文件夾下尋找與傳入參數(文件名)匹配的文件,如找不到則報錯

    但是如果你在templates下創建了一個文件夾another,且將index.html移動進這個文件夾內,
那麽這時需要你這樣使用這個文件
    render_template(‘another/index.html‘)
‘‘‘

運行後打開鏈接會發現在<//body>中書寫的文字顯示在了網頁上

接下來,我們嘗試使用HTML在頁面的右上角顯示用戶的用戶名
因為顯示用戶名需要與數據庫通信,所以不能將其寫死了,不可以像下面這樣書寫代碼

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    這是HTML文件中出現的文字
    <p>用戶名:Outro</p> <!--這樣書寫是錯誤的!-->
</body>
</html>

所以我們應該試著數據庫模擬傳參的行為
回到app.py,在index()函數中修改render_template()

@app.route(‘\‘)
def index():
    return render_template(‘index.html‘, username = Outro)

在index.html中的<//body>添加

</head>
<body>
    這是HTML文件中出現的文字
    <p>用戶名:{{ username }}</p> <!-- 被添加的內容 -->
</body>
</html>
<!-- Tip:兩個嵌套花括號{{被引用的變量}}是Flask的特有語法,記住就好 -->

這樣我們就完成了參數的傳遞(模擬數據庫的通信)
重新運行一下程序(註意,這裏無論你是否開啟了flask的debug模式,都要重新運行一下,因為
修改html文件並不會引起服務器重啟)
接著讀者們可以嘗試自己修改一下render_template(‘index.html‘, username = ‘123‘)
中的 username 內容。觀察頁面的變化

當然,網頁中可添加的信息完全不止這些,比如客戶還想向網頁中添加年齡、性別、身高或者電話等等信息
下面的實現方式就顯得有些愚笨了
app.py中的部分代碼

@app.route(‘\‘)
def index():
    return render_template(‘index.html‘, username = ‘xxx‘, gender = ‘xxx‘, height = ‘xxx‘)    

index.html中的部分代碼

</head>
<body>
    這是HTML文件中出現的文字
    <p>用戶名:{{ username }}</p>
    <p>身高:{{ height }}</p>
    <p>性別:{{ gender }}</p>
</body>
</html>

而且這樣的實現方式同時並不利於後期維護
因此我們可以用下面這種方式實現

@app.route(‘\‘)
def index():
    # 以字典的方式存儲你需要放到網站上的信息
    context = {
        ‘username‘ : ‘Outro‘,
        ‘height‘ : ‘180‘,
        ‘gender‘ : ‘male‘
    }  
    # 這裏使用 **congtext 說明是將傳入的參數作為字典進行處理
    return render_template(‘index.html‘, **context)

模板中訪問屬性和字典

在原有的項目template01中的app.py文件中添加下列代碼

from flask import Flask, url_for,render_template

app = Flask(__name__)


@app.route(‘/‘)
def index():
    # 被添加的代碼
    class Person(object):
        name = ‘Outro‘
        age = 18
    p = Person()
    # 被添加的代碼
    context = {
        ‘username‘:‘Outro‘,
        ‘height‘:‘180‘,
        ‘gender‘:‘male‘,
        ‘age‘:‘18‘,
        # 被添加的代碼
        ‘person‘:p,
        ‘websites‘:{
            ‘baidu‘:‘www.baidu.com‘,
            ‘google‘:‘www.google.com‘
        }
        # 被添加的代碼

    }
    return render_template(‘index.html‘, **context)

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

在index.html文件中添加代碼至如下代碼所示

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <p>用戶名:{{ username }}</p>
    <p>身高:{{ height }}</p>
    <p>性別:{{ gender }}</p>

    <hr>
    <p>名字:{{ person.name }}</p>
    <p>年齡:{{ person.age }}</p>
    <hr>
    <p>百度:{{ websites.baidu }}</p>
    <p>谷歌:{{ websites.google }}</p>
    <!-- 上兩行代碼可以這樣寫:
        <p>百度:{{ websites.[baidu] }}</p>
        <p>谷歌:{{ websites.[‘google‘] }}</p>
    -->
    

</body>
</html>

以上就是在Flask中使用字典序和類對象的方法

if判斷

新建項目if_statement,項目中新建文件if_statement.py,代碼如下

from flask import Flask,render_template

app = Flask(__name__)


@app.route(‘/‘)
def index():
    return render_template(‘index.html‘,)


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

在templates文件夾下新建文件index.html,代碼如下:

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

現在做一個需求,實現是否判斷已登陸的功能
更改上方代碼為:

if_statement.py

from flask import Flask,render_template

app = Flask(__name__)


@app.route(‘/<is_login>/‘)
def index(is_login):
    if is_login == ‘1‘:
        user = {
            ‘username‘:‘Outro‘,
            ‘age‘:18
        }
        return render_template(‘index.html‘,user = user)
    else:
        return render_template(‘index.html‘)

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

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    {% if user %} <!-- 判斷 user 是否存在 -->
        <a href="#">{{ user.username }}</a>
        <a href="#">註銷</a>
    {% else %}
        <a href="#">登陸</a>
        <a href="#">註冊</a>
    {% endif %}

</body>
</html>

這時,保存兩個文件並運行if_statement,會顯示網頁錯誤
因為此時的路徑需要你輸入一個login的狀態,於是在端口號後面添加
/1得到已經登陸的狀態,/其他數字得到未登錄的狀態

接著我們看一下下面這段HTML代碼(修改index.html)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <!-- 使用 and 做了另一個對年齡的判斷 -->
    {% if user and user.age > 18 %}
        <a href="#">{{ user.username }}</a>
        <a href="#">註銷</a>
    {% else %}
        <a href="#">登陸</a>
        <a href="#">註冊</a>
    {% endif %}

</body>
</html>

如註釋所寫,HTML的if使用and關鍵字做與邏輯運算

for循環

新建項目for_statement,新建文件for_statement.py 和 index.html

字典遍歷
for_statement.py

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

@app.route(‘/‘)

def index():
    user = {
        ‘username‘:‘Outro‘,
        ‘age‘:‘18‘
    }
    return render_template(‘index.html‘,user = user)


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

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <!-- 對字典的遍歷 -->
    {% for v,k in user.items() %}
        <p>{{ v }}:{{ k }}</p>
    {% endfor %}
</body>
</html>

列表遍歷
修改for_statement.py 和 index.html 為下列代碼

from flask import Flask,render_template

app = Flask(__name__)

# for遍歷字典

@app.route(‘/‘)

def index():
    user = {
        ‘username‘:‘Outro‘,
        ‘age‘:‘18‘
    }
    # 添加 websites 列表變量
    websites = [‘baidu.com‘,‘google.com‘]
    return render_template(‘index.html‘,user = user, websites = websites)

if __name__ == ‘__main__‘:
    app.run()
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    {% for v,k in user.items() %}
        <p>{{ v }}:{{ k }}</p>
    {% endfor %}
    <!-- 新增 for 循環輸出列表值 -->
    {% for website in websites %}
        <p>{{ website }}</p>
    {% endfor %}
    
</body>
</html>

小案例:做一個四大名著的網絡表(書名,作者,價格)
修改for_statement.py 和 index.html 如下

from flask import Flask,render_template

app = Flask(__name__)

@app.route(‘/‘)
def index():
    books = [
        {
        ‘name‘ : ‘西遊記‘,
        ‘author‘ : ‘吳承恩‘,
        ‘price‘ : ‘109‘
        },
        {
            ‘name‘ : ‘紅樓夢‘,
            ‘author‘: ‘曹雪芹‘,
            ‘price‘: ‘200‘
        },
        {
            ‘name‘: ‘三國演義‘,
            ‘author‘: ‘羅貫中‘,
            ‘price‘: ‘120‘
        },
        {
            ‘name‘: ‘水滸傳‘,
            ‘author‘: ‘施耐庵‘,
            ‘price‘: ‘130‘
        }
    ]
    return render_template(‘index.html‘,books = books)

if __name__ == ‘__main__‘:
    app.run()
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<!-- 表格 -->
<table>
    <!-- 表頭 -->
    <thead>
        <th>書名</th>
        <th>作者</th>
        <th>價格</th>
    </thead>
    <tbody>
    {% for book in books %}
        <tr>
            <td>{{ book.name }}</td>
            <td>{{ book.author }}</td>
            <td>{{ book.price }}</td>
        </tr>
    {% endfor %}

    </tbody>
</table>
</body>
</html>

過濾器

創建一個項目名稱為 filter_demo 創建文件 filter_demo.py, index.html代碼如下:

from flask import Flask, render_template

app = Flask(__name__)


@app.route(‘/‘)
def index():
    return render_template(‘index.html‘)


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

案例1:如果用戶上傳了頭像,則顯示,如果沒有則顯示默認頭像

在該論壇中https://bbs.csdn.net/topics/392280790 (2018/5/17)
賦值其中一個非默認頭像的圖片地址,然後修改 filter_demo.py 中的代碼為

from flask import Flask, render_template

app = Flask(__name__)

> 引用塊內容

@app.route(‘/‘)
def index():
    return render_template(‘index.html‘, avatar = "https://avatar.csdn.net/F/4/9/2_zhao4zhong1.jpg")


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

接著修改 index.html 文件中的代碼

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>過濾器</title>
</head>
<body>
<img src="{{ avatar|default(‘https://avatar.csdn.net/D/1/D/2_keer_zu.jpg‘) }}" >
</body>
</html>

其中 | 符號為管道符號,作用是過濾。在這裏的意思是如果找不到 avatar ,則將其過濾為default
之後我們可以將 filter_demo.py 文件中的 avatar 變量(刪去),然後運行一下文件。

案例2:在你的頁面中添加評論
修改 filter_demo.py 和 index.html 文件如下
fliter_demo.py

from flask import Flask, render_template

app = Flask(__name__)


@app.route(‘/‘)
def index():
    comment = [
        {
            ‘user‘:‘Outro‘,
            ‘content‘:‘空白頁面啊這個!‘
        },
        {
            ‘user‘:‘遊客‘,
            ‘content‘:‘破站!‘
        }
    ]
    return render_template(‘index.html‘, avatar = "https://avatar.csdn.net/F/4/9/2_zhao4zhong1.jpg",comments = comment)


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

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>過濾器</title>
</head>
<body>
<img src="{{ avatar|default(‘https://avatar.csdn.net/D/1/D/2_keer_zu.jpg‘) }}" >

<hr>

<p>評論數:({{ comments | length }})</p>
<ul>
    {% for comment in comments %}
        <li>
            <a href="#">{{ comment.user }}</a>
            <p>{{ comment.content }}</p>
        </li>
    {% endfor %}

</ul>
</body>
</html>

在這裏的 length 是用來取comments 的長度的,也就是計算評論數

連接數據庫註意事項

由於MySQLdb不支持python3.x,因此需要使用pymysql來代替它。
而使用 SQLAlchemy 時其URI格式如下

DB_URI = ‘mysql+pymysql://{}:{}@{}/{}?charset=utf8‘.format(USERNAME, PASSWORD, HOSTNAME, DATABASE)

此外,第一次配置環境後運行時會報警告

C:\Users\Outro\Virtualenv\flask-env\lib\site-packages\pymysql\cursors.py:170:Warning (1366, "Incorret string value: ‘\\xD6\\xD0\\xB9\\xFA\\xB1\\xEA...‘ for column ‘VARIABLE_VALUE‘ at row 518") result = self._query(query)

可以無視,數據庫可以照常連接,另外,我在重啟之後再無報錯,讀者可以試試配置環境後重啟試試

數據的增刪改查操作通過session完成

增加:
config.py

USERNAME = ‘root‘
PASSWORD = ‘root‘
HOSTNAME = ‘127.0.0.1‘
DATABASE = ‘db_demo2‘
DB_URI = ‘mysql+pymysql://{}:{}@{}/{}?charset=utf8‘.format(USERNAME,PASSWORD,
                                                           HOSTNAME,DATABASE)

SQLALCHEMY_DATABASE_URI = DB_URI
SQLALCHEMY_TRACK_MODIFICATIONS = True

app.py

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import config

app = Flask(__name__)
app.config.from_object(config)
db = SQLAlchemy(app)

class Article(db.Model):
    __tablename__ = ‘article‘
    id = db.Column(db.Integer, primary_key=True,autoincrement=True)
    title = db.Column(db.String(100),nullable=Flask)
    content = db.Column(db.Text,nullable=False)

db.create_all()

@app.route(‘/‘)
def hello_world():
    article1 = Article(title=‘aaa‘,content=‘bbb‘)
    # 增加一條數據
    db.session.add(article1)
    # 執行事務
    db.session.commit()
    return ‘Hello!‘


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

查找:

@app.route(‘/‘):
    result = Article.query.filter(Article.title == ‘aaa‘).all()
    article1 = result[0]
    print(article1.title)
    print(article1.content)
    return ‘Hello!‘

第二種方式:

@app.route(‘/‘):
    # result = Article.query.filter(Article.title == ‘aaa‘) 
    # 因為 result 的數據類型此時是 Query,它可以進行類似於 list 的操作
    # 如果想要對第一條數據進行訪問的話,可以用如下方式
    result = Article.query.filter(Article.title == ‘aaa‘).first()
    # 使用first()方法的話,如果沒有數據,那麽它返回null
    # 如果是用 [] 訪問數據,如果沒有數據,它就會拋出異常

    article1 = result[0]
    print(article1.title)
    print(article1.content)
    return ‘Hello!‘

修改:

@app.route(‘/‘)
def hello_world():
    # 先查找需要被修改的表項
    result = Article.query.filter(Article.title == ‘aaa‘).first()
    # 修改該表項的某個字段
    result.title = ‘new title‘
    # 呈遞事務請求
    db.session.commit()
    return ‘Hello‘

刪除:

@app.route(‘/‘)
def hello_world():
    # 先找到需要被刪除的表項
    result = Article.query.filter(Article.content == ‘bbb‘).first()
    # 刪除該項目
    db.session.delete(result)
    # 呈遞該請求
    db.session.commit()
    return ‘Hello‘

外鍵以及 backref 的使用

1 為一個表添加外鍵:

# 用戶表
class User(db.Model):
    id = db.Column(db.Integer,primery_key=True,autoincrement=True)
    username = db.Column(db.String(100),nullable=False)

# 文章表
class Article(db.Model):
    id = db.Column(db.Integet,primery_key=True,autoincrement=True)
    title = db.Column(db.String(100),nullable=False)
    content = db.Column(db.Text, nullable=False)
    author_id = db.Column(db.Integer,db.ForeignKey(‘user.id‘))

使用外鍵查找作者

article = Article.query.filter(Article.title == ‘aaa‘).first()
author_id = article.author_id
user = User.query.filter(User.id == author_id).first()
print(‘username : %s‘ % user.username)

2 為一個表使用 refback

class Article(db.Model):
    id = db.Column(db.Integer,primery_key=True,autoincrement=True)
    title = db.Column(db.String(100),nullable=False)
    content = db.Column(db.Text,nullable=False)
    author_id = db.Column(db.Integer,db.ForeignKey=‘user.id‘)

    author = db.relationship(‘User‘,backref=db.backref(‘articles‘))

使用 refback 查詢某個已知作者的所有文章

user = User.query.filter(User.username == ‘Outro‘)
result = user.articles
for article in result:
    print(‘-‘*10)
    print(article.title)

使用 refback 查詢已知文章的作者

article = Article.query.filter(Article.title == ‘xxx‘).first()
print(‘username : %s‘ % article.author.username)

多對多關系

多對多的關系,要通過一個中間表進行關聯
中間表的創建方式如下:

article_tag = db.Table(‘article_tag‘,
        db.Column(‘Column_name1‘,db.Integer,db.ForeignKey(‘article.id‘)),
        db.Column(‘Column_name2‘,db.Integer,db.ForeignKey(‘tag.id‘)))

設置關聯:

class Article(db.Model):
    __tablename__ = ‘table‘
    id = db.Column(db.Integer,primer_key=True,autoincrement=True)
    title = db.Column(db.String(100),nullable=False)

    tag = db.relationship(‘Tag‘,secongdary=article_tag,backref=db.backref(‘articles‘))

Flask-Script

1.Flask-Script的作用:
?可以通過 cmd 形式操作Flask,包括跑開發版本的服務器,設置數據庫,定時任務等
2.安裝:
?進入虛擬環境,鍵入"pip install flask-script" 進行安裝。
3.運行:
?在cmd中激活虛擬環境,使用python file_name.py運行
示例代碼:
創建一個項目,兩個文件 app.py 以及 manager.py
flask_scripy_demo.py

from flask import Flask
app = Flask(__name__)

@app.route(‘/‘)
def hello_world():
    return ‘Hello World!‘

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

manager.py

from flask_script import Manager
# 也可以寫成 from file_name import app
# from flask_scripy_demo import app
import app
# app.app:引用 app.py 中的app變量
manager = Manager(app.app)

@manager.command
def runserver():
    print(‘Server is running!‘)

if __name__ == ‘__main__‘:
    manager.run()

cmd下鍵入

python manager.py runserver

按下回車,運行成功會出現

Server is running!

添加命令,並通過命令調用功能
flask_script_demo.py

from flask import Flask
app = Flask(__name__)

@app.route(‘/‘)
def hello_world():
    return ‘Hello World!‘

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

新建文件db_script.py
db_script.py

from flask_script import Manager

DBManager = Manager()

@DBManager.command
def init():
    print(‘數據庫初始化完成‘)

@DBManager.command
def migrate():
    print(‘數據表遷移成功‘)

manager.py

from flask_script import Manager
# 也可以寫成 from file_name import app
# from flask_scripy_demo import app
import app
from db_script.py import DBManager

# app.app:引用 app.py 中的app變量
manager = Manager(app.app)

@manager.command
def runserver():
    print(‘Server is running!‘)

manager.add_command(‘db‘,DBManager)

if __name__ == ‘__main__‘:
    manager.run()

cmd 中分別鍵入1,2兩條命令

python manager.py db init
回車
python manager.py db imgrate
回車

運行成功後分別顯示

數據庫初始化完成
數據表遷移成功

model分離主文件

?如果將所有的 class 均放在主文件中,會使各個文件分工不明確,因此需要將 model (class)集中放在一起。這樣的話就有兩個文件 app.py 和 models.py。app.py 是一定會引用models.py 中的class, 那麽如果 models.py 需要引用 app.py 中的變量時,這時候不能夠在models.py 中書寫

from app.py import xxx

這樣會導致遞歸引用,編譯器也會報錯,所以可以用以下思路解決該問題
技術分享圖片
config.py

USERNAME = ‘root‘
PASSWORD = ‘root‘
HOST = ‘127.0.0.1‘
DATABASE = ‘db_demo4‘
DATABASE_URI = ‘mysql+pymysql://{}:{}@{}/{}?charset=utf8‘.format(USERNAME,PASSWORD,HOST,DATABASE)

SQLALCHEMY_DATABASE_URI = DATABASE_URI
SQLALCHEMY_TRACK_MODIFICATIONS = True

exts.py

from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

model.py

from exts import db

class Article(db.Model):
    __tablename__ = ‘article‘
    id = db.Column(db.Integer,primary_key=True,autoincrement=True)
    title = db.Column(db.String(100),nullable=False)

app.py

from flask import Flask
from models import Article
from exts import db
import config

app = Flask(__name__)
app.config.from_object(config)
db.init_app(app)

db.create_all()

@app.route(‘/‘)
def hello_world():
    return ‘Hello World!‘

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

如果不方便在定義的時候將db初始化,可以在後期調用db.init()函數初始化db。
運行app.py,會報錯,錯誤信息如下:

RuntimeError: No application found. Either work inside a view function or push an application context. 

這是因為,雖然代碼中使用了db.init_app(app)註冊了 app ,但是並未將其添加進 app 棧中,情況如下圖:
技術分享圖片
因此,程序在 app棧 中無法訪問到 app。所以需要手動將 app 加入到棧中,修改 app.run 代碼如下:

from flask import Flask
import config
from exts import db
# 一定要引用模塊,否則在運行後程序會因為找不到模型,創建表失敗
from models import Article

app = Flask(__name__)
app.config.from_object(config)

db.init_app(app)
# 增加下行代碼,修改應用程序級別的上下文請求
# app_context()將會生成 AppContext 對象
# with 塊將會將其(db.create_all())入棧,並且使得 current_app 指針指向該應用程序(db.create_all())
‘‘‘
app_context()
Create an AppContext. Use as a with block to push the context, which will make current_app point at this application.
‘‘‘
###########################
with app.app_context():
    db.create_all()
###########################

@app.route(‘/‘)
def hello_world():
    return ‘Hello World!‘


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

Flask-Migrate

??當你的數據庫中已上線且已有多條用戶數據時,想要添加數據庫中的屬性,使用db.create_all是不能夠將屬性映射到數據庫中的,通過先 drop 再 create 的方式則會刪除已有的用戶數據。這個時候就需要使用 Flask-Migrate。
在 cmd 中打開虛擬環境,使用pip install flask-migrate進行安裝。

代碼示例基於下述四個基本文件,文件名及代碼如下述
config.py

USERNAME = ‘root‘
PASSWORD = ‘root‘
DATABASE = ‘migrate_demo‘
HOST = ‘127.0.0.1‘

DATABASE_URI = ‘mysql+pymysql://{}:{}@{}/{}?charset=utf8‘.format(USERNAME,PASSWORD,HOST,DATABASE)

SQLALCHEMY_DATABASE_URI = DATABASE_URI
SQLALCHEMY_TRACK_MODIFICATIONS = True

exts.py

from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

models.py

from exts import db

class Article(db.Model):
    __tablename__ = ‘article‘
    id = db.Column(db.Integer,primary_key=True,autoincrement=True)
    title = db.Column(db.String(100),nullable=False)
    content = db.Column(db.Text,nullable=False)    

migrate_demo.py

from flask import Flask
import config
from exts import db
from models import Article

app = Flask(__name__)
app.config.from_object(config)

db.init_app(app)


@app.route(‘/‘)
def hello_world():
    return ‘Hello World!‘


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

創建文件 manage.py 內容如下
manage.py

from migrate_demo import app
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand
from exts import db
# db 要獲取模型
from models import Article

# 創建思想:模型 -> 遷移文件 -> 生成表
manager = Manager(app)

# 1. 要使用flask_migrate,必須綁定 app 和 db
migrate = Migrate(app, db)

# 2.把MigrateCommand命令添加到manager中
manager.add_command(‘db‘, MigrateCommand)

if __name__ == ‘__main__‘:
    manager.run()

在激活虛擬環境的 cmd 下鍵入以下命令

python manage.py db init
python manage.py db migrate
python manage.py db upgrade

第一條命令可以理解為建立數據表模型
第二條命令可以理解為建立遷移文件
第三條命令可以理解為更具遷移文件更新數據庫

在執行完第一條命令後,會發現 PyCharm 中的項目下多了一個名為 migrations 的文件夾
技術分享圖片
此時的 versions 文件夾是沒有文件的
執行完第二條命令之後,會發現 migrations 文件夾下的 versions 文件夾多出了一個文件
技術分享圖片
此文件即為遷移文件
執行完第三條命令之後,在 mysql 中查看,發現數據庫已經更新。且數據表中有一個名為 alembic_version 的數據表,它是用於記錄遷移文件的版本號,暫且不管

根據思路

模型 -> 遷移文件 -> 生成表

可以推斷出,如果你更新了數據表模型(本例中為 models.py 中的 Article 類),那麽在更新數據庫的時候,第一條指令時不需要執行的,需要分別執行第二和第三條指令。
修改 models.py 文件如下

from exts import db

class Article(db.Model):
    __tablename__ = ‘article‘
    id = db.Column(db.Integer,primary_key=True,autoincrement=True)
    title = db.Column(db.String(100),nullable=False)
    content = db.Column(db.Text,nullable=False)
    # 添加 tags 屬性
    tags = db.Column(db.String(100),nullable=False)

在 cmd 中鍵入第二和第三條命令之後
查看 mysql 中的表,會發現 tags 屬性已經被添加進數據表中
此時如果查看 versions 文件夾,會發現多出一個文件
技術分享圖片
這就是更新後的遷移文件

Chrome 查看 cookie 的方法:
點擊右上角豎排三點的按鈕,點擊 設置,打開 高級選項,點擊 內容設置,點擊 Cookie 項,查看所有 Cookie 和網站數據,找到 127.0.0.1,點擊即可瀏覽 session 信息。

  • 在 Flask 中使用 session,需要將 session 導入項目,使用下行命令
    python from flask import session
  • 使用 session 時,需要設置 SECRET_KEY 對 session 中的進行數據加密,設置方式有兩種,這兩種都可以基於隨機生成的數串
    第一種:
    python import os app.config[‘SECRET_KEY‘] = os.urandom(24)
    第二種:
    新建 config.py 文件,添加以下代碼
    python import os SECRET_KEY = os.urandom
    在 app.py 中添加下行代碼
    python import config app.config.from_object(config)
  • 使用 session 時,就如同使用字典一樣。比如下列代碼實現了向 session 中添加用戶
    python session[‘username‘] = ‘Outro‘
    當你想要獲取 session 中的某個數據時,和字典的操作方式相似
    python session.get(‘username‘)
    當你想要刪除 session 中的某個數據時,代碼如下
    python session.pop(‘username‘)
    或者
    python del session[‘username‘]
    當你想要清除 session 中的所有數據時,代碼如下
    python session.clear()
  • 過期時間
    過期時間可以使用以下代碼設置
    python session.permanent = True
    默認31天後關閉
    如果不設置,則默認關閉瀏覽器關閉

    如果要設置特定的時間,可以使用以下代碼
    ```python
    from datetime import timedelta
    app.config[‘PERMANENT_SESSION_LIFETIME‘] = timedelta(days=7)

    @app.route(‘/‘)
    def hello_world():
    session[‘username‘] = ‘Outro‘
    # session 默認過期時間是瀏覽器關閉後
    # permanent 為True時,過期時間自動設置為31天
    session.permanent = True
    return ‘Hello World!‘
    ```

測試完整代碼如下:

from flask import Flask,session
import os
from datetime import timedelta

app = Flask(__name__)
app.config[‘SECRET_KEY‘] = os.urandom(24)
app.config[‘PERMANENT_SESSION_LIFETIME‘] = timedelta(days=7)

# app.config.from_object(config)

# 添加上數據到session中
# 操作session和操作字典是相同的
# SECRET_KEY

@app.route(‘/‘)
def hello_world():
    session[‘username‘] = ‘Outro‘
    # session 默認過期時間是瀏覽器關閉後
    # permanent 為True時,過期時間自動設置為31天
    session.permanent = True
    return ‘Hello World!‘

@app.route(‘/get/‘)
def get():
    # session[‘username‘] 未找到拋出異常
    # session.get(‘username‘) 未找到返回 null
    return session.get(‘username‘)

@app.route(‘/delete/‘)
def delete():
    print(session.get(‘username‘))
    print(session.pop(‘username‘))
    print(session.get(‘username‘))
    return ‘success‘

@app.route(‘/clear/‘)
def clear():
    print(‘clear‘)
    print(session.get(‘username‘))
    # 刪除session中的所有數據
    session.clear()
    print(session.get(‘username‘))
    return ‘success‘



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

get 和 post

  1. get 是用戶獲取服務器端數據的請求,使用場景一般為獲取或查詢數據,它通常跟在url的?號後面,不會修改服務器上的數據。
  2. post 使用戶修該服務器數據的請求,使用場景一般為論壇評論或者更新博客等等。當然它也可以只用於獲取數據。
  3. get 和 post 均包含在 flask 下的 request 庫中,操作方式與字典類似。
  • 獲取 get 請求
    創建 html 文件 index.html,內容如下
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首頁</title>
</head>
<body>

<a href="{{ url_for(‘search‘,q = ‘hello‘) }}">跳轉到搜索頁面</a>
</body>
</html>

在 app.py 中添加下列代碼:

from flask import request


@app.route(‘/search/‘)
def search():
    # 下行為獲取 get 請求的關鍵行
    q = request.args.get(‘q‘)

對於 index.html 中的

<a href="{{ url_for(‘search‘,q = ‘hello‘) }}">跳轉到搜索頁面</a>

此行代碼,點擊鏈接後的網址為
127.0.0.1/search/?q=hello
將該行代碼修改至如下內容後

<a href="{{ url_for(‘search‘,p = ‘hello‘) }}">跳轉到搜索頁面</a>

此行代碼,點擊鏈接後的網址為
127.0.0.1/search/?p=hello

  • 獲取 post 請求
    創建 html 文件 login.html,內容如下
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="{{ url_for(‘login‘) }}" method = "post">
    <table>
        <tbody>
            <tr>
                <td>用戶名: </td>
                <td><input type="text" placeholder="請輸入用戶名" name = "username"></td>
            </tr>
            <tr>
                <td>密碼:</td>
                <!--type 後的字段為該內容的字段類型-->
                <td><input type="password" placeholder="請輸入密碼" name = "password"></td>
            </tr>
            <tr>
                <td></td>
                <td><input type="submit" value="登陸"></td>
            </tr>
        </tbody>
    </table>
</form>
</body>
</html>

在 app.py 文件中添加下行代碼:

# methods 是 add_url_rule 文件中 rule 關鍵字需要獲取到的一個參數
# rule 被初始化為 rule = self.url_rule_class(rule, methods=methods, **options)
# GET 和 POST 則是固定兩種模式的關鍵字
@app.route(‘/login/‘,methods=[‘GET‘,‘POST‘])
def login():
    # request.method 方法能夠獲取到當前的模式
    if request.method == ‘GET‘:
        return render_template(‘login.html‘)
    else:
        username = request.form.get(‘username‘)
        password = request.form.get(‘password‘)
        print("username: ", username)
        print("password: ", password)
        return "login success"

線程隔離的 g 對象

g 的意思是 global
它是專門用於保存用戶數據的,在不同請求中,g 並不是同一個 g,換言之,當前請求完全結束時,當前的 g 也就銷毀了。簡而言之,g 的作用範圍,只在一個請求(即一個線程)中。
示例代碼以及文件如下:
app.py

from flask import Flask, g, request, render_template
from utils import login_log
app = Flask(__name__)


@app.route(‘/‘)
def hello_world():
    return ‘Hello World!‘

@app.route(‘/login/‘,methods=[‘GET‘,‘POST‘])
def login():
    if request.method == ‘GET‘:
        return render_template(‘login.html‘)
    else:
        username = request.form.get(‘username‘)
        password = request.form.get(‘password‘)
        if username == ‘Outro‘ and password == ‘123‘:
            # login_log(username)
            g.username = username
            login_log()
            return ‘登陸成功‘
        else:
            return ‘您的用戶名或者密碼輸入錯誤‘

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

utils.py

from flask import g

def login_log():
    print("當前登陸的用戶是: %s" % g.username)

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="" method="post">
    <table>
        <tbody>
            <tr>
                <td>用戶名:</td>
                <td><input type="text" name="username"></td>
            </tr>
            <tr>
                <td>密碼:</td>
                <td><input type="password" name="password"></td>
            </tr>
            <tr>
                <td></td>
                <td><input type="submit" value = "登陸"></td>
            </tr>
        </tbody>
    </table>
</form>
</body>
</html>

Flask 學習筆記