Flask養(yǎng)生

FLASK框架

簡介:

flask是一個非常小的python web框架 只提供了一個強勁的核心 其它都是通過第三方擴展庫來實現(xiàn)

安裝:

sudo pip install flask

虛擬環(huán)境

安裝虛擬環(huán)境 pip install virtualenv

創(chuàng)建自己的虛擬環(huán)境 virtualenv --no-site-packages 虛擬環(huán)境的名稱

啟動虛擬環(huán)境 source venv/bin/activate

退出虛擬環(huán)境 deactivate (退出以后 進入到的是全局的·python環(huán)境)

虛擬環(huán)境遷移

pip freeze
pip list
pip freeze > requirment.txt
pip freeze
pip list
pip install -r requirment.txt

解釋:虛擬環(huán)境 就是相當于自己新裝了一個python的解釋器 不受全局解釋器 中的包的影響(獨立) 如果在虛擬環(huán)境中 安裝了flask,在退出虛擬環(huán)境以后 使用不了 flask的 因為你的全局的python解釋器 中沒有安裝flask

一、完整的啟動

from flask import Flask #導入Flask

app = Flask(__name__) #__name__是必傳參數(shù)  flask類的實例化

#路由地址
#127.0.0.1:5000/
@app.route('/') #裝飾器路由
def index(): #視圖函數(shù)名稱
    return 'hello flask'


#只在當前主文件中啟動 flask
if __name__ == '__main__':
    app.run()  #運行flask

二、app.run() 啟動參數(shù)

參數(shù) 參數(shù)說明
debug 調試模式 默認為False 需要啟動改為True
port 端口號 默認5000
host 主機地址 默認127.0.0.1 ~ 127.255.255.254
threaded 線程 默認False True開啟多線程

實例:

#只在當前主文件中啟動 flask
if __name__ == '__main__':
    app.run(host='127.0.0.1',port=5001,debug=True,threaded=True)  #運行flask
    app.run(debug=True)  #開啟調試模式

三、視圖函數(shù)

(1) 無參路由

@app.route('/')
def index():
    return '首頁'

(2) 帶參路由

#訪問地址 127.0.0.1:5000/welcome/zhangsan/
@app.route('/welcome/<name>/')
def welcome(name):
    # return '必須有響應'
    return '歡迎'+name

(3) 傳遞多個參數(shù)

#帶多個參數(shù)
#http://127.0.0.1:5000/args/zhansan/18/
# @app.route('/args/<name>/<age>/')
# http://127.0.0.1:5000/args/zhansan_18/
@app.route('/args/<name>_<age>/')
def args(name,age):
    return '我叫{} 我今年{}歲了'.format(name,age)

(4) 限制參數(shù)的類型

@app.route('/demo/<int:age>/') #限制傳參類型為int
@app.route('/demo/<float:age>/') #限制傳參類型為float
@app.route('/demo/<string:age>/') #默認就是字符串
@app.route('/demo/<path:age>/') #把參數(shù)age位置的參數(shù)中的/不再認為是路由地址的分隔符,而是參數(shù)的一部分  屬于字符串類型的一部分
#/demo/zhangsan/a/b/c/
@app.route('/demo/<name>/<path:age>/') #把參數(shù)age位置的參數(shù)中的/不再認為是路由地址的分隔符,而是參數(shù)的一部分  屬于字符串類型的一部分
# http://127.0.0.1:5000/demo/zhangsan/a/b/c/
@app.route('/demo/<path:age>/<name>/') #把參數(shù)age位置的參數(shù)中的/不再認為是路由地址的分隔符,而是參數(shù)的一部分  屬于字符串類型的一部分
def demo(age,name):
    print(age,name)
    # print(type(age))
    return '我是測試的視圖'

注意:

  1. 每個路由地址結尾的/ 建議都加上 如果路由地址存在結尾的/ 瀏覽器會自動加上 如果沒有結尾的 / 請求時 存在/ 則請求失敗
  2. 傳參的格式 <參數(shù)名稱>
  3. 傳遞多個參數(shù) 使用/分隔符 或者_進行多個參數(shù)的拼接
  4. 參數(shù)傳遞進來都為字符串類型(默認)
  5. 限制參數(shù)類型 int/float/string/path

四、重定向

從一個視圖跳轉到另外一個視圖的操作

from flask import redirect,url_for

(1) redirect 通過路由地址跳轉

實例

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

@app.route('/args/<name>/<age>/')
def args(name,age):
    return name+age

@app.route('/redirect/')
def myRedirect():
    return redirect('/') #重定向到首頁
    return redirect(/args/zhansgan/18/)

(2) url_for 通過視圖函數(shù)名 反向解析出路由地址

實例

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

@app.route('/args/<name>/<age>/')
def args(name,age):
    return name+age

