flask彙總

flask框架

藍圖

隨着flask程序愈來愈複雜,咱們須要對程序進行模塊化的處理,以前學習過python的模塊化管理,因而針對一個簡單的flask程序進行模塊化處理html

Blueprint概念

  • 簡單來講,Blueprint 是一個存儲操做方法的容器,
    這些操做在這個Blueprint 被註冊到一個應用以後就能夠被調用,
    Flask 能夠經過Blueprint來組織URL以及處理請求。前端

    在Flask中Blueprint具備的屬性

  • Flask使用Blueprint讓應用實現模塊化
  • 一個應用能夠具備多個Blueprint
  • 在一個應用中,一個模塊能夠註冊屢次
  • Blueprint能夠單獨具備本身的模板、靜態文件或者其它的通用操做方法
  • 在一個應用初始化時,就應該要註冊須要使用的Blueprint
  • 一個Blueprint並非一個完整的應用,它不能獨立於應用運行,而必需要註冊到某一個應用中。python

    使用藍圖的步驟

    藍圖/Blueprint對象用起來和一個應用/Flask對象差很少,最大的區別在於一個 藍圖對象沒有辦法獨立運行,必須將它註冊到一個應用對象上才能生效mysql

  • 1,建立一個藍圖對象
    admin=Blueprint('admin',name)
    • 藍圖對象中的參數
      • 藍圖的url前綴
        • 當咱們在應用對象上註冊一個藍圖時,能夠指定一個url_prefix關鍵字參數(這個參數默認是/)
        • 在應用最終的路由表 url_map中,在藍圖上註冊的路由URL自動被加上了這個前綴,這個能夠保證在多個藍圖中使用相同的URL規則而不會最終引發衝突,只要在註冊藍圖時將不一樣的藍圖掛接到不一樣的自路徑便可
        • url_for

url_for('admin.index') # /admin/
* 註冊靜態路由
* 和應用對象不一樣,藍圖對象建立時不會默認註冊靜態目錄的路由。須要咱們在 建立時指定 static_folder 參數。
* 下面的示例將藍圖所在目錄下的static_admin目錄設置爲靜態目錄web

admin = Blueprint("admin",name,static_folder='static_admin')
app.register_blueprint(admin,url_prefix='/admin')
* 如今就可使用/admin/static_admin/ 訪問static_admin目錄下的靜態文件了
* 定製靜態目錄URL規則 :能夠在建立藍圖對象時使用 static_url_path 來改變靜態目錄的路由。下面的示例將爲 static_admin 文件夾的路由設置爲 /lib
* admin = Blueprint("admin",name,static_folder='static_admin',static_url_path='/lib')
app.register_blueprint(admin,url_prefix='/admin')
* 設置模版目錄
* 藍圖對象默認的模板目錄爲系統的模版目錄,能夠在建立藍圖對象時使用 template_folder 關鍵字參數設置模板目錄sql

admin = Blueprint('admin',name,template_folder='my_templates')
* 若是在 templates 中存在和 my_templates 同名文件,則系統會優先使用 templates 中的文件數據庫

  • 2,在這個藍圖對象上進行操做,註冊路由,指定靜態文件夾,註冊模版過濾器
    @admin.route('/')
    def admin_home():
    return 'admin_home'
  • 3,在應用對象上註冊這個藍圖對象
    app.register_blueprint(admin,url_prefix='/admin')
    • 這裏的前綴和藍圖對象的前綴同樣的做用
  • 當這個應用啓動後,經過/admin/能夠訪問到藍圖中定義的視圖函數django

    虛擬環境的

    爲甚麼搭建虛擬環境

    在實際開發中,多個程序的運行能夠能須要調試各類版本的不一樣環境,好比不一樣的python解釋器,不一樣的flask版本
    問題描述:若是直接進行多個版本安裝,會致使後安裝的內容覆蓋先安裝的.
    解決辦法:使用虛擬環境,不一樣的程序選擇不一樣的環境,互不影響json

搭建步驟

  • 1.先查看當前電腦中是否有虛擬環境命令
    virtualenv --version
  • 2.[可選]安裝虛擬環境的命令:
    sudo pip install virtualenv
    sudo pip install virtualenvwrapper
  • 3.查看是否有mkvirtualenv建立虛擬環境指令
    mkvirtualenv --versionflask

  • 4.[可選]安裝完虛擬環境後,若是提示找不到mkvirtualenv命令,須配置環境變量
    • 4.一、建立目錄用來存放虛擬環境
      mkdir $HOME/.virtualenvs
    • 4.二、打開~/.bashrc文件,並添加以下:
      export WORKON_HOME=$HOME/.virtualenvs
      source /usr/local/bin/virtualenvwrapper.sh
    • 4.三、運行
      source ~/.bashrc
  • 5.建立虛擬環境的命令 :
    mkvirtualenv 虛擬環境名稱(默認python2.x)
    例: mkvirtualenv py_flask
    • mkvirtualenv -p python3 虛擬環境名稱(指定python3.x)
      例 :mkvirtualenv -p python3 py3_flask

      如何使用虛擬環境

  • 1.查看虛擬環境的命令 :

    workon 兩次tab鍵 或者 workon 回車

  • 2.使用虛擬環境的命令 :

    workon 虛擬環境名稱
    例 :workon py_flask
    例 :workon py3_flask

  • 3.退出虛擬環境的命令 :

    deactivate

  • 4.刪除虛擬環境的命令(須要先退出):

    rmvirtualenv 虛擬環境名稱

    例 :刪除虛擬環境py3_flask

    先退出:deactivate

    再刪除:rmvirtualenv py3_flask

    如何在虛擬環境中安裝工具包

  • 1.安裝flask-0.10.1的包:
    pip install 包名稱
    例 : 安裝flask-0.10.1的包
    pip install flask==0.10.1
  • 2.查看虛擬環境中安裝的包 :
    pip freeze
  • 工具包安裝的位置
    • python2版本下:
      ~/.virtualenvs/py_flask/lib/python2.7/site-packages/
    • python3版本下:
      ~/.virtualenvs/py3_flask/lib/python3.5/site-packages

      web應用程序交互流程

      flask

      核心

  • jinja2
    • 模板
  • 拓展包
    • flask_script
      • 命令行方式啓動
        python hello.py runserver -host ip地址
      • from flask import Flask

      • 1.從flask_script中導入Manager類

      • from flask_script import Manager

      • app = Flask(name)

      • 2.使用Manager管理app對象

      • manager = Manager(app)

      • @app.route('/')

      • def hello_world():

      • return "helloworld"
      • if name == 'main':

      • manager.run()
    • flask_wtf
      • 後臺CSRF校驗機制
      • from flask import Flask,render_template

      • from flask_wtf import CSRFProtect

      • app = Flask(name)

      • 設置SECRET_KEY

      • app.config["SECRET_KEY"] = "fjkdjfkdfjdk"

      • 保護應用程序

      • CSRFProtect(app)

      • @app.route('/')

      • def show_page():

      • return render_template('file01csrf.html')
      • @app.route('/add_data',methods=["POST"])

      • def add_data():

      • return "登錄成功"
      • if name == 'main':

      • app.run(debug=True)
      • 前端隱藏表單
      • {#設置隱藏的csrf_token,使用了CSRFProtect保護app以後,便可使用csrf_token()方法#}
      • <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
      • <label>用戶名:</label> <input type="text" name="username"><br>
      • <label>密碼:</label> <input type="text" name="username"><br>
      • <input type="submit" value="登錄">
    • flask_migrate
      • 數據庫遷移
    • flask_sqlalchemy
      • flask操做數據庫
  • werkzeug
    • 路由模塊
      • from werkzeug.route import Converter
        自定義轉換器轉換器

        最簡單的flaskweb

        • 新建文件helloworld.py文件

        1.導入Flask類

        from flask import Flask

        2.建立Flask對象接收一個參數__name__,它會指向程序所在的包
        app = Flask(name)

        3.裝飾器的做用是將路由映射到視圖函數index

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

        4.Flask應用程序實例的run方法,啓動WEB服務器

        if name == 'main':
        app.run()

加載應用程序的配置方式

  • from_pyfile
    • 從配置文件中添加
      在同級建立配置文件.ini
      配置內容每行一個
    • 從配置文件中加載配置

    • app.config.from_pyfile('config.ini')

  • from_object
    • 從配置對象中添加
    • 配置對象,裏面定義須要給 APP 添加的一系列配置

    • class Config(object):

    • DEBUG = True
    • 從配置對象中加載配置

    • app.config.from_object(Config)

  • from_enver
    • 從環境變量中添加
    • 加載指定環境變量名稱所對應的相關配置

    • app.config.from_envvar('FLASKCONFIG')

Flask()建立時的參數

  • name
    • 當前程序運行__main__
    • Flask程序所在的包(模塊),傳 name 就能夠

    • 其能夠決定 Flask 在訪問靜態文件時查找的路徑

  • static_folder
    • 靜態文件存儲的文件夾,默認爲static
  • static_url_path
    • 靜態文件訪問路徑,默認爲 /static
  • template_folder
    • 模板文件存儲的文件夾,默認爲 template

      app.run()傳遞的參數

  • host
  • port
  • debug

    路由的定義

    指定請求方式

  • methods
  • @app.route('/demo2', methods=['GET', 'POST'])

  • def demo2():

  • # 直接從請求中取到請求方式並返回
  • return request.method
  • 配合postman軟件開發
  • Postman功能(https://www.getpostman.com/features)

  • 主要用於模擬網絡請求包

  • 快速建立請求

  • 回放、管理請求

  • 快速設置網絡代理

指定請求參數

路由傳遞參數,整數

@app.route('/user/ ')
def user_info(user_id):
return 'the num is %d' % user_id

  • 不填轉換器名稱默認是字符串
    • 默認的就是UnicodeConverter, 因此不填轉換器名稱, 或者使用default、string都是同樣的. - 默認將參數當作普通字符串對待. DEFAULT_CONVERTERS = { 'default': UnicodeConverter, 'string': UnicodeConverter, 'any': AnyConverter, 'path': PathConverter, 'int': IntegerConverter, 'float': FloatConverter, 'uuid': UUIDConverter, }
  • 自定義轉換器
  • from flask import Flask

  • 導入基類轉換器

  • from werkzeug.routing import BaseConverter

  • app = Flask(name)

  • 1.自定義類,繼承自BaseConverter

  • class MyRegexConverter(BaseConverter):

  • # 2.編寫初始化方法, init方法, 接收兩個參數, url_map, regex, 並初始化父類空間和子類空間
  • def __init__(self,url_map,regex):
  • super(MyRegexConverter, self).__init__(url_map)
  • self.regex = regex
  • 3.將自定義轉換器類,添加到默認的轉換列表中

  • app.url_map.converters['re'] = MyRegexConverter

  • 使用自定義轉換器

  • 接收3位整數

  • @app.route('/<re("\d{3}"):num>')

  • def hello_world(num):

  • print("num = %s"%num)
  • return "the num is %s"%num
  • 接收一個手機號

  • @app.route('/<re("1[345678]\d{9}"):mobile>')

  • def get_phone_number(mobile):

  • return "the mobile is %s"%mobile
  • if name == 'main':

  • app.run()
    • 繼承與BaseCoverter
    • 能夠直接指定regex值,重寫init方法,傳遞正則規則
    • class BaseConverter(object):

    • """Base class for all converters."""
    • regex = '[^/]+'
    • weight = 100
    • def __init__(self, map):
    • self.map = map
    • 也能夠不直接指定regex值,在使用自定義轉換器的時候指定正則規則
    • class MyConverter(BaseConverter):

    • def __init__(self,url_map,regex):
    • super(MyConverter, self).__init__(url_map)
    • self.regex = regex
    • app.url_map.converters['re'] = MyConverter

    • @app.route('/<re("\d{3}"):num>')

    • def index(num):

    • return num
    • 自定義轉換器類中的其餘方法
    • class MyConverter(BaseConverter):

    • def __init__(self,url_map,regex):
    • super(MyConverter, self).__init__(url_map)
    • self.regex = regex
    • def to_python(self, value):
    • return value
    • def to_url(self, value):
    • return value
    • app.url_map.converters['re'] = MyConverter

      • to_python
        • 匹配成功後被調用,能夠對匹配到的參數進行處理,好比轉換匹配到的數據的類型,在正則匹配完成以後,調用視圖函數以前,能夠對數據進行最後的處理
      • to_url
        • 在正則匹配以前調用執行,並傳入參數,調用完成以後纔開始真正的路由匹配,不關心是否匹配成功,能夠經過此函數修正要傳入路由中的參數,方便更好的正則匹配

          重定向

          重定向

          @app.route('/demo5')
          def demo5():
          # 使用 url_for 生成指定視圖函數所對應的 url
          return redirect(url_for('user_info', user_id=100))

  • redirect('路徑')
    • 經過指定路徑重定向到函數
  • url_for('函數名',key=value)
    • 經過指定函數,返回路徑,能夠附帶附帶請求參數

      返回json

  • 注意contentType
  • contentType: 告訴服務器,我要發什麼類型的數據

  • dataType:告訴服務器,我要想什麼類型的數據,若是沒有指定,那麼會自動推斷是返回 XML,仍是JSON,仍是script,仍是String。

  • json和dict轉換
    • dict->json
      • json = json.dumps(dict)
      • json = jsonity(key=value)
    • json->dict
      • dict = json.loads(json)
  • jsonify
  • 自定義狀態碼
    • 手動指定(響應體——狀態碼+響應頭)
    • make_response()
    • @app.route('/')

    • def hello_world():

    • # 1.直接返回, 響應體 + 狀態碼
    • # return "hello",666
    • # return "hello","666 BIGERROR"
    • # 2.手動建立響應體對象
    • response = make_response("hello")
    • response.status = "888 XIAGAO"
    • return response
      • status
      • headers

        上下文

        至關於一個容器,保存了 Flask 程序運行過程當中的一些信息。

請求上下文

保存了客戶端和服務器交互的數據

  • request
  • session

    應用上下文

    flask 應用程序運行過程當中,保存的一些配置信息,好比程序名、數據庫鏈接、應用信息等

  • current_app
  • 應用程序上下文,用於存儲應用程序中的變量,能夠經過current_app.name打印當前app的名稱,也能夠在current_app中存儲一些變量,例如:

  • 應用的啓動腳本是哪一個文件,啓動時指定了哪些參數

  • 加載了哪些配置文件,導入了哪些配置

  • 連了哪一個數據庫

  • 有哪些public的工具類、常量

  • 應用跑再哪一個機器上,IP多少,內存多大

  • current_app.name

  • current_app.test_value='value'

  • g
  • g 做爲 flask 程序全局的一個臨時變量,充當者中間媒介的做用,咱們能夠經過它傳遞一些數據,g 保存的是當前請求的全局變量,不一樣的請求會有不一樣的全局變量,經過不一樣的thread id區別

  • g.name='abc'

  • 注意:不一樣的請求,會有不一樣的全局變量

狀態保持

http是一種無狀態協議,瀏覽器請求服務器是無狀態的。
無狀態:指一次用戶請求時,瀏覽器、服務器不知道以前這個用戶作過什麼,每次請求都是一次新的請求。
無狀態緣由:瀏覽器與服務器是使用 socket 套接字進行通訊的,服務器將請求結果返回給瀏覽器以後,會關閉當前的 socket 鏈接,並且服務器也會在處理頁面完畢以後銷燬頁面對象。
有時須要保持下來用戶瀏覽的狀態,好比用戶是否登陸過,瀏覽過哪些商品等
實現狀態保持主要有兩種方式:
在客戶端存儲信息使用Cookie
在服務器端存儲信息使用Session
二.生活狀態舉例:

Cookie是存儲在瀏覽器中的一段純文本信息,建議不要存儲敏感信息如密碼,由於電腦上的瀏覽器可能被其它人使用
Cookie基於域名安全,不一樣域名的Cookie是不能互相訪問的
應用場景:廣告推送;商城結帳提取商品信息

  • 獲取與設置cookie
  • from flask import Flask, make_response, request

  • app = Flask(name)

  • 設置cookie值

  • @app.route('/set_cookie')

  • def set_cookie():

  • response = make_response("set cookie")
  • response.set_cookie("name","zhangsan")
  • response.set_cookie("age","13",10) #10秒有效期
  • return response
  • 獲取cookie

  • @app.route('/get_cookie')

  • def get_cookie():

  • #獲取cookie,能夠根據cookie的內容來推薦商品信息
  • # name = request.cookies['haha']
  • name = request.cookies.get('name')
  • age = request.cookies.get('age')
  • return "獲取cookie,name is %s, age is %s"%(name,age)
  • if name == 'main':

  • app.run(debug=True)

Session

對於敏感、重要的信息,建議要存儲在服務器端,不能存儲在瀏覽器中,如用戶名、餘額、等級、驗證碼等信息,因此可使用session進行保存
在服務器端進行狀態保持的方案就是Session

  • 依賴cookie
  • 獲取和設置session
  • from flask import Flask,session

  • app = Flask(name)

  • 設置SECRET_KEY

  • app.config["SECRET_KEY"] = "fhdk^fk#djefkj&&&"

  • 設置session

  • @app.route('/set_session/ ')

  • def set_session(name):

  • session["name"] = name
  • session["age"] = "13"
  • return "set session"
  • 獲取session內容

  • @app.route('/get_session')

  • def get_session():

  • name = session.get('name')
  • age = session.get('age')
  • return "name is %s, age is %s"%(name,age)
  • if name == 'main':

  • app.run(debug=True)
  • SECRET_KEY

    請求鉤子

    在客戶端和服務器交互的過程當中,有些準備工做或掃尾工做須要處理,好比:

    在請求開始時,創建數據庫鏈接;
    在請求開始時,根據需求進行權限校驗;
    在請求結束時,指定數據的交互格式;
    爲了讓每一個視圖函數避免編寫重複功能的代碼,Flask提供了通用設施的功能,即請求鉤子。

before_first_request

在第一次請求以前調用,能夠在此方法內部作一些初始化操做
如:數據庫的鏈接

before_request

在每次請求以前調用,這時候已經有請求了,可能在這個方法裏面作請求的校驗

若是請求的校驗不成功,能夠直接在此方法中進行響應,直接return以後那麼就不會執行視圖函數

@app.before_request
def before_request():
print("before_request")
# if 請求不符合條件:
# return "laowang"

after_request

在執行完視圖函數以後會調用,而且會把視圖函數所生成的響應傳入,能夠在此方法中對響應作最後一步統一的處理

@app.after_request
def after_request(response):
print("after_request")
response.headers["Content-Type"] = "application/json"
return response

teardown_request

請每一次請求以後都會調用,會接受一個參數,參數是服務器出現的錯誤信息

@app.teardown_request
def teardown_request(e):
print("teardown_request")

當前Request對象

request 就是flask中表明當前請求的 request 對象,其中一個請求上下文變量(理解成全局變量,在視圖函數中直接使用能夠取到當前本次請求)

args

  • 地址欄數據,問號後面
    www.baidu.com?key=value&key=value

    data

  • json/xml,post提交的請求

    form

  • 表單

  • 記錄請求中的cookie信息

    files

  • 文件內容

    headers

  • 記錄請求中的報文頭

    method

  • 記錄請求中使用的HTTP方法

    url

  • 記錄請求的url地址

    路由和視圖函數在定義和匹配過程當中關鍵類

    BaseConverter

  • 記錄匹配規則

    Rule

  • 記錄視圖函數與路由映射關係

    Map

  • 路由與視圖函數關係列表

    MapAdapter

  • 作具體匹配工做

    Flask_script

    屬於flask的擴展包,經過使用Flask-Script擴展,咱們能夠在Flask服務器啓動的時候,經過命令行的方式傳入參數。而不只僅經過app.run()方法中傳參,好比咱們能夠經過:

繼承腳本擴展,動態運行程序,指定IP,PORT

python hello.py runserver -host ip地址

Manager(app)

from flask import Flask

1.從flask_script中導入Manager類

from flask_script import Manager

app = Flask(name)

2.使用Manager管理app對象

manager = Manager(app)

@app.route('/')
def hello_world():
return "helloworld"

if name == 'main':
manager.run()

數據庫

  • ORM
  • ORM 全拼Object-Relation Mapping. 稱爲對象-關係映射

  • 主要實現模型對象到關係數據庫數據的映射.

    • 優勢
      • 對數據庫的操做都轉化成對類,屬性和方法的操做.
        不用編寫各類數據庫的sql語句.
        不在關注,使用的是mysql、oracle...等數據庫
    • 缺點
      • 相比較直接使用SQL語句操做數據庫,有性能損失.
  • Flask-SQLAlchemy
  • SQLALchemy 其實是對數據庫的抽象,讓開發者不用直接和 SQL 語句打交道,而是經過 Python 對象來操做數據庫,在捨棄一些性能開銷的同時,換來的是開發效率的較大提高

  • SQLAlchemy是一個關係型數據庫框架,它提供了高層的 ORM 和底層的原生數據庫的操做。flask-sqlalchemy 是一個簡化了 SQLAlchemy 操做的flask擴展。

    • 安裝
      • pip install flask-sqlalchemy
      • 若是鏈接的是 mysql 數據庫,須要安裝 mysqldb
        pip install flask-mysqldb
      • 提示:若是flask-mysqldb安裝不上,安裝, pip install pymysql
    • 數據庫鏈接設置
      • 數據庫連接地址
        app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:mysql@127.0.0.1:3306/test'
        • 格式:mysql:// <用戶名> : <密碼> @: <端口> /數據庫名稱
      • 動態追蹤修改設置,如未設置只會提示警告
        app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
      • 有時候須要查看映射的sql語句
        • app.config['SQLALCHEMY_ECHO'] = True
      • 配置完成須要去 MySQL 中建立項目所使用的數據庫
        $ mysql -uroot -pmysql
        $ create database test charset utf8;
      • 配置信息彙總
        • SQLALCHEMY_BINDS 一個映射 binds 到鏈接 URI 的字典。更多 binds 的信息見用 Binds 操做多個數據庫。
        • SQLALCHEMY_ECHO 若是設置爲Ture, SQLAlchemy 會記錄全部 發給 stderr 的語句,這對調試有用。(打印sql語句)
        • SQLALCHEMY_RECORD_QUERIES 能夠用於顯式地禁用或啓用查詢記錄。查詢記錄 在調試或測試模式自動啓用。更多信息見get_debug_queries()。
        • SQLALCHEMY_NATIVE_UNICODE 能夠用於顯式禁用原生 unicode 支持。當使用 不合適的指定無編碼的數據庫默認值時,這對於 一些數據庫適配器是必須的(好比 Ubuntu 上 某些版本的 PostgreSQL )。
        • SQLALCHEMY_POOL_SIZE 數據庫鏈接池的大小。默認是引擎默認值(一般 是 5 )
        • SQLALCHEMY_POOL_TIMEOUT 設定鏈接池的鏈接超時時間。默認是 10 。
        • SQLALCHEMY_POOL_RECYCLE 多少秒後自動回收鏈接。這對 MySQL 是必要的, 它默認移除閒置多於 8 小時的鏈接。注意若是 使用了 MySQL , Flask-SQLALchemy 自動設定 這個值爲 2 小時。
      • 鏈接其餘數據庫
        • Postgres:
          postgresql://scott:tiger@localhost/mydatabase
        • MySQL:
          mysql://scott:tiger@localhost/mydatabase
        • Oracle:
  • oracle://scott:tiger@127.0.0.1:1521/sidname
    * SQLite (注意開頭的四個斜線):
    sqlite:////absolute/path/to/foo.db
    • 建立sqlalchemy對象
      • 建立SQLAlchemy對象

        db = SQLAlchemy(app)
    • 使用模型類建立數據表
      • 一對多關係
        • 一方
          • 建立模型類(繼承自db.Model)
          • class Student(db.Model):

          • __tablename__ = "students"
          • id = db.Column(db.Integer,primary_key=True)
          • name = db.Column(db.String(64),unique=True,nullable=False)
          • #關係屬性
          • courses = db.relationship("Course",backref="students",secondary=tb_student_course)
            • 默認表名爲模型類的名字
              __tablename__指定表名
              • class Student(db.Model):
                tablename = "students"
            • 類屬性名錶示的字段名
            • 定義列對象

            • 變量做爲字段名

            • 括號內是字段類型和字段約束

              • id = db.Column(db.Integer,primary_key=True)
                name = db.Column(db.String(64),unique=True,nullable=False)
            • 關係屬性
            • 關係屬性方便查詢使用

            • 經過 table_name.courses能夠訪問到‘Course’模型類定義的數據表

            • ‘Course’模型類定義的數據表,經過Course.students能夠訪問到本數據表

              • courses = db.relationship("Course",backref="students")
              • lazy='dynamic'
              • 第三個參數lazy決定了何時SQLALchemy從數據庫中加載數據

              • 若是設置爲子查詢方式(subquery),則會在加載完Role對象後,就當即加載與其關聯的對象,這樣會讓總查詢數量減小,但若是返回的條目數量不少,就會比較慢

              • 設置爲 subquery 的話,role.users 返回全部數據列表

              • 另外,也能夠設置爲動態方式(dynamic),這樣關聯對象會在被使用的時候再進行加載,而且在返回前進行過濾,若是返回的對象數不少,或者將來會變得不少,那最好採用這種方式

              • 關係屬性在任何一方設置都行
        • 多方
          • 設置外鍵
            • role_id = db.Column(db.Integer,db.ForeignKey(Role.id))
      • 多對多關係
        • 建立一張關聯表
          關聯表數據分別和兩張表設置外鍵
          • tb_student_course = db.Table('tb_student_course',
            db.Column('student_id', db.Integer, db.ForeignKey('students.id')),
            db.Column('course_id', db.Integer, db.ForeignKey('courses.id'))
            )
        • 關係屬性設置在任何一方都行
          • courses = db.relationship('Course', secondary=tb_student_course,
            backref='student',
            lazy='dynamic')
    • 向數據表中添加數據

      • if name == main:
        以後
        • 一對多
          • 建立模型類對象
            • stu1 = Student(name='張三')
            • cou1 = Course(name='物理')
          • 將模型類對象添加進會話
            • db.session.add(obj) 添加對象
              db.session.add_all([obj1,obj2,..]) 添加多個對象
            • db.session.delete(obj) 刪除對象
          • 提交會話
            • db.session.commit() 提交會話
            • db.session.rollback() 回滾
            • db.session.remove() 移除會話
        • 多對多
          • 建立模型類對象
            • stu1 = Student(name='張三')
              stu2 = Student(name='李四')
              stu3 = Student(name='王五')
            • cou1 = Course(name='物理')
              cou2 = Course(name='化學')
              cou3 = Course(name='生物')
          • 關聯表添加關聯數據
            • stu1.courses = [cou2, cou3]
              stu2.courses = [cou2]
              stu3.courses = [cou1, cou2, cou3]
          • 將模型類對象添加進會話
            • db.session.add(obj) 添加對象
              db.session.add_all([obj1,obj2,..]) 添加多個對象
            • db.session.delete(obj) 刪除對象
          • 提交會話
            • db.session.commit() 提交會話
            • db.session.rollback() 回滾
            • db.session.remove() 移除會話
    • app.run()
  • 數據庫的遷移
  • 在Flask中可使用Flask-Migrate擴展,來實現數據遷移。而且集成到Flask-Script中,全部操做經過命令就能完成。

  • 爲了導出數據庫遷移命令,Flask-Migrate提供了一個MigrateCommand類,能夠附加到flask-script的manager對象上。

    • 虛擬環境中安裝Flask-Migrate
      • pip install flask-migrate
    • 導入相應的包
      • from flask_sqlalchemy import SQLAlchemy
        from flask_migrate import Migrate,MigrateCommand
        from flask_script import Shell,Manager
    • 建立Flask對象
      • app = Flask(name)
    • 使用flask-script的manager接管app對象
      • manager = Manager(app)
    • 設置app鏈接數據庫的配置信息
    • 建立SQLAlchemy對象,讀取app中的配置信息
      • db = SQLAlchemy(app)
    • 使用數據庫遷移框架
      • migrate = Migrate(app,db)
        第一個參數是Flask實例,第二個參數是sqlalchemy數據庫實例
    • 使用Flask_script中的manager動態運行程序
      • manager是Flask-Script的實例,這條語句在flask-Script中添加一個db命令

        manager.add_command('db',MigrateCommand)

數據庫遷移

  • 配合flask_migrate
    pip install flask-migrate

  • from flask import Flask

  • from flask_sqlalchemy import SQLAlchemy

  • from flask_migrate import Migrate,MigrateCommand

  • from flask_script import Shell,Manager

  • app = Flask(name)

  • manager = Manager(app)

  • app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:mysql@127.0.0.1:3306/Flask_test'

  • app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True

  • app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True

  • db = SQLAlchemy(app)

  • 第一個參數是Flask的實例,第二個參數是Sqlalchemy數據庫實例

  • migrate = Migrate(app,db)

  • manager是Flask-Script的實例,這條語句在flask-Script中添加一個db命令

  • manager.add_command('db',MigrateCommand)

  • 定義模型Role

  • class Role(db.Model):

  • # 定義表名
  • __tablename__ = 'roles'
  • # 定義列對象
  • id = db.Column(db.Integer, primary_key=True)
  • name = db.Column(db.String(64), unique=True)
  • user = db.relationship('User', backref='role')
  • #repr()方法顯示一個可讀字符串,
  • def __repr__(self):
  • return 'Role:'.format(self.name)
  • 定義用戶

  • class User(db.Model):

  • __talbe__ = 'users'
  • id = db.Column(db.Integer, primary_key=True)
  • username = db.Column(db.String(64), unique=True, index=True)
  • #設置外鍵
  • role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
  • def __repr__(self):
  • return 'User:'.format(self.username)
  • if name == 'main':

  • manager.run()
    • app = Flask(name)
    • manager = Manager(app)
    • db = SQLAlchemy(app)
    • migrate = Migrate(app,db)

    • manager.add_command('db',MigrateCommand)
    • manager.run()
  • 建立遷移倉庫
    • 這個命令會建立migrations文件夾,全部遷移文件都放在裏面。

      python database.py db init
  • 建立遷移腳本
    • 自動建立遷移腳本有兩個函數
      upgrade():函數把遷移中的改動應用到數據庫中。
      downgrade():函數則將改動刪除。
      自動建立的遷移腳本會根據模型定義和數據庫當前狀態的差別,生成upgrade()和downgrade()函數的內容。
      對比不必定徹底正確,有可能會遺漏一些細節,須要進行檢查
      python database.py db migrate -m 'initial migration'
  • 更新數據庫
    • python database.py db upgrade
  • 返回之前的版本
    • 能夠根據history命令找到版本號,而後傳給downgrade命令:

python app.py db history

輸出格式: -> 版本號 (head), initial migration
* 回滾到指定版本
python app.py db downgrade 版本號

  • 實際操做順序
    • 實際操做順序:
      1.python 文件 db init
      2.python 文件 db migrate -m"版本名(註釋)"
      3.python 文件 db upgrade 而後觀察表結構
      4.根據需求修改模型
      5.python 文件 db migrate -m"新版本名(註釋)"
      6.python 文件 db upgrade 而後觀察表結構
      7.若返回版本,則利用 python 文件 db history查看版本號
      8.python 文件 db downgrade(upgrade) 版本號

      模板

      Jinja2模板

      1.獲取各類變量的值

      <h3>整數: {{ my_num + 20}}</h3>
      <h3>字符串: {{ my_str + " python" }}</h3>
      <h3>元組: {{ my_tuple }}, 分開獲取:{{ my_tuple[0] }}, {{ my_tuple[1] }}</h3>
      <h3>列表: {{ my_list }}, 分開獲取:{{ my_list[0] }}, {{ my_list[1] }}</h3>
      <h3>字典: {{ my_dict }},分開獲取:{{ my_dict.name }}, {{ my_dict[age] }}</h3>
      <h2>2.遍歷元祖中全部的元素</h2>
      {% for item in my_tuple %}
          <li>{{ item }}</li>
      {% endfor %}
      
      <h2>3.取出列表中全部偶數</h2>
      {% for item in my_list %}
          {% if item %2 == 0 %}
              {{ item }}
          {% endif %}
      {% endfor %}
      
      <h2>4.遍歷字典內容</h2>
      {% for key in my_dict %}
          {# 若是直接是mydict.key ,那麼這個key是一個字符串, 若是是 mydict[key], 那麼key當成變量 #}
          <li>{{ key }} = {{ my_dict[key] }}</li>
      {% endfor %}
  • Jinja2模板概述
  • 用來展現數據的html頁面,這個過程也一般稱爲渲染,屬於Jinja2的功能 使用模板的好處:

  • 視圖函數只負責業務邏輯和數據處理(業務邏輯方面)

  • 而模板則取到視圖函數的數據結果進行展現(視圖展現方面)

  • 代碼結構清晰,耦合度低

  • Jinja2特色
  • Jinja2:是 Python 下一個被普遍應用的模板引擎,是由Python實現的模板語言,他的設計思想來源於 Django 的模板引擎,並擴展了其語法和一系列強大的功能,其是Flask內置的模板語言。

  • 模板語言:是一種被設計來自動生成文檔的簡單文本格式,在模板語言中,通常都會把一些變量傳給模板,替換模板的特定位置上預先定義好的佔位變量名。

  • 使用render_template函數封裝模板引擎

    • python代碼實現
    • 仿照django實現模板語言
    • 過濾器支持鏈式調用{{
      str|lower|reverse }}
    • 直接在模板中支持運算
  • Jinja2模板語法
    • 獲取變量值
      • 整數:{ {number} }

        元祖:{ {tuple[0]} }


        列表:{ { list[0] } }


        字典:{ { dict['key'] } }

    • 分支語句
      • { % if 條件 % }
        語句1
        { % else % }
        語句2
        { % endif % }
    • for循環
      • {% for 變量 in 容器 %}
        語句
        {% endfor%}
        • for循環中能夠訪問i的特殊變量
          • loop.index 當前循環迭代的次數(從 1 開始)
          • loop.index0 當前循環迭代的次數(從 0 開始)
          • loop.revindex 到循環結束須要迭代的次數(從 1 開始)
          • loop.revindex0 到循環結束須要迭代的次數(從 0 開始)
          • loop.first 若是是第一次迭代,爲 True 。
          • loop.last 若是是最後一次迭代,爲 True 。
          • loop.length 序列中的項目數。
          • loop.cycle 在一串序列間期取值的輔助函數。見下面示例程序。
    • 註釋
      • {# 註釋內容 #}
  • Jinja2模板過濾器
    • Jinja2模板自帶的兩種過濾器
    • 過濾器的本質就是函數。有時候咱們不只僅只是須要輸出變量的值,咱們還須要修改變量的顯示,甚至格式化、運算等等,而在模板中是不能直接調用 Python 中的某些方法,那麼這就用到了過濾器。

      • 字符串過濾器
  • 使用格式:{{ 字符串 | 字符串過濾器 }}
    * safe:禁用轉義

    {{ 'hello' | safe }}


    * capitalize:把變量值的首字母轉成大寫,其他字母轉小寫

    {{ 'hello' | capitalize }}


    * lower:把值轉成小寫

    {{ 'HELLO' | lower }}


    * upper:把值轉成大寫

    {{ 'hello' | upper }}


    * title:把值中的每一個單詞的首字母都轉成大寫

    {{ 'hello' | title }}


    * reverse:字符串反轉

    {{ 'olleh' | reverse }}


    * format:格式化輸出

    {{ '%s is %d' | format('name',17) }}


    * striptags:渲染以前把值中全部的HTML標籤都刪掉

    {{ 'hello' | striptags }}


    * 列表過濾器
  • 使用格式:{{ 列表 | 列表過濾器 }}
    * first:取第一個元素

    {{ [1,2,3,4,5,6] | first }}


    * last:取最後一個元素

    {{ [1,2,3,4,5,6] | last }}


    * length:獲取列表長度

    {{ [1,2,3,4,5,6] | length }}


    * sum:列表求和

    {{ [1,2,3,4,5,6] | sum }}


    * sort:列表排序

    {{ [6,2,3,1,5,4] | sort }}


    * 使用過濾器的其餘操做語句
    * 語句塊操做
    {% filter upper %}
    #一大堆文字#
    {% endfilter %}
    * 鏈式調用
    {{ "hello world" | reverse | upper }}
    • Jinja2自定義過濾器的兩種方式
      • 先定義函數(一個形參接受原值)
        • 添加到過濾器列表,app.add_template_filter('函數名',‘過濾器名稱’)
        • def do_listreverse(li):

        • # 經過原列表建立一個新列表
        • temp_li = list(li)
        • # 將新列表進行返轉
        • temp_li.reverse()
        • return temp_li
        • app.add_template_filter(do_listreverse,'lireverse')

      • 定義函數,直接使用過濾器列表裝飾@app.teemplate_filter('過濾器名稱')
      • @app.template_filter('lireverse')

      • def do_listreverse(li):

      • # 經過原列表建立一個新列表

      • temp_li = list(li)

      • # 將新列表進行返轉

      • temp_li.reverse()

      • return temp_li

  • 模板代碼的複用
  • 在模板中,可能會遇到如下狀況:

  • 多個模板具備徹底相同的頂部和底部內容

  • 多個模板中具備相同的模板代碼內容,可是內容中部分值不同

  • 多個模板中具備徹底相同的 html 代碼塊內容

  • 像遇到這種狀況,可使用 JinJa2 模板中的 宏、繼承、包含來進行實現

    • 宏是Jinja2中的函數,調用後,直接返回一個模板,或者字符串

    • 當模板中出現大量重複功能代碼的時候,可使用宏來進行封裝

      • 定義宏
        能夠在當前文件,也能夠在其餘文件
      • {% macro input(name,value='',type='text') %}

      • <input type="{{type}}" name="{{name}}" value="{{value}}">
      • {% endmacro %}

        • 使用當前文件的宏
        • {{ input('name' value='zs')}}

        • 使用其餘文件的宏
        • {%import 'filename.html' as 別名%}

        • {%別名.函數名(參數)%}
    • 繼承
    • 將公共的內容抽取到父類模板,共子類使用的形式稱爲繼承.

    • 通常Web開發中,繼承主要使用在網站的頂部菜單、底部。這些內容能夠定義在父模板中,子模板直接繼承,而不須要重複書寫。

      • 父模板
      • 父模板中使用多個block組成

        • {% block top %}
          頂部菜單
          {% endblock top %}

{% block content %}
正文內容
{% endblock content %}

{% block bottom %}
底部
{% endblock bottom %}
* 子模版
* 繼承後,子類徹底擁有父類內容,而且子類能夠進行重寫,若是寫保留父類內容使用: super()

* {% extends 'base.html' %}

{% block content %}
須要填充的內容
{% endblock content %}
* 模板繼承的注意點
* 不支持多繼承
爲了便於閱讀,在子模板中使用extends時,儘可能寫在模板的第一行。
不能在一個模板文件中定義多個相同名字的block標籤。
定義block模板的時候,必定要加上endblock結束標記
* 包含
* > Jinja2模板中,除了宏和繼承,還支持一種代碼重用的功能,叫包含(Include)。它的功能是將另外一個模板整個加載到當前模板中,並直接渲染。

* 格式:

{% include 'hello.html' %}
或者
{% include 'hello.html' ignore missing %}
提示: ignore missing 加上後若是文件不存在,不會報錯
* 總結
* 宏(Macro)、繼承(Block)、包含(include)均能實現代碼的複用。
繼承(Block)的本質是代碼替換,通常用來實現多個頁面中重複不變的區域。
宏(Macro)的功能相似函數,能夠傳入參數,須要定義、調用。
包含(include)是直接將目標模板文件整個渲染出來。

  • 模板特有的變量和函數
  • 你能夠在本身的模板中訪問一些 Flask 默認內置的函數和對象

    • config
      • 你能夠從模板中直接訪問Flask當前的config對象:

{{config.DEBUG}}
輸出:True
* request
* 就是flask中表明當前請求的request對象:

{{request.url}}
輸出:http://127.0.0.1
* g變量
* 在視圖函數中設置g變量的 name 屬性的值,而後在模板中直接能夠取出

{{ g.name }}
* url_for()
* url_for會根據傳入的路由器函數名,返回該路由對應的URL,在模板中始終使用url_for()就能夠安全的修改路由綁定的URL,則不比擔憂模板中渲染出錯的連接:

{{url_for('home')}}
* 若是咱們定義的路由URL是帶有參數的,則能夠把它們做爲關鍵字參數傳入url_for(),Flask會把他們填充進最終生成的URL中:

{{ url_for('post', post_id=1)}}
/post/1
* get_flashed_messages()
* 這個函數會返回以前在flask中經過flask()傳入的消息的列表,flash函數的做用很簡單,能夠把由Python字符串表示的消息加入一個消息隊列中,再使用get_flashed_message()函數取出它們並消費掉:

{%for message in get_flashed_messages()%}
{{message}}
{%endfor%}

  • Flask_WTF表單
    • 使用代碼定義表單
    • 在模板中進行表單渲染
    • 提供一系列的驗證器,對錶單提交的數據進行驗證
    • 流程
      • 定義類繼承子FlaskForm
      • 在類中編寫字段內容,驗證函數
      • 建立,渲染到頁面
  • csrf
    • flask_wtf模塊提供了csrf攻擊的保護
    • 使用流程:

from flask_wtf.csrf import CSRFProtect
CSRFProtect(app)
* CSRFProtect(app)保護原理:
* > 對應用程序app中的post,put,dispatch,delete, 4種類型的請求作保護,由於這些類型的請求是用於更改服務器的資源
* > 當以上面4種類型的請求,操做服務器資源的時候,會校驗cookie中的csrf_token, 表單中的csrf_token信息
* > 只有上面兩者的值相等的時候,那麼校驗則經過,能夠操做服務器資源

* csrf_token值的生成須要加密, 因此設置SECRET_KEY
* 先後端代碼
    * 後端代碼
     * > from flask_wtf import CSRFProtect
     * > app.config["SECRET_KEY"] = "fjkdjfkdfjdk"
     * > CSRFProtect(app)

    * 前端代碼
     * > {#設置隱藏的csrf_token,使用了CSRFProtect保護app以後,便可使用csrf_token()方法#}
     * >     <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">

render_template()函數

from flask import Flask,render_template
app = Flask(name) #默認省略了三個參數,static_url_path, static_folder, template_folders

@app.route('/')
def hello_world():
#定義數據,整數,字符串,元祖,列表,字典
num = 10
str = "hello"
tuple = (1,2,3,4)
list = [5,6,7,8]
dict = {
"name":"張三",
"age":13
}

return render_template('file01.html',my_num=num,my_str=str,my_tuple=tuple,my_list=list,my_dict=dict)

if name == 'main':
app.run(debug=True)

  • 將數據傳遞給模板
    return render_template('模板文件名',key=value)

XMind: ZEN - Trial Version

相關文章
相關標籤/搜索