@app.route('/redirect/')
def myRedirect():
    print(url_for('index')) # / 
    print(url_for('indexx')) #  報錯 當前視圖函數(shù)不存在
    print(url_for('args',name='zhangsan',age=18) #/args/zhangsan/18/
    return redirect(url_for('index')) #重定向到首頁
    return redirect(url_for('args'name='zhangsan',age=18)) 

注意:

當url_for 構造不存在的視圖函數(shù)時 則報錯

當url_for 構造需要傳參的路由地址的時候 不存參則報錯

五、abort(http標準狀態(tài)碼) 終止

from flask import abort

  1. 消息
  2. 成功
  3. 重定向
  4. 請求錯誤
  5. 服務器錯誤

實例

@app.route('/')
def index():
    print('abort上方')
    # abort(404)
    abort(500)
    print('abort下方')
    # return 'index.html'
 
參數(shù)e接受錯誤的信息
    
#捕獲404狀態(tài)碼的錯誤信息
@app.errorhandler(404)
def page_not_found(e):
    print(e)
    # return 'page_not_found'
    return e
#捕獲500狀態(tài)碼的錯誤信息
@app.errorhandler(500)
def server_error(e):
    return 'server_error'

注意:

abort上面的代碼正常執(zhí)行 下面的代碼不再執(zhí)行 和raise一樣

六、響應 response

概述: 我們響應 可以給瀏覽器響應文字內容,html頁面的代碼 和 狀態(tài)碼 創(chuàng)建cookie

(1) 使用return 文字內容

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

(2) 響應內容并指定響應的狀態(tài)碼

@app.route('/')
def index():
    return 'index.html',404

(3) 通過make_response 構造響應

from flask imoprt make_response

@app.route('/make_response/')
def makeResponse():
    # res = make_response('make_response響應的內容')
    res = make_response('make_response響應的內容',404)
    return res

七、cookie 會話控制

協(xié)議:http

http:超文本傳輸協(xié)議 無狀態(tài)協(xié)議

(1) 設置cookie 不設置過期時間

注意: 默認存活時間為 瀏覽會話結束(關閉瀏覽器)

主體結構

Response.set_cookie(

? key,

? value,

? max_age=None #設置過期時間 單位為秒數(shù)

? expires =None, #設置過期時間 為時間戳的秒數(shù)

? path = '/' #設置當前cookie存儲為全局

)

實例

#cookie的操作
@app.route('/set_cookie/')
def set_cookie():
    res = make_response('設置cookie')
    res.set_cookie('name','zhangsan')
    return res

(2) 獲取cookie

@app.route('/get_cookie/')
def get_cookie():
    # print(request.cookies['name']) #獲取請求過來的cookie
    # print(request.cookies.get('name')) #獲取請求過來的cookie
    return 'cookie中name的值為{}'.format(request.cookies.get('name','沒有值'))

(3) 刪除cookie

@app.route('/del_cookie/')
def del_cookie():
    res = make_response('刪除name的cookie')
    res.delete_cookie('name')
    return res

(4) 設置cookie 并設置過期時間

#cookie的操作
@app.route('/set_cookie/')
def set_cookie():
    res = make_response('設置cookie')
    # res.set_cookie('name','zhangsan') #不舍值過期時間
    # res.set_cookie('name','zhangsan',max_age=60) #max_age 存活的秒數(shù)
    lift_time = time.time()+40 #設置40秒以后的時間戳
    res.set_cookie('name','zhangsan',expires=lift_time) #expires 值為存活的時間戳
    return res

八、session

概述:服務器要識別 不同的用戶的請求 根據(jù)cookie攜帶著的唯一session_id 值 來進行區(qū)分不同用戶的請求

所以session基于cookie 如果cookie被禁用了 session也不能使用

session加密需要加密種子 secret_key 進行加密

(1) 設置session

注意: 不設置過期時間 默認為瀏覽會話結束

@app.route('/set_session/')
def index():
    session['username'] = '張三'
    return '設置set_session'

(2) 獲取session

@app.route('/get_session/')
def get_session():
    return '獲取session 值為{}'.format(session.get('username'))

(3) 刪除session

@app.route('/del_session/')
def del_session():
    session.pop('username') #刪除key為username的數(shù)據(jù)
    # session.clear() #刪除全部
    return '刪除session'

(4) 設置session及過期時間

@app.route('/set_session/')
def index():
    session.permanent = True #持久化存儲
    app.permanent_session_lifetime = timedelta(minutes=5) #設置存活時間為5分鐘
    session['username'] = '張三'
    return '設置set_session'

day2

一、request對象

瀏覽器帶著用戶的請求 經由flask框架創(chuàng)建出 request對象 request對象 包含當前用戶的所有的請求信息

導入

from flask import request

request對象屬性

  1. url 完整的url請求

  2. base_url 去掉get傳參的url

  3. host_url 只有主機地址和端口號

  4. path 裝飾中的路由地址

  5. method 請求方法

  6. args 獲取get傳參

    request.args.get(key) 只能獲取唯一key的值 如果key存在重復 則獲取不到其它值

    request.args.getlist(key) 獲取key以列表形式返回(拿到所有的value)

  7. form 獲取表單post傳遞過來的數(shù)據(jù)

  8. files 獲取文件上傳的數(shù)據(jù)

  9. headers 獲取請求頭信息

  10. cookies 獲取所有的cookie

  11. json 獲取json數(shù)據(jù)

二、flask-script擴展

概念: 就是一個flask終端運行的解析器 因為在項目完成以后 就不應該有任何的改動 所有通過終端 添加不同的參數(shù) 來進行不同的啟動項

安裝

sudo pip3 install flask-script

使用

from flask_script import Manager
app = Flask(__name__)
manager= Manager(app)

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

啟動命令

python3 文件名稱.py runserver -d -r -h127.0.0.1 -p5000 --threaded

python3 manage.py runserevr -d -r #開啟debug和自動加載

三、藍本 blueprint

概念: 將視圖拆分到不同的文件中 藍本就是解決這個問題的

導入

from flask import Blueprint

user.py

from flask import Blueprint,request #導入藍本
user = Blueprint('user',__name__)

@user.route('/login/')
def login():
    return '登陸'

manage.py

from user import user #導入user藍本對象
#注冊藍本
app.register_blueprint(user) #127.0.0.1:5000/login/
#設置當前藍本的前綴
# app.register_blueprint(user,url_prefix='/user') #127.0.0.1:5000/user/login/

藍本中的重定向

index->login

@app.route('/')
def index():
    # return redirect(url_for('login')) #錯誤寫法 報錯
    return redirect(url_for('user.login')) #user藍本中的login視圖函數(shù)

四、請求鉤子函數(shù)

類Django框架的中間件

在主程序中使用

鉤子函數(shù) 函數(shù)說明
before_first_request 第一次請求之前
before_request 每次請求之前
after_request 每次請求之后
teardown_request 每次請求之后 即使有異常出現(xiàn)

實例

#第一次請求之前
@app.before_first_request
def before_first_request():
    print('before_first_request')
#每次請求之前
@app.before_request
def before_request():
    if request.path == '/form/' and request.method == 'GET':
        abort(404)
    print('before_request')
#每次請求之后
@app.after_request
def after_request(res):
    print('after_request')
    return res
#每次請求之后
@app.teardown_request
def teardown_request(res):
    print('teardown_request')
    return res

在藍本中使用

鉤子函數(shù) 函數(shù)說明
before_app_first_request 第一次請求之前
before_app_request 每次請求之前
after_app_request 每次請求之后
teardown_app_request 每次請求之后 即使有異常出現(xiàn)

實例

#在藍本中

#第一次請求之前
@user.before_app_first_request
def before_first_request():
    print('before_first_request')
#每次請求之前
@user.before_app_request
def before_request():
    if request.path == '/form/' and request.method == 'GET':
        abort(500)
    print('before_request')
#每次請求之后
@user.after_app_request
def after_request(res):
    print('after_request')
    return res
#每次請求之后
@user.teardown_app_request
def teardown_request(res):
    print('teardown_request')
    return res

模板引擎

模板引擎: 就是一定規(guī)則 書寫 和 替換 最終展示成完整的html文件 響應給用戶

模板引擎: jinja2

目錄結構

project/
    templates/ 模板目錄
    manage.py

(1) flask中渲染模板的函數(shù)

導入

from flask import render_template,render_template_string

render_template() #將模板渲染后響應給用戶

@app.route('/')
def index():
    # return render_template('index.html')
    print(render_template('index.html')) #render_templates將模板代碼渲染以后 響應給用戶
    return 'index.html'

render_template_string() #渲染字符串 響應給用戶

@app.route('/')
def index():
    return render_template_string('世界上最牛的字是哪一個?<span style="font-size:16px;color:red;">昊</span>')

(2) 模板中 分為倆大類

  1. 變量

    {{ 變量名稱 }}

    注意:

    ? 如果在模板中存在變量 且視圖函數(shù)沒有傳遞 則插入空白字符(什么都沒有) 不報錯

    實例

    @app.route('/')
    def index():
        return render_template('index.html',title='首頁')
    
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>{{ title }}</title>
    </head>
    <body>
    <h1>首頁</h1>
    </body>
    </html>
    
  1. 標簽

    {% 標簽名稱 %}

(3) 過濾器

根據(jù)管道符 | 對傳遞的值進行過濾

  1. abs 絕對值

    val|abs
    
  2. default 默認值

    默認情況 只有給定的變量不存在則執(zhí)行 默認值

    {{ var|default('默認值') }}

    {{ False|default('默認值') }} 不走

    {{ 不存在的變量|default('默認值') }} 執(zhí)行默認值

    設置default執(zhí)行flase的默認值

    {{ var|default('默認值',boolean=True) }}

  3. first 返回序列中的第一個

  4. last 返回序列中的最后一個值

  5. format 格式化字符串的輸出

    <li>{{ "我叫%s 我今年%d歲了 存款為%.2f元"|format('張三',20,1234) }}</li>
    
  6. length 長度

  7. join 拼接字符串

  8. safe 不轉義代碼

  9. int 轉換為整形

  10. float 轉換為浮點形

  11. list 轉換為列表

  12. lower 轉換小寫

  13. upper 轉換為大寫

  14. replace 替換

  15. trim 去除倆測空白字符

  16. striptags 去除html標簽

使用的安裝

sudo pip3 install 模塊名稱

  1. flask
  2. flask-script
  3. flask-moment
  4. flask-cache
  5. flask-uploads
  6. flask-wtf
  7. flask-bootstrap
  8. flask-sqlalchemy
  9. pillow
  10. pymysql
  11. flask-mail
  12. flask-migrate

(4) 標簽

格式{% tag %}

if 標簽

if...elif..else

{% if 90<=data.grade<=100 %}
<p>優(yōu)</p>
{% elif 80<=data.grade %}
<p>良</p>
{% elif 70<=data.grade %}
<p>中</p>
{% elif 60<=data.grade %}
<p>合格</p>
{% else %}
<p>不及格</p>
{% endif %}

for 標簽

{% for i in ... %}

? 循環(huán)體

{% else %}

? ...

{% endfor %}

<ol>
{#    {% for i in range(10) %}#}
    {% for i in xx %}
     {% for k,v in data.items() %}  #迭代字典
        <li>{{ i }}</li>
    {% else %}
        <li>你是否能看到我</li>
    {% endfor %}
</ol>

注意:

當?shù)淖兞?不存在 則執(zhí)行else

獲取迭代的狀態(tài)

變量 描述
loop.index 當前迭代的索引 從1開始
loop.index0 當前迭代的索引 從0開始
loop.first 是否為第一次迭代
loop.last 是否為最后一次迭代
loop.length 迭代的長度

(5) 文件的包含 include

格式

{% include "路徑/文件.html" %}

把文件引入到當前的位置 實現(xiàn)模板代碼的復用

實例

header.html

<header>
    頭部
</header>

footer.html

<footer>
    尾部
</footer>

test.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
{#{% include 'index.html' %}#}
{% include 'common/header.html' %}
<div>content內容部分</div>
{% include 'common/footer.html' %}
</body>
</html>

注意:

include 會將導入的代碼 相當于粘貼到了 你導入的位置上 導入文件的所有代碼都會在導入的位置顯示出來

(6) 宏 macro

概述

宏相當于 python中的函數(shù) 需要定義 調用 傳參

主體結構

{% macro 宏名稱([參數(shù)...]) %}
    內容
{% endmacro %}

調用

{{ 宏的名稱([參數(shù)...]) }}

導入

在common下創(chuàng)建pubulicform.html文件

在form.html中導入

實例

from ... import ...

{% from 'common/publicform.html' import form %}
{{ form('用戶名','text','username') }}
{{ form('密碼','password','userpass') }}
{{ form('','submit','','提交') }}

import ...

{% import 'common/publicform.html' as publicform %}
{{ publicform.form('用戶名','text','username') }}
{{ publicform.form('密碼','password','userpass') }}
{{ publicform.form('','submit','','提交') }}

注意

  1. 宏的調用只能在宏的下方來調用
  2. 宏如果有形參 且沒有默認值 則可以不傳實參
  3. 宏如果沒有形參 則不可以傳實參
  4. 宏的形參默認值 和python一樣
  5. 宏的默認值 遵循默認值規(guī)則 有默認值的放在右側 沒有默認值的 放在左側
  6. 關鍵字的參數(shù)使用 同python函數(shù)一樣

day3

一、模板的繼承 extends

概念: flask中的模板繼承 可以通過繼承 將很多重復的元素 抽離出來 放在父模板中 通過模板中 的extends,block 實現(xiàn)父模板繼承和替換的使用

語法

{% extends '父模板的名稱.html' %}

{% block 替換的名稱 %} 要替換的內容部分 {% endblock %}

自定義一個base.html模板
<!DOCTYPE html>
<html lang="en">
{% block header %}
<head>
    {% block meta %}
        <meta charset="UTF-8">
    {% endblock %}
    <title>{% block title %}base{% endblock %}</title>
    <style>
    {% block headercss %}
        div{
            font-size: 18px;
            color: aqua;
        }
    {% endblock %}
    </style>

    {% block linkscript %}
    {% endblock %}
</head>
{% endblock %}
<body>
<header>
    頭部
</header>
<div id="content">
    {% block content %}
        主體內容部分
    {% endblock %}
</div>
<footer>
    底部
</footer>
</body>
</html>

子模板 index.html

{% extends 'common/base.html' %}
{% block title %}
首頁
{% endblock %}
{% block headercss %}
    {{ super() }}
    ul,li{
       list-style:none;
        margin:0;
        padding:0;
    }
    li{
        width:100px;
        height:100px;
        border-radius:50px;
        background-color:yellow;
    }
     #content{
        color:red;
    }
{% endblock %}
{% block content %}
    <h1>首頁內容</h1>
    <ul>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
    </ul>
{% endblock %}
{# 以下代碼不會被加載 #}
{#{% block newblock %}#}
{#    <h1>首頁內容</h1>#}
{#{% endblock %}#}
{#sdasjdjsaldjaslkdalksjlksajdlasjdalskdjalkdsaskl#}

注意:

不可以在base模板block 以外的位置 添加任何的代碼 因為都不會被加載

{{ super() }} 將base.html模板中替換的代碼在調用(復用)

二、flask-bootstrap flask的bootstrap第三方擴展庫

安裝:

sudo pip3 install flask-bootstrap

使用

自定義base.html

{% extends 'bootstrap/base.html' %}
{% block title %}
    boot-base 更改自己的base
{% endblock %}
{% block navbar %}
    <nav class="navbar navbar-inverse" style="border-radius:0px;">
        <div class="container-fluid">
            <!-- Brand and toggle get grouped for better mobile display -->
            <div class="navbar-header">
                <button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
                        data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <a class="navbar-brand" href="#"><span class="glyphicon glyphicon-eye-open"
                                                       aria-hidden="true"></span></a>
            </div>

            <!-- Collect the nav links, forms, and other content for toggling -->
            <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                <ul class="nav navbar-nav">
                    <li class="active"><a href="#">首頁<span class="sr-only">(current)</span></a></li>
                    <li><a href="#">發(fā)表帖子</a></li>

                </ul>

                <ul class="nav navbar-nav navbar-right">
                    <form class="navbar-form navbar-left">
                        <div class="form-group">
                            <input type="text" class="form-control" placeholder="Search">
                        </div>
                        <button type="submit" class="btn btn-default">Submit</button>
                    </form>
                    <li><a href="#">登錄</a></li>
                    <li><a href="#">注冊</a></li>
                    <li class="dropdown">
                        <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
                           aria-expanded="false">個人中心 <span class="caret"></span></a>
                        <ul class="dropdown-menu">
                            <li><a href="#">Action</a></li>
                            <li><a href="#">Another action</a></li>
                            <li><a href="#">Something else here</a></li>
                            <li role="separator" class="divider"></li>
                            <li><a href="#">Separated link</a></li>
                        </ul>
                    </li>
                </ul>
            </div><!-- /.navbar-collapse -->
        </div><!-- /.container-fluid -->
    </nav>
{% endblock %}
{% block content %}
    <div class="container">
        {% block pagecontent %}
            <p>少小離家老大回</p>
            <p>安能辯我是雌雄</p>
        {% endblock %}
    </div>
{% endblock %}

三、錯誤頁面定制

manage.py

#錯誤頁面定制
@app.errorhandler(404)
def page_not_found(e):
    return render_template('error.html',code=404,e=e,info='您訪問的頁面被外星人抓走了')

@app.errorhandler(500)
def page_not_found(e):
    return render_template('error.html',code=500,e=e,info='我們內部有些事情需要解決 請稍后訪問')

error.html

{% extends 'common/boot-base.html' %}
{% block title %}
{{ code }}頁面
{% endblock %}
{% block pagecontent %}
    <h3>{{ info }}</h3>
    <div class="alert alert-warning" role="alert"><h4>{{ code }}</h4>{{ e }}</div>
{% endblock %}

四、靜態(tài)資源

創(chuàng)建static靜態(tài)資源目錄

目錄結構

project/
    templates/
        common/
        main/
            index.html
        ....
    static/
        img/
        css/
        js/
        upload/
    manage.py

實例

{% block pagecontent %}
    <h3>{{ info }}</h3>
    <div class="alert alert-warning" role="alert"><h4>{{ code }}</h4>{{ e }}</div>
    <img src="{{ url_for('static',filename='img/timg.jpg',_external=True) }}" alt="">
{% endblock %}

五、視圖傳遞多個參數(shù)

(1) 原始傳參

@app.route('/')
def index():
    return render_template('index.html',arg1=v1,arg2=v2...)

(2) 使用字典

@app.route('/')
def index():
    return render_template('index.html',data={'arg':'v1','arg2':'v2'...})

(3) 使用**

@app.route('/')
def index():
    data={'arg':'v1','arg2':'v2'...}
    return render_template('index.html',**data)

(4) locals()

以字典的形式獲取局部變量

@app.route('/')
def index():
    return render_template('index.html',**locals())變量

表單

六、flask原生表單

form1.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>原生表單</title>
</head>
<body>
<form action="{{ url_for('check_form',_external=True) }}" method="post">
    <p>用戶名 <input type="text" name="username"></p>
    <p>密碼 <input type="password" name="userpass"></p>
    <p><input type="submit" value="submit"></p>
</form>
</body>
</html>

manage.py

#跳轉到登錄的表單頁
@app.route('/login/')
def login():
    return render_template('form1.html')

#表單提交過來處理的視圖函數(shù)
@app.route('/check_form/',methods=['POST'])
def check_form():
    print(request.form.get('username'))
    print(request.form.get('userpass'))
    return '提交過來了'

倆個合并為同一個

#表單提交過來處理的視圖函數(shù)
@app.route('/login/',methods=['GET','POST'])
def check_form():
    if request.method == 'POST':
        print(request.form.get('username'))
        print(request.form.get('userpass'))
    return render_template('form1.html')

七、flask-wtf

概述: 是一個用于處理flask表單的擴展庫 提供了表單校驗 csrf等功能

安裝

sudo pip3 install flask-wtf

csrf_token 的生成 需要依賴flask的secret_key

python中常量:一般用于配置文件的設置 變量的大寫 在代碼運行過程中 不能被修改和銷毀的量 稱之為常量(python中沒有)

(1) 字段屬性

字段類型 字段說明
StringField 普通文本字段
SubmitField 提交按鈕
PasswordField 密碼框
HiddenField 隱藏域
TextAreaField 多行文本域
DateField 日期
DateTimeFIeld 時間和日期
IntegerField 整數(shù)
FloatField 浮點數(shù)
BooleanField 布爾字段
RadioFIeld 單選框
SelectField 下拉
FileField 文件上傳

(2) 驗證器

驗證器 驗證器說明
DataRequired 必填
Email 郵箱
IPAddress ip地址
Length 長度
NumberRang 值的范圍
EqualTo 驗證倆個字段的值是否相同
URL 驗證url地址
Regexp 正則驗證

實例

form2.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="" method="post">
    {#  添加csrf——token驗證  #}
    <p>{{ form.csrf_token }}</p>
    <p>{{ form.username.label() }}</p>
    <p>{{ form.username() }}
        {#    顯示錯誤信息的判斷    #}
        {% if form.username.errors %}
            {{ form.username.errors.0 }}
        {% endif %}
    </p>
    <p>{{ form.userpass.label() }}</p>
    <p>{{ form.userpass() }}
        {#    顯示錯誤信息的判斷    #}
        {% if form.userpass.errors %}
            {{ form.userpass.errors.0 }}
        {% endif %}
    </p>
    <p>{{ form.submit() }}</p>
</form>
</body>
</html>

manage.py

from flask import Flask,render_template,request
from flask_script import Manager
from flask_bootstrap import Bootstrap
from flask_wtf import FlaskForm # 導入表單類的基類
from wtforms import StringField,PasswordField,SubmitField #導入字段類型
from wtforms.validators import DataRequired,Length #導入驗證器

app = Flask(__name__)
# app.secret_key = 'abcd'
app.config['SECRET_KEY'] = 'abcd'
app.config['BOOTSTRAP_SERVE_LOCAL'] = True #加載本地樣式
bootstrap = Bootstrap(app)
manager = Manager(app)

#自定義表單類
class Login(FlaskForm):
    #username 為 name=username 用戶名label標簽和內容  validators驗證器
    username = StringField('用戶名',validators=[DataRequired(message='用戶名不能為空')])
    userpass = PasswordField('密碼',validators=[DataRequired(message='密碼不能為空'),Length(min=6,max=12,message='密碼長度為6~12位')])
    submit = SubmitField('登錄')

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


@app.route('/login/',methods=['GET','POST'])
def login():
    form = Login() #實例化自定義表單類
    if form.validate_on_submit(): #如果csrf_token和數(shù)據(jù)驗證都通過則為真
        return '數(shù)據(jù)提交過來了'
    return render_template('form2.html',form=form)


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

day5 MODEL

flask作為一款MVT框架 也有自己的ORM flask-sqlalchemy

安裝: sudo pip3 install flask-sqlalchemy

使用ORM原因:

當項目越來越大的時候 如果采用原生SQL的方式 就會有如下問題

  1. 出現(xiàn)大量的原生SQL,如果條件越多 代碼越長 并且重復使用率低
  2. 很多的sql語句 都是在業(yè)務邏輯中拼接出來的 如果數(shù)據(jù)庫需要更改 就要去修改這些sql的邏輯 很容易漏掉某些sql的修改
  3. 寫原生sql容易忽略安全問題

使用ORM好處:

  1. 易用性 使用ORM做數(shù)據(jù)庫的開發(fā) 可以有效的減少重復SQL的概率 寫出來的代碼 也更加的清晰直觀
  2. 設計靈活 可以輕松的寫出sql語句
  3. 可移植性

一、原生sql

(1) 創(chuàng)建一個庫

create database if not exists bj1805 character set utf8;

(2) 創(chuàng)建表

create table if not exists user(

id int primary key auto_increment,

username varchar(20) not null default 'xxx',

sex tinyint default 1

)

(3) 導入 sqlalchemy

from sqlalchemy import create_engine

(4) 配置 URI 鏈接地址

DB_URI = 'mysql+pymysql:// 用戶名:密碼@主機名:端口號/數(shù)據(jù)庫名稱'

實例

from sqlalchemy import create_engine

HOSTNAME = '127.0.0.1'
USERNAME = 'root'
PASSWORD = '123456'
PORT = 3306
DATABASE = 'bj1805'

DB_URI = 'mysql+pymysql://{}:{}@{}:{}/{}'.format(USERNAME,PASSWORD,HOSTNAME,PORT,DATABASE)

engine = create_engine(DB_URI)

#鏈接數(shù)據(jù)庫
with engine.connect() as con:
    #創(chuàng)建表
    con.execute('create table test(username varchar(20))')
    con.execute('insert into test values("xxxx")')

二、flask-sqlalchemy的使用

(1) 導入

from flask_sqlalchemy import SQLalchemy

(2) 配置

app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:123456@127.0.0.1:3306/bj1805'
#是否追蹤數(shù)據(jù)發(fā)生的改變 占用額外資源  將值改為True或者Flase進行禁用
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

(3) 實例化

db = SQLalchemy(app)

三、設計模型

字段類型

類型名 python中的類型 類型說明
Integer int 存儲整形
SmallInteger int 小整形
BigInteger int 長整形
Float float 浮點形
String str varchar類型
Text str text長文本類型
Boolean bool tinyint 整形
Date datetime.date 日期
Time datetime.time 時間
DateTime datetime.datetime 日期和時間

可選約束條件

選項 說明
primary_key 默認False True創(chuàng)建主鍵索引
unique 唯一索引
index 常規(guī)索引
nullable 是否可以為null 默認True
default 默認值

實例:

#創(chuàng)建模型
class User(db.Model):
    __tablename__ = 'user' #表名默認為類名 給當前模型起表名
    #id 字段名稱 db.Column  整形 主鍵索引
    id = db.Column(db.Integer,primary_key=True)
    username = db.Column(db.String(20),index=True)
    age = db.Column(db.Integer)
    sex = db.Column(db.Boolean,default=True) #default默認值 并不是修改表結構 而是在插入數(shù)據(jù)的時候 沒有給值 則將默認值插入
    info = db.Column(db.String(100))
    #添加構造方法
    def __init__(self,username='',age=18,sex=True,info='默認值'):
        self.username = username
        self.age = age
        self.sex = sex
        self.info = info

創(chuàng)建模型

@app.route('/create_all/')
def create_all():
    db.create_all()
    return '創(chuàng)建表'

刪除模型

#刪除表  只刪除和模型類重名的表
@app.route('/drop_all/')
def drop_all():
    db.drop_all()
    return '刪除表'

四、增刪改查

(1) 添加一條數(shù)據(jù) add

#添加數(shù)據(jù)
@app.route('/insert_one/')
def insert_one():
    # u = User(username='張三',age=18,info='張三的個人簡介')
    try:
        u = User('李四',20,False,'李四的個人簡介')
        db.session.add(u)
        db.session.commit() #提交
    except:
        db.session.rollback() #回滾
    return '添加一條數(shù)據(jù)'

(2) 添加多條數(shù)據(jù) add_all

#添加多條數(shù)據(jù)
@app.route('/add_many/')
def add_many():
    u1 = User('王五',30)
    u2 = User('趙六',40)
    db.session.add_all([u1,u2])
    db.session.commit() #提交
    return '添加多條數(shù)據(jù)'

(3) 修改

#修改1
@app.route('/update/')
def update():
    u = User.query.get(1) #查詢id為1的數(shù)據(jù)對象
    # print(u)
    u.info = '堅持到無能為力'
    db.session.add(u)
    db.session.commit()
    return '修改'

(4) 刪除 delete

#刪除
@app.route('/delete/')
def delete():
    u = User.query.get(5)
    db.session.delete(u) #刪除
    db.session.commit()
    return '刪除'

五、拆分MVT(簡單的拆分)

project/
    App/
        __init__.py
        views/
        models/
    settings.py #配置文件
    exts.py #extensions 加載第三方擴展庫
    manage.py

六、設置提交方式

(1) 設置為自動提交

app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True

不需要在db.session.commit()

(2) 自定義增刪的基礎類

class Base:
    #自定義添加一條數(shù)據(jù)的方法
    def save(self):
        try:
            db.session.add(self)
            db.session.commit()
        except:
            db.session.rollback()

    #自定義添加多條的方法
    @staticmethod
    def save_all(*args):
        try:
            db.session.add_all(args)
            db.session.commit()
        except:
            db.session.rollback()
    #自定義刪除方法
    def delete(self):
        try:
            db.session.delete(self)
            db.session.commit()
        except:
            db.session.rollback()

使用

#創(chuàng)建模型
class User(Base,db.Model):

views.py

@view.route('/insert_one/')
def insert_one():
    # u = User('JQKA',16,False)
    # db.session.add(u)
    # db.session.commit()
    sexList = [True,False]
    username = random.randrange(11111, 99999)
    u1 = User(username, random.randint(1, 70), sexList[random.randint(0, 1)], str(username) + '的個人說明')
    u1.save() #添加
    return '添加數(shù)據(jù)'


@view.route('/insert_many/')
def insert_many():
    sexList = [True,False]
    # sexList[random.randint(0,2)]
    username = random.randrange(11111,99999)
    u1 = User(username,random.randint(1,70),sexList[random.randint(0,1)],str(username)+'的個人說明')
    username = random.randrange(11111, 99999)
    u2 = User(username, random.randint(1, 70), sexList[random.randint(0,1)], str(username) + '的個人說明')
    # db.session.add_all([u1,u2])

    User.save_all(u1,u2) #添加多條
    return '添加多條數(shù)據(jù)'

@view.route('/delete/')
def delete():
    User.query.get(12).delete()
    return '刪除'

七、數(shù)據(jù)庫操作

查詢集

查詢數(shù)據(jù)的集合

分類

  1. 原始查詢集

    就是通過類名.query得到的就是原始查詢集

  2. 數(shù)據(jù)查詢集

    通過過濾的方法 最終拿到的數(shù)據(jù)就三數(shù)據(jù)查詢集

(1) all() 得到所有數(shù)據(jù)查詢集 返回列表

類名.query.all()

@view.route('/all/')
def all():
    data = User.query.all()
    return render_template('show.html',data = data)

(2) filter() 過濾 默認沒有條件 返回所有

類名.query.filter([類名.屬性名 比較運算符 值。。。])

@view.route('/filter/')
def filter():
    # data = User.query.filter() #返回所有
    data = User.query.filter(User.age<60,User.age>10) #查詢 年齡在59-11之間
    return render_template('show.html',data=data)

(3) filter_by() 只支持單條件查詢

沒有條件 則返回所有

屬性=值,,,,,,

類名.query.filter_by(username='張三'....)

@view.route('/filter_by/')
def filter_by():
    # data = User.query.filter_by() #返回所有
    data = User.query.filter_by(sex=True) #查詢性別為True的所有數(shù)據(jù)
    return render_template('show.html',data=data)

(4) offset(num) 偏移量

@view.route('/offset/')
def offset():
    data = User.query.offset(5) #偏移5條數(shù)據(jù) 從第六條開始取
    return render_template('show.html',data=data)

(5) limit(num) 取值

@view.route('/limit/')
def limit():
    data = User.query.limit(5) #取出5條數(shù)據(jù)
    return render_template('show.html',data=data)

(6) order_by() 排序

默認升序

-類名.屬性名

@view.route('/order_by/')
def order_by():
    # data = User.query.order_by(User.age) #按照年齡升序
    data = User.query.order_by(-User.age) #按照年齡降序
    return render_template('show.html',data=data)

(7) first() 第一條

類名.query.first()

@view.route('/first/')
def first():
    # data = User.query.first() #取出第一條數(shù)據(jù)
    data = User.query.order_by(-User.age).first() #取出第一條數(shù)據(jù)
    print(data)
    return 'first'

(8) get(id值) 根據(jù)ID值進行查詢

查詢成功返回 對象

查詢失敗 返回None

@view.route('/get/')
def Get():
    data = User.query.get(1)
    print(data)
    return 'get'

(9) contains() 包含關系

@view.route('/contains/')
def contains():
    data = User.query.filter(User.username.contains('9')) #查詢包含數(shù)字9的數(shù)據(jù)
    return render_template('show.html',data=data)

(10) like 模糊查詢

@view.route('/like/')
def like():
    # data = User.query.filter(User.username.like('%9%')) #包含9的數(shù)據(jù)
    # data = User.query.filter(User.username.like('%4')) #4作為結尾
    data = User.query.filter(User.username.like('5%')) #5作為開頭
    return render_template('show.html',data=data)

(11) startswith 以...開頭 endswith 以...結尾

@view.route('/with/')
def With():
    # data = User.query.filter(User.username.startswith('9')) #以9作為開頭
    data = User.query.filter(User.username.endswith('4')) #以4作為結尾
    return render_template('show.html',data=data)

(12) 比較運算符

  1. __gt__大于
  2. __lt__ 小于
  3. __ge__ 大于等于
  4. __le__ 小于等于
  5. >=
  6. <=
  7. ==
  8. !=
  9. >
  10. <

實例

@view.route('/bj/')
def bj():
    #年齡 大于30  and  年齡小于50
    data = User.query.filter(User.age.__gt__(30),User.age.__lt__(50))
    return render_template('show.html',data=data)

(13) in 和 not in 在...范圍內 不再 ...范圍內

@view.route('/in/')
def In():
    #查詢id在 1,2,3,4,12,13,14范圍內的數(shù)據(jù)
    # data = User.query.filter(User.id.in_([1,2,3,4,12,13,14]))
    # 查詢id不在 1,2,3,4,12,13,14范圍內的數(shù)據(jù)
    data = User.query.filter(~User.id.in_([1,2,3,4,12,13,14]))
    return render_template('show.html',data=data)

(14) null 空

@view.route('/null/')
def null():
    #查詢username為null的數(shù)據(jù)
    # data = User.query.filter(User.username.is_(None))
    # data = User.query.filter(User.username==None)

    #查詢部位null的數(shù)據(jù)
    # data = User.query.filter(User.username.isnot(None))
    # data = User.query.filter(~User.username.is_(None))
    data = User.query.filter(User.username!=None)

(15) and_ 邏輯與查詢

from sqlalchemy import and_

默認為and查詢

@view.route('/and/')
def And():
    # data = User.query.filter(User.sex==True,User.id<10)
    data = User.query.filter(and_(User.sex==True,User.id<10))
    return render_template('show.html',data=data)

(16) or_ 邏輯或

from sqlalchemy import or_

@view.route('/or/')
def Or():
    data = User.query.filter(or_(User.sex==True,User.id<10))
    return render_template('show.html',data=data)

(17) and和 or的操作

@view.route('/or/')
def Or():
    data = User.query.filter(and_(User.username.contains('9')),or_(User.sex==True,User.id<10))
    #select * from user where username like '%9%' and (sex=1 or id<10)
    return render_template('show.html',data=data)

(17) not_ 邏輯非

from sqlalchemy import not_

@view.route('/not/')
def Not():
    #只能一個條件
    data = User.query.filter(not_(User.sex==True))
    return render_template('show.html',data=data)

(18) count 統(tǒng)計

@view.route('/count/')
def count():
    #統(tǒng)計數(shù)據(jù)條數(shù)
    data = User.query.count()
    print(data)
    return 'count'
    # return render_template('show.html',data=data)

一、文件遷移

(1) 安裝

sudo pip3 install flask-migrate

sudo pip3 install flask-script

(2) 使用

from flask_script import Manager
from flask_migrate import Migrate,MigrateCommand

migrate = Migrate(app) #實例化遷移對象
manager = Manager(app)
manager.add_command('db',MigrateCommand) #添加遷移命令
...
if __name__ == '__main__':
    manager.run()

(3) 生成遷移目錄

python3 manage.py db init

此刻會在項目中生成一個叫migrations的目錄

(4) 生成遷移文件

python3 manage.py db migrate

(5) 執(zhí)行遷移文件(更新數(shù)據(jù)庫)

python3 manage.py db upgrade

三、拆分MVT的

blog/
    App/
        static/     #靜態(tài)目錄
            img/
            css/
            js/
            upload/
        templates/  #模板目錄
            common/
                base.html
        models/     #模型目錄
            __init__.py
        views/      #視圖目錄
            __init.py   
        forms/      #表單目錄
            __init__.py
        settings.py #配置文件
        extensions.py   #加載第三方擴展庫
        email.py    #郵件
        __init__.py #App包的初始化
    migrations/     #遷移目錄
    venv/           #虛擬環(huán)境
    manage.py       #啟動文件

分頁顯示博客

paginate 分頁類  返回一個分頁對象 pagination
paginate實例化傳參
    page    必須參數(shù) 當前頁碼
    per_page  每頁顯示數(shù)據(jù)的條數(shù)  默認20
    error_out 當查詢出錯的時候  是否報404的錯誤 默認為True
    
pagination對象
    屬性:
        items   當頁的所有數(shù)據(jù)
        page    當前頁碼
        pages   總頁碼是
        total   總數(shù)據(jù)條數(shù)
        per_page    每頁多少條
        prev_num    上一頁的頁碼
        next_num    下一頁的頁碼
        has_prev    是否有上一頁
        has_next    是否有下一頁
    方法:
        prev    上一頁的分頁對象
        next    下一頁的分頁對象
        iter_pages  是一個迭代器 返回所有的頁碼數(shù) 顯示不下則顯示 ...

緩存

安裝:

sudo pip3 install flask-cache

使用

from flask_cache import Cachce

cache = Cache(app,config={"CACHE_TYPE":"緩存類型"})

實例

extension.py

from flask_cache import Cache
cache = Cache(config={"CACHE_TYPE":'simple'})#實例化緩存 簡單的緩存
cache.init_app(app=app) #緩存

main.py

from App.extensions import cache

@main.route('/')
@cache.cached(timeout=120) #timeout 過期的時間
def index():
    print('你能看到我?guī)状?。?!?)

緩存類型

  1. @cache.cached(timeout=120) #只是緩存頁面
  2. @cache.memoize(timeout=120) #會根據(jù)不同傳參進行不同的緩存

改正之后的緩存

from flask import Blueprint,render_template,request,current_app,redirect,url_for
from App.models import Posts
from App.extensions import cache

main = Blueprint('main',__name__)

@main.route('/')
# @cache.cached(timeout=120)
def index():
    return redirect(url_for('main.main_cache',page=1))


@main.route('/show/<int:page>/')
@cache.memoize(timeout=120) #根據(jù)不同參數(shù)進行數(shù)據(jù)的緩存
def main_cache(page):
    print('你能看到我?guī)状?。?!?)
    # try:
    #     page = int(request.args.get('page', 1))
    # except:
    #     page = 1
    p = Posts.query.filter_by(pid=0).order_by(Posts.timestamp.desc()).paginate(page, current_app.config['CON_NUM'],False)
    data = p.items  # 當前頁的數(shù)據(jù)
    return render_template('main/index.html', data=data, pagination=p)

清除緩存的辦法

(1) 在settings.py中 配置緩存時間

CACHE_DEFAULT_TIME

(2) 給裝飾器添加timeout參數(shù) 時間秒

@cache.cached(timeout=120)
@cache.memoize(timeout=120) #根據(jù)不同參數(shù)進行數(shù)據(jù)的緩存

(3) 清除所有緩存

cache.clear()

(4) 只清除 memoize的緩存

cache.delete_memoized(緩存的視圖函數(shù)名稱)

(5) 只清除 cached的緩存

cache.delete(key_prefix 緩存前綴)

表多對多的使用

添加多對多數(shù)據(jù)

u1 = User.query.get(1)
p1 = Posts.query.get(1)
#1號用戶 收藏 1號帖子
u1.favorites.append(p1)

查看多對多數(shù)據(jù)

u1.favorites.all() #查看用戶1收藏了那些帖子
p1.users.all() #查看帖子被哪些用戶收藏

刪除多對多數(shù)據(jù)

u1 = User.query.get(1)
p1 = Posts.query.get(1)
u1.favorites.remove(p1)

上線部署

(1) 安裝

  1. 在要安裝項目的目錄創(chuàng)建虛擬環(huán)境

    virtualenv venv

  2. source activate # 開啟虛擬開發(fā)環(huán)境模式

  3. pip3 install uwsgi # 安裝uwsgi

(2) 配置

uwsgi配置文件支持很多格式,我采用.ini格式,命名為uconfig.ini具體內容如下:

[uwsgi]

# 外部訪問地址,可以指定多種協(xié)議,現(xiàn)在用http便于調試,之后用socket
socket = 0.0.0.0:8000 # uwsgi的監(jiān)聽端口

# 指向項目目錄
chdir =  /home/xlg/test/

# flask啟動程序文件
wsgi-file = manage.py

# flask在manage.py文件中的app名
callable = app

plugins = python# 這行一定要加上,不然請求時會出現(xiàn)-- unavailable modifier requested: 0 --錯誤提示

# 處理器數(shù)
processes = 1

# 線程數(shù)
threads = 2

#狀態(tài)檢測地址
stats = 127.0.0.1:9191

(3) 安裝 nginx

Nginx:sudo apt-get install nginx

Nginx是輕量級、性能強、占用資源少,能很好的處理高并發(fā)的反向代理軟件。Ubuntu 上配置 Nginx 也是很簡單,不要去改動默認的 nginx.conf 只需要將/etc/nginx/sites-available/default文件替換掉就可以了。
新建一個 default 文件:

server{
listen  80; # 服務器監(jiān)聽端口
        server_name 192.168.100.136; # 這里寫你的域名或者公網IP
        location / {
                uwsgi_pass      127.0.0.1:8000; # 轉發(fā)端口,需要和uwsgi配置當中的監(jiān)聽端口一致
                include uwsgi_params; # 導入uwsgi配置
                #uwsgi_param UWSGI_PYTHON /home/自己創(chuàng)建的目錄/venv; # Python解釋器所在的路徑(這里為虛擬環(huán)境)
                uwsgi_param UWSGI_PYTHON /usr/bin/python3;  
                uwsgi_param UWSGI_CHDIR  /home/xlg/test/;# # 自己創(chuàng)建的目錄 項目根目錄
                uwsgi_param UWSGI_SCRIPT manage:app; # 指定啟動程序
                #比如你測試用test.py文件,文件中app = Flask(name),那么這里就填 test:app
        }
}

服務啟動

  1. sudo service start
  2. sudo service stop
  3. sudo service restart

指定配置文件,后臺運行 uwsgi, 這時再刷新一下之前打開的頁面,就可以看到應用正常運行了。

uwsgi uconfig.ini

訪問地址

192.168.100.136

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容