日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Flask视图、模板、模型

發布時間:2023/12/20 编程问答 51 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Flask视图、模板、模型 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Day01

Flask前期了解:

為什么要使用框架:

web網站發展至今,特別是服務器端,涉及到的知識、內容,非常廣泛。這對程序員的要求會越來越高。如果采用成熟,穩健的框架,那么一些基礎的工作,比如,網絡操作、數據庫訪問、會話管理等都可以讓框架來處理,那么程序開發人員可以把精力放在具體的業務邏輯上面。使用Web框架開發Web應用程序可以降低開發難度,提高開發效率。 總結一句話:避免重復造輪子(wheel)。

Flask框架的誕生:

Flask誕生于2010年,是Armin ronacher(人名)用Python語言基于Werkzeug工具箱編寫的輕量級Web開發框架。它主要面向需求簡單的小應用。Flask本身相當于一個內核,其他幾乎所有的功能都要用到擴展(郵件擴展Flask-Mail,用戶認證Flask-Login),都需要用第三方的擴展庫來實現。比如可以用Flask-extension加入ORM、窗體驗證工具,文件上傳、身份驗證等。Flask沒有默認使用的數據庫,你可以選擇MySQL,也可以用NoSQL。其 WSGI 工具箱采用 Werkzeug(路由模塊) ,模板引擎則使用 Jinja2 。Python最出名的框架要數Django,此外還有Flask、Tornado等框架。雖然Flask不是最出名的框架,但是Flask應該算是最靈活的框架之一,這也是Flask受到廣大開發者喜愛的原因。

基于python的web (微) 框架

重量級框架 django為了方便業務程序的開發,提供了豐富的工具及其組件 輕量級框架 flask只提供web核心功能,自由靈活,高度定制,用到什么就可以選擇什么樣的第三方庫來實現其功能;

官方文檔

http://flask.pocoo.org/docs/0.12/ 英文 http://docs.jinkan.org/docs/flask/ 中文

flask依賴庫

flask依賴的基礎的三個庫jinja2 模板引擎(就是可以用模板語法和html標簽結合生成想要的網頁源代碼)Werkzeug WSGI (web server gateway interface)工具集:Werkzeug 是一個 WSGI(在 Web 應用和多種服務器之間的標準 Python 接口) 工具集Itsdangerous 基于Django的簽名模塊現在不僅僅是三個 但是依然前兩個有用安裝時候會出現6個 看看就可以了

flask流行的主要原因

1 有非常齊全的官方文檔,上手非常方便2 有非常好的擴展機制和第三方擴展環境,工作中常見的軟件都會有對應的擴展,動手實現擴展也很容易3 社區活躍度非常高 flask的熱度已經超過django好幾百了4 微型框架的形式給了開發者更大的選擇空間

MVC簡介:

MVC的全拼為Model-View-Controller,最早由TrygveReenskaug在1978年提出,是施樂帕羅奧多研究中心(Xerox PARC)在20世紀80年代為程序語言Smalltalk發明的一種軟件設計模式,是為了將傳統的輸入(input)、處理(processing)、輸出(output)任務運用到圖形化用戶交互模型中而設計的。隨著標準輸入輸出設備的出現,開發人員只需要將精力集中在業務邏輯的分析與實現上。后來被推薦為Oracle旗下Sun公司Java EE平臺的設計模式,并且受到越來越多的使用ColdFusion和PHP的開發者的歡迎。現在雖然不再使用原來的分工方式,但是這種分工的思想被沿用下來,廣泛應用于軟件工程中,是一種典型并且應用廣泛的軟件架構模式。后來,MVC的思想被應用在了Web開發方面,被稱為Web MVC框架。MVC框架的核心思想是:解耦,讓不同的代碼塊之間降低耦合,增強代碼的可擴展性和可移植性,實現向后兼容。當前主流的開發語言如Java、PHP、Python中都有MVC框架。Web MVC各部分的功能 M:全拼為Model,主要封裝對數據庫層的訪問,對數據庫中的數據進行增、刪、改、查操作。V:全拼為View,用于封裝結果,生成頁面展示的html內容。C:全拼為Controller,用于接收請求,處理業務邏輯,與Model和View交互,返回結果。

MVC的處理邏輯圖:

概括性總結MVC:

一種軟件設計架構風范 Model數據的封裝,數據的抽象,用來操作數據的入口 View視圖,主要用來呈現給用戶的 Controller控制器,主要用來接收用戶請求(輸入),并且協調模型和視圖用來做視圖和模型之間的數據的交互 核心理念解耦 實現結果將數據操作,頁面展示,邏輯處理進行了拆分

MTV也叫做MVT

Models封裝數據操作數據庫的表和字段的定義 Template模板用來展示數據 Views視圖函數相當于controller接收請求,協調模型和模板

MTV的處理邏輯圖:

Flask基本準備使用前的工作:

創建Flask的虛擬環境:

在開發中每個項目都有自己的獨立環境,所以需要virtualenv創建環境 一般公司的開發都在ubuntu下,所以安裝如下: 1. $ sudo apt install virtualenv sudo apt install python-pip 注意:如果沒有apt 那么解決辦法 sudo apt update 更新源中的軟件包包管理工具apt update 更新源中的軟件包apt install xxx 安裝指定的軟件xxxapt remove xxx 卸載軟件(僅僅卸載軟件)apt autoremove xxx 卸載軟件(會卸載軟件和沒有用的依賴包) 接著下載virtualenv的管理工具: 2. sudo pip install virtualenvwrapper 注意有丟包的問題 virtualenvwrapper.sh安裝路徑/usr/local/bin/ 編輯bashrc文件 3. sudo vim ~/.bashrc 4.打開之后,進行下面編輯: #python export WORKON_HOME = /home/xxx(用戶名字)/.virtualenvs 5 注意需要創建.virtualenv的文件夾 mkdir $HOME/.virtualenvs 用來存放虛擬環境 source xxx(virtualenvwrapper.sh安裝路徑) virtualenvwrapper.sh 激活路徑 例如: #python export VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3 就是每次創建的虛擬環境都是依靠python3的 export WORKON_HOME=$HOME/.virtualenvs source /usr/local/bin/virtualenvwrapper.sh6 激活創建后后的環境變量 source .bashrc 7 創建3.x的虛擬環境 mkvirtualenv xxx(虛擬環境的名字) -p /usr/bin/python3 退出虛擬環境 deactivate 進入虛擬環境 workon ENV_NAME(虛擬環境的名字) 查詢路徑 whereis python3對于pip管理python包的操作: pip install xxx 安裝某一個軟件pip uninstall xxx 卸載某一個軟件pip list 列出所有的依賴包pip freeze 列出自己安裝的所有的依賴包

web網站的工作方式(http的請求方式):

Flask開始正文:

創建flask的虛擬環境mkvirtualenv Flaskpython1901 -p /usr/bin/python3 查看虛擬環境pip freezepip list虛擬環境遷移pip freeze > requirements.txt遷出pip install -r requirements.txt遷入 安裝pip install flask 創建項目mkdir python1901 mkdir Flaskday01 mkdir FirstFlask vim HelloFlask.py代碼結構from flask import Flask# 創建flask應用的對象,__name__如果以當前文件作為啟動文件那么__name__值是__main__,如果不是啟動文件那么它值的就是當前模塊的名字。app = Flask(__name__)@app.route("/index/")def index():return "helloflask"if __name__ == "__main__":app.run()啟動服務器 python 文件名字.py默認端口號 5000 只允許本機鏈接如果想修改端口號那么可以在run方法中添加參數在啟動的時候可以添加參數 在run()中debug是否開啟調試模式,開啟后修改過python代碼自動重啟如果修改的是html/js/css 那么不會自動重啟host主機,默認是127.0.0.1 指定為0.0.0.0代表本機ipport指定服務器端口號threaded是否開啟多線程 # 修改服務器端口號 可以在run方法中添加port屬性# 注意 注意port的數據類型 可以是整型 可以是字符串# app.run(port=8888)# 注意 默認情況下只允許本機訪問 可以修改run方法中的host屬性值為0.0.0.0# app.run(port='8888',host='0.0.0.0')# 默認情況下 修改python代碼必須要重啟服務器# 如果不想重啟服務器 那么需要加一個debug參數# 注意的是 修改python代碼 可以通過保存重啟服務器 那么如果是html css js等前端# 代碼 那么就必須重啟服務器# app.run(port='8888',debug=True,threaded=True)PIN碼全稱Personal Identification Number.就是SIM卡的個人識別密碼。手機的PIN碼是保護SIM卡的一種安全措施,防止別人盜用SIM卡,如果啟用了開機PIN碼,那么每次開機后就要輸入4到8位數PIN碼。 在輸入三次PIN碼錯誤時,手機便會自動鎖卡,并提示輸入PUK碼解鎖,需要使用服務密碼撥打運營商客服熱線,客服會告知初始的PUK碼,輸入PUK碼之后就會解鎖PIN碼。

Flask的腳本命令行參數:

命令行參數:作用就是可以在啟動程序的時候進行修改主機ip和端口號等等。1.安裝pip install flask-script如果安裝的慢可以用豆瓣源進行安裝:pip install flask-script -i https://pypi.douban.com/simple作用啟動命令行參數可以在腳本中添加自己喜歡的參數;2.初始化修改 app.py文件為manager.py3.將app對象交給manager來管理(和Django很相似)注意:Ctrl + Alt + Space 快速導入任意類 或者 倒包的快捷鍵 ctrl + space/alter + entermanager = Manager(app=app)修改 文件.run()為manager.run()4.運行python manager.py runserver -p xxx -h xxxx -d -r參數注意:不可以大寫。- p 端口 port- h 主機 host- d 調試模式 debug- r 重啟(重新加載) reload(restart)基本使用:index返回字符串@app.route('/index/')def index():return 'index'模板first.html@app.route('/first/')def hello():return render_template("test.html")返回值的類型類也是字符串;靜態文件css注意<link rel="stylesheet" href="/static/css/hello.css">對于開始啟動項目會出現以下錯誤狀態碼:404 路徑錯誤405 請求方式錯誤500 服務器錯誤302 重定向301 永久重定向

Flask的簡單運行過程:

所有Flask程序必須有一個程序實例。Flask調用視圖函數后,會將視圖函數的返回值作為響應的內容,返回給客戶端。一般情況下,響應內容主要是字符串和狀態碼。當客戶端想要獲取資源時,一般會通過瀏覽器發起HTTP請求。此時,Web服務器使用WSGI(Web Server Gateway Interface)協議,把來自客戶端的所有請求都交給Flask程序實例。WSGI是為 Python 語言定義的Web服務器和Web應用程序之間的一種簡單而通用的接口,它封裝了接受HTTP請求、解析HTTP請求、發送HTTP,響應等等的這些底層的代碼和操作,使開發者可以高效的編寫Web應用。程序實例使用Werkzeug來做路由分發(URL請求和視圖函數之間的對應關系)。根據每個URL請求,找到具體的視圖函數。 在Flask程序中,路由的實現一般是通過程序實例的route裝飾器實現。route裝飾器內部會調用add_url_route()方法實現路由注冊。調用視圖函數,獲取響應數據后,把數據傳入HTML模板文件中,模板引擎負責渲染響應數據,然后由Flask返回響應數據給瀏覽器,最后瀏覽器處理返回的結果顯示給客戶端。

Flask基礎結構

項目Apptemplates模板默認也需要和項目保持一致static靜態資源默認需要和我們的項目保持一致,在一個路徑中,指的Flask對象創建的路徑viewsmodels坑點一:起初視圖函數是在manager.py文件中,如果把視圖函數改到views.py文件中那么再執行過程中manager.py和其他的文件的惠出現找不到路徑的問題。出現如下: Not Found The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again 后臺回報出404錯誤;第二個坑--封裝__init__文件-- 在注冊藍圖之前,將app對象的初始化在__init__.py文件中,從而進行封裝。 from flask import Flask def create_app():app = Flask(__name__)return app 之后在manager.py輸入: from App import create_app app = create_app()

路由分發機制:

在flask中路由分發 1、如果路由名字后面沒有寫/ 那么請求的路徑后面就不可以寫/ 2、如果路由名字后面寫了/ 那么請求的路徑后面怎么寫都可以 # 所以在route()中路由名字一般情況下 /路由名字/ @app.route('/index/') # 路由的名字和視圖函數的名字 一般情況下一致,這個是開發的經驗; def index():return 'index'

對于解決路由路徑:

1、在注冊之前在__init__.py中進行app對象的初始化; from flask import Flask def create_app():app = Flask(__name__)return app 之后在manager.py輸入: from App import create_app app = create_app() 2、藍圖1. 宏偉藍圖(宏觀規劃)2. 藍圖也是一種規劃,主要用來規劃urls(路由)3. 藍圖基本使用- 安裝- pip install flask-blueprint- 初始化藍圖 blue = Blueprint('first',__name__)需要在view視圖函數中進行初始化;- 調用藍圖進行路由注冊 app.register_blueprint(blueprint=blue)代碼: views文件中: from flask import Blueprint # 初始化藍圖 blue = Blueprint('first',__name__) @blue.route("/") def hello_world():return "Hello"manager文件中: from App.views import blue # 注冊藍圖 app.register_blueprint(blueprint=blue)

視圖函數:

# 使用藍圖的坑 藍圖對象的創建和使用必須都要在views里 @app.route('/helloint/') def helloint():# 視圖函數的返回值類型 必須是一個字符串 元祖 response實例對象 不能是一個整數# return 1return 'this is return value'# 執行一個視圖函數 返回的是一個頁面 @app.route('/hellotemplate/') def hellotemplate():# 跳轉到一個頁面# return render_template('hellotemplate.html') # 從數據庫中將查詢到的數據 方到頁面中name = '本夕'return render_template('hellotemplate.html',name=name) 一般括號中的變量的命名與函數中變量是同名,開發經驗而已,然而在模板中接收的鍵(也就是括號中的變量參數)引用模板文件: <!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> {# link鏈接最低需要 rel、href屬性 script鏈接最低需要 type、src屬性#}<link rel="stylesheet" href="/static/css/hellotemplate.css"> </head> <body> {# ul>li*5 按table鍵#}<ul><li>許嵩</li><li>汪蘇瀧</li><li>徐良</li><li>星弟</li><li>小賤</li></ul> <!--老板說了 這需要睡3秒 如果尾款收到了那么就將此行代碼刪除--> {# 修改了頁面中的html css js ...需要重啟服務器#}<h3>{{ name }}</h3> </body> </html>

瀏覽器帶參數請求:

帶參數的請求從客戶端或者瀏覽器發過來的請求帶參數@blue.route('/getstudents/<id>/')def getstudents(id):return '學生%s'+id路由中包含路徑參數語法<converter:var_name>書寫的converter可以省略,默認類型就是stringconverterstring接收的時候也是str, 匹配到 / 的時候是匹配結束@blue.route('/getperson/<string:name>/')def getperson(name):print(name)print(type(name))return namepath接收的時候也是str, / 只會當作字符串中的一個字符處理,并且返回getperson1后面的所有string類型的參數;@blue.route('/getperson1/<path:name>/')def getperson1(name):print(name)print(type(name))return nameint@blue.route('/makemoney/<int:money>/')def makemoney(money):print(type(money))return '1'float參數寫的時候必須是一個float數;@blue.route('/makemoneyfloat/<float:money>/')def makemoney(money):print(type(money))return '1'uuiduuid 類型,一種格式@blue.route(('/getuu/'))def getuu():uu = uuid.uuid4()print(uu)return str(uu)------------------------------------@blue.route('/getuuid/<uuid:uuid>/')def getuuid(uuid):print(uuid)print(type(uuid))return '2'any任意一個已提供選項的任意一個 而不能寫參數外的內容 注意的是參數必須是字符串 如果不加雙引號,必須是字符,不可以寫整數 ,注意/必須寫@blue.route('/getany/<any(a,b):p>/')def getany(p):return '1'請求方式postman一種模擬請求工具方法參數中添加methods=['GET','POST']安裝https://blog.csdn.net/Shyllin/article/details/802577551. 默認支持GET,HEAD,OPTIONS2. 如果想支持某一請求方式,需要自己手動指定3. 在route方法中,使用methods=["GET","POST","PUT","DELETE"]應該都是大寫,因為在前后端分離中要求大小寫的區別;

簡易的注冊跳轉頁面:

# 視圖函數的設置 # 執行一個視圖函數 然后跳轉到注冊頁面 # 輸入用戶名字 密碼 然后點擊注冊 執行一個請求 然后輸出注冊成功 @blue.route('/toregister/') def toregister():return render_template('register.html') # 默認flask中允許使用get options head請求方式 不允許使用post請求方式 # methods允許訪問的請求方式 # 如果定義了methods 那么默認的請求方式將失效 # mothods常用的有5中 post get patch put delete @blue.route('/register/',methods=['POST','Get']) def register():return '注冊成功'#模板設置 <!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body> {#get 請求 參數是顯示到瀏覽器的地址路徑上的 不安全post 請求 參數不會顯示get請求參數的數據量 相對于比較 大概2k左右post 參數的數據量 相對于比較大表單默認請求方式是get #}<form action="/register/" method="get">name:<input type="text" name="name" placeholder="請輸入用戶名字"><br>password:<input type="password" name="password" placeholder="請輸入密碼"><br><input type="submit" value="提交"> </form> </body> </html>

反向解析:

反向解析,常用在模板中:獲取請求資源路徑url_for(藍圖的名字.方法名字)@blue.route("/heheheheheheheheheehhehe/", methods=["GET","POST","PUT"])def hehe():return "呵呵噠"@blue.route("/gethehe/")def get_hehe():p = url_for("first.hehe")return p 應用: views文件中 # 反向解析的應用 # 執行一個路由 跳轉到登錄頁面 然后點擊登錄 返回歡迎光臨 @blue.route('/tologin/') def tologin():return render_template('login.html') @blue.route('/login/',methods=['get','post']) def login():return '歡迎光臨紅浪漫'模板: <!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body><form action="{{ url_for('blue.login') }}" method="post">name:<input type="text" name="name" placeholder="please input your name"><br>pass:<input type="password" name="password" placeholder="please input your password"><br><button>提交</button></form> </body> </html>

request

request是一個Python中請求的內置對象 內置對象:不需要創建就可以直接使用的對象 屬性1.method **請求方法2.base_url去掉get參數的url3.host_url只有主機和端口號的url4.url完整的請求地址5.remote_addr **請求的客戶端地址6.args **- get請求參數的包裝,args是一個ImmutableMultiDict對象,類字典結構對象- 數據存儲也是key-value- 最外層是元組,次外層是大列表,列表中的元素是元組,元組中左邊是key,右邊是 value如:ImmutableMultiDict([('name', 'zhanshang'), ('name', 'wangwu')])7.form **- 存儲結構和args一致- 默認是接收post參數- 還可以接收 PUT,PATCH參數8.files ** form標簽中有一個參數 enctype=maltipart/form-data 請求方式必須是post 使用files接 收文件上傳9.headers 請求頭10.path路由中的路徑11.cookies請求中的cookie12.session 與request類似,也是一個內置對象,可以直接打印 print(session)

代碼:

# request對象的屬性 @blue.route('/testrequest/',methods=['get','post']) def testrequest():# 獲取請求的方式print(request.method)print(request.base_url)print(request.host_url)print(request.url)print(request.path)# 反扒print(request.remote_addr)# request.args.get方法的作用是獲取get請求方式的參數# 如果參數中 有2個或者2個以上的參數名字一致那么只是返回第一個符合條件的參數值# 如果想獲取所有的參數值 那么調用的方法是getlist getlist方法的返回值類型是列表print(request.args.get('name'))print(request.args.getlist('name'))# post請求方式 如何獲取請求參數print(request.form.get('name'))print(request.form.get('age'))print(request.headers)return 'testrequest'@blue.route('/upload/', methods=['GET', 'POST']) def upload_file():if request.method == 'POST':f = request.files['the_file']f.save('/var/www/uploads/uploaded_file.txt')如果你想知道上傳前文件在客戶端的文件名是什么,你可以訪問 filename 屬性。但請記住, 永遠不要信任這個值,這個值是可以偽造的。如果你要把文件按客戶端提供的文件名存儲在服務器上,那么請把它傳遞給 Werkzeug 提供的 secure_filename() 函數: from flask import request from werkzeug import secure_filename @blue.route('/upload', methods=['GET', 'POST']) def upload_file():if request.method == 'POST':f = request.files['the_file']f.save('/var/www/uploads/' + secure_filename(f.filename))

Response

對于視圖函數Response的返回類型,有如下幾種方式: 字符串、元組、response。 1、返回的值為字符串類型:如果只是單一的返回字符串,就是返回內容或者數據,在return返回值中還可以有第二個返回,就是放的是狀態碼@blue.route('/response/')def get_response():return '德瑪西亞',404render_template 函數渲染模板將模板變成字符串,返回給瀏覽器; @blue.route('/rendertemplate/')def render_temp():resp = render_template('Response.html')print(resp)print(type(resp))return rese,5002、返回response類型:make_response函數Response對象返回內容狀態碼@blue.route('/makeresponse/')def make_resp():resp = make_response('<h2>xxxxxxxx</h2>',502)print(resp)print(type(resp))return reseredirect函數重定向@blue.route('/redirect/')def make_redir():return redirect('/makeresponse/')重定向結合反向解析 url_for@blue.route('/redirect/')def make_redir():return redirect(url_for('first.make_resp'))Response()函數

代碼:

# response 1、返回字符串類型: # return 返回的是字符串 @blue.route('/testresponse/') def testresponse():return 'testresponse',500# render_template方法返回值類型也是一個字符串 @blue.route('/testresponse1/') def testresponse1():res = render_template('index.html')print(res)print(type(res))return res2、返回response類型: # return后面也可以返回一個response類型的對象 # make_response方法返回的是response類型的對象 @blue.route('/testresponse2/') def testresponse2():res = make_response('<h1>哈哈</h1>')print(type(res))return res@blue.route('/testresponse3/') def testresponse3():# redirect# 重定向的方法的返回值類型是response對象res = redirect(url_for('blue.testresponse2'))print(type(res))return res@blue.route('/testresponse4/') def testresponse4():res = Response('呵呵')print(type(res))return res

異常

abort拋出異常:直接拋出 顯示錯誤狀態碼 終止程序運行abort(404)eg:@blue.route('/makeabort/')def make_abort():abort(404/502)return '天還行'捕獲異常:@blue.errorhandler()- 異常捕獲- 可以根據狀態或 Exception進行捕獲- 函數中要包含一個參數,參數用來接收異常信息eg:@blue.errorhandler(502)def handler502(exception):return '不能讓你看到狀態碼'代碼: # 異常 @blue.route('/testabort/') def testabort():abort(404)return 'testabort'# errorhandler參數必須是拋出的狀態碼 如果沒有拋出將不會執行 @blue.errorhandler(404) # 注意函數必須有一個參數 該參數是exception def testhandler(exception):return '沒問題'

會話技術

1.請求過程Request開始,到Response結束 2.連接都是短連接 3.延長交互的生命周期 4.將關鍵數據記錄下來 5.Cookie是保存在瀏覽器端/客戶端的狀態管理技術 6.Session是服務器端的狀態管理技術

會話機制流程圖:

cookie

Cookie1.客戶端會話技術2.所有數據存儲在客戶端3.以key-value進行數據存儲層4.服務器不做任何存儲5.特性支持過期時間max_ageexpries根據域名進行cookie存儲不能跨網站(域名)不能跨瀏覽器自動攜帶本網站的所有cookie6.cookie是服務器操作客戶端的數據7.通過Response進行操作cookie登陸使用設置cookie response.set_cookie('username',username)response獲取cookie username = request.cookies.get('username','游客')request刪除cookie response.delete_cookie('username')response代碼: # @blue.route('/tologincookie/') # def tologincookie(): # return render_template('logincookie.html') # # # @blue.route('/logincookie/',methods=['get','post']) # def logincookie(): # # 獲取post請求的參數的值 # # get方法里面應該寫的是標簽的name屬性值 # # 標簽的name屬性值 是 <input type='text' name='name'> # # 如果后端想獲取前端頁面的屬性值 那么get方法中就應該寫 標簽的name的值 # name = request.form.get('name') # # # redirect方法的返回值類型就是repsonse對象 # response = redirect(url_for('blue.toindexcookie')) # # response.set_cookie('name',name) # # return response # # # 跳轉到indexcookie.html # @blue.route('/toindexcookie/') # def toindexcookie(): # return render_template('indexcookie.html')# 先創建一個登錄頁面 # 在登錄頁面中輸入用戶名字 # 點擊登錄 # 跳轉到indexcookie.html # 該頁面中 有一句話 歡迎xxx來到紅浪漫洗浴 @blue.route('/tologincookie/') def tologincookie():return render_template('logincookie.html')@blue.route('/logincookie/',methods = ['get','post']) def logincookie():# 獲取的文本框中的內容name = request.form.get('name')# 反向解析 獲取的是路由地址 redirect是執行另一個路由# url_for(‘blue.indexcookie’)===>/indexcookie/# redirect('/indexcookie/')response = redirect(url_for('blue.indexcookie'))# 將name存儲到了cookie上response.set_cookie('name',name)return response# 跳轉到index頁面 @blue.route('/indexcookie/') def indexcookie():# 獲取cookie中的namename = request.cookies.get('name','游客')return render_template('indexcookie.html',name=name)@blue.route('/logoutcookie/') def logoutcookie():response = redirect(url_for('blue.indexcookie'))response.delete_cookie('name')return response模板: logincookie.html <!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body> {#提交要觸發的是form表單中action的值#} {#{{ url_for('blue.logincookie') }} 是反向解析 反向解析的結果是路由路徑 阿#}<form action="{{ url_for('blue.logincookie') }}" method="post">name:<input type="text" name="name" placeholder="please input your name"><br><button>提交</button></form> </body> </html>indexcookie.html <!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body>歡迎{{ name }}來到紅浪漫,拖鞋手牌拿好,樓上二樓左拐。。。。<br><a href="{{ url_for('blue.logoutcookie') }}">退出</a> </body> </html>

session

Session1.服務端會話技術2.所有數據存儲在服務器中3.默認存在服務器的內存中- django默認做了數據持久化(存在了數據庫中)4.存儲結構也是key-value形勢,鍵值對【注】單純的使用session是會報錯的,需要使用在__init__方法中配置app.config['SECRET_KEY']='110'# session緩存的位置app.config['SESSION_TYPE']='redis'app.config['SESSION_KEY_PREFIX'] = 'python1901'Session(app=app)# session = Session()# session.init_app(app=app)# No module named 'redis' 沒有redis的模塊 需要pip install redis

session的使用步驟:

flask-session使用步驟:1 pip install flask-session2 app.config['SESSION_TYPE']='redis'3 Session(app=app)/session = Session() session.init_app(app=app)查看redis keys *查看session生命周期 ttl sessionsession登陸使用設置 session['username'] = username獲取 session.get('username')刪除response.delete_cookie('session')session.pop('username')代碼: # seesion @blue.route('/tologinsession/') def tologinsession():return render_template('loginsession.html')@blue.route('/loginsession/',methods = ['get','post']) def loginsession():# 獲取的文本框中的內容name = request.form.get('name')# 反向解析 獲取的是路由地址 redirect是執行另一個路由# url_for(‘blue.indexcookie’)===>/indexcookie/# redirect('/indexcookie/')response = redirect(url_for('blue.indexsession'))# 將name存儲到了cookie上session['name'] = namereturn response# 跳轉到index頁面 @blue.route('/indexsession/') def indexsession():# 獲取cookie中的namename = session.get('name','游客')return render_template('indexsession.html',name=name)@blue.route('/logoutsession/') def logoutsession():response = redirect(url_for('blue.indexsession'))session.pop('name')return responseloginsession.html <!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body> {#提交要觸發的是form表單中action的值#} {#{{ url_for('blue.logincookie') }} 是反向解析 反向解析的結果是路由路徑 阿#}<form action="{{ url_for('blue.loginsession') }}" method="post">name:<input type="text" name="name" placeholder="please input your name"><br><button>提交</button></form> </body> </html>indexsession.html <!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body>歡迎{{ name }}來到紅浪漫,拖鞋手牌拿好,樓上二樓左拐。。。。<br><a href="{{ url_for('blue.logoutcookie') }}">退出</a> </body> </html>

session持久化問題

Session- django中對session做了持久化,存儲在數據庫中- 可以修改到redis中flask中沒有對默認session進行任何處理- flask-session 可以實現session的數據持久化- 各種位置,更推薦使用redis- 緩存在磁盤上的時候,管理磁盤文件使用lru, 最近最少使用原則服務端會話技術Flask中沒有對默認Session進行處理,默認存在內存中Session需要持久化 Redis中實現方案插件 flask-sessionpip install flask-session在國內源安裝pip install flask-sessin -i https://pipy.douban.com/simple初始化Session對象 配置init中app.config['SESSION_TYPE'] = 'redis'持久化的位置初始化創建session的對象有2中方式 分別是以下兩種1 Session(app=app)2 se = Session() se.init_app(app = app)安裝redispip install redis需要配置SECRET_KEY='110'其他配置--視情況而定app.config['SESSION_KEY_PREFIX']='flask'查看redis內容redis-clikeys *get keysession生存時間31天 ttl sessionflask的session的生存時間是31天,django的session生存時間是15天

Template模板:

在前面的示例中,視圖函數的主要作用是生成請求的響應,這是最簡單的請求。實際上,視圖函數有兩個作用:處理業務邏輯和返回響應內容。在大型應用中,把業務邏輯和表現內容放在一起,會增加代碼的復雜度和維護成本。本節學到的模板,它的作用即是承擔視圖函數的另一個作用,即返回響應內容。 模板其實是一個包含響應文本的文件,其中用占位符(變量)表示動態部分,告訴模板引擎其具體值需要從使用的數據中獲取。使用真實值替換變量,再返回最終得到的字符串,這個過程稱為“渲染”。Flask使用Jinja2這個模板引擎來渲染模板。Jinja2能識別所有類型的變量,包括{}。 Jinja2模板引擎,Flask提供的render_template函數封裝了該模板引擎,render_template函數的第一個參數是模板的文件名,后面的參數都是鍵值對,表示模板中變量對應的真實值。

Jinja2官方文檔(http://docs.jinkan.org/docs/jinja2/)

變量

在模板中{{ variable }}結構表示變量,是一種特殊的占位符,告訴模板引擎這個位置的值,在渲染模板時從視圖函數中獲取數據;Jinja2除了能識別基本類型的變量,還能識別{};

<p>{{mydict['key']}}</p> <p>{{mylist[1]}}</p> <p>{{mylist[myintvar]}}</p>from flask import Blueprint, render_template blue = Blueprint('blue',__name__) @blue.route('/') def index():mydict = {'key':'silence is gold'}mylist = ['Speech', 'is','silver']myintvar = 0return render_template('vars.html',mydict=mydict,mylist=mylist,myintvar=myintvar)

反向路由:

Flask提供了url_for()輔助函數,可以使用程序URL映射中保存的信息生成URL;url_for()接收視圖函數名作為參數,返回對應的URL;

如調用url_for(‘index’,_external=True)返回的是絕對地址,在下面這個示例中是http://127.0.0.1:5000/index。

@app.route('/index') def index():return render_template('index.html')@app.route('/user/') def redirect():return url_for('index',_external=True)

過濾器

過濾器的本質就是函數。有時候我們不僅僅只是需要輸出變量的值,我們還需要修改變量的顯示,甚至格式化、運算等等,這就用到了過濾器。 過濾器的使用方式為:變量名 | 過濾器。 過濾器名寫在變量名后面,中間用 | 分隔。如:{{variable | capitalize}},這個過濾器的作用:把變量variable的值的首字母轉換為大寫,其他字母轉換為小寫。 其他常用過濾器如下:

字符串操作:

safe:禁用轉義;

<p>{{ '<em>hello</em>' | safe }}</p>

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

<p>{{ 'hello' | capitalize }}</p>

lower:把值轉成小寫;

<p>{{ 'HELLO' | lower }}</p>

upper:把值轉成大寫;

<p>{{ 'hello' | upper }}</p>

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

<p>{{ 'hello' | title }}</p>

trim:把值的首尾空格去掉;

<p>{{ ' hello world ' | trim }}</p>

reverse:字符串反轉;

<p>{{ 'olleh' | reverse }}</p>

format:格式化輸出;

<p>{{ '%s is %d' | format('name',17) }}</p>

striptags:渲染之前把值中所有的HTML標簽都刪掉;

<p>{{ '<em>hello</em>' | striptags }}</p>

列表操作

first:取第一個元素

<p>{{ [1,2,3,4,5,6] | first }}</p>

last:取最后一個元素

<p>{{ [1,2,3,4,5,6] | last }}</p>

length:獲取列表長度

<p>{{ [1,2,3,4,5,6] | length }}</p>

sum:列表求和

<p>{{ [1,2,3,4,5,6] | sum }}</p>

sort:列表排序

<p>{{ [6,2,3,1,5,4] | sort }}</p>

語句塊過濾(不常用):

{% filter upper %}this is a Flask Jinja2 introduction{% endfilter %}

自定義過濾器:

過濾器的本質是函數。當模板內置的過濾器不能滿足需求,可以自定義過濾器。自定義過濾器有兩種實現方式:一種是通過Flask應用對象的add_template_filter方法。還可以通過裝飾器來實現自定義過濾器。

自定義的過濾器名稱如果和內置的過濾器重名,會覆蓋內置的過濾器。

實現方式一:通過調用應用程序實例的add_template_filter方法實現自定義過濾器。該方法第一個參數是函數名,第二個參數是自定義的過濾器名稱。

def filter_double_sort(ls):return ls[::2] app.add_template_filter(filter_double_sort,'double_2')

實現方式二:用裝飾器來實現自定義過濾器。裝飾器傳入的參數是自定義的過濾器名稱。

@app.template_filter('db3') def filter_double_sort(ls):return ls[::-3]

Web表單:

web表單是web應用程序的基本功能。

它是HTML頁面中負責數據采集的部件。表單有三個部分組成:表單標簽、表單域、表單按鈕。表單允許用戶輸入數據,負責HTML頁面數據采集,通過表單將用戶輸入的數據提交給服務器。

在Flask中,為了處理web表單,我們一般使用Flask-WTF擴展,它封裝了WTForms,并且它有驗證表單數據的功能。

WTForms支持的HTML標準字段

字段對象 說明
StringField 文本字段
TextAreaField 多行文本字段
PasswordField 密碼文本字段
HiddenField 隱藏文本字段
DateField 文本字段,值為datetime.date格式
DateTimeField 文本字段,值為datetime.datetime格式
IntegerField 文本字段,值為整數
DecimalField 文本字段,值為decimal.Decimal
FloatField 文本字段,值為浮點數
BooleanField 復選框,值為True和False
RadioField 一組單選框
SelectField 下拉列表
SelectMultipleField 下拉列表,可選擇多個值
FileField 文本上傳字段
SubmitField 表單提交按鈕
FormField 把表單作為字段嵌入另一個表單
FieldList 一組指定類型的字段

WTForms常用驗證函數

驗證函數 說明
DataRequired 確保字段中有數據
EqualTo 比較兩個字段的值,常用于比較兩次密碼輸入
Length 驗證輸入的字符串長度
NumberRange 驗證輸入的值在數字范圍內
URL 驗證URL
AnyOf 驗證輸入值在可選列表中
NoneOf 驗證輸入值不在可選列表中

使用Flask-WTF需要配置參數SECRET_KEY。

CSRF_ENABLED是為了CSRF(跨站請求偽造)保護。 SECRET_KEY用來生成加密令牌,當CSRF激活的時候,該設置會根據設置的密匙生成加密令牌。

在HTML頁面中直接寫form表單:

#模板文件 <form method='post'><input type="text" name="username" placeholder='Username'><input type="password" name="password" placeholder='password'><input type="submit"> </form>

視圖函數中獲取表單數據:

from flask import Flask,render_template,request@app.route('/login',methods=['GET','POST']) def login():if request.method == 'POST':username = request.form['username']password = request.form['password']print username,passwordreturn render_template('login.html',method=request.method)

使用Flask-WTF實現表單。

配置參數:

app.config['SECRET_KEY'] = 'silents is gold'

模板頁面:

<form method="post">#設置csrf_token{{ form.csrf_token() }}{{ form.us.label }}<p>{{ form.us }}</p>{{ form.ps.label }}<p>{{ form.ps }}</p>{{ form.ps2.label }}<p>{{ form.ps2 }}</p><p>{{ form.submit() }}</p>{% for x in get_flashed_messages() %}{{ x }}{% endfor %}</form>

視圖函數:

#coding=utf-8 from flask import Flask,render_template,\redirect,url_for,session,request,flash#導入wtf擴展的表單類 from flask_wtf import FlaskForm #導入自定義表單需要的字段 from wtforms import SubmitField,StringField,PasswordField #導入wtf擴展提供的表單驗證器 from wtforms.validators import DataRequired,EqualTo app = Flask(__name__) app.config['SECRET_KEY']='1'#自定義表單類,文本字段、密碼字段、提交按鈕 class Login(Form):us = StringField(label=u'用戶:',validators=[DataRequired()])ps = PasswordField(label=u'密碼',validators=[DataRequired(),EqualTo('ps2','err')])ps2 = PasswordField(label=u'確認密碼',validators=[DataRequired()])submit = SubmitField(u'提交')@app.route('/login') def login():return render_template('login.html')#定義根路由視圖函數,生成表單對象,獲取表單數據,進行表單數據驗證 @app.route('/',methods=['GET','POST']) def index():form = Login()if form.validate_on_submit():name = form.us.datapswd = form.ps.datapswd2 = form.ps2.dataprint name,pswd,pswd2return redirect(url_for('login'))else:if request.method=='POST':flash(u'信息有誤,請重新輸入!')print form.validate_on_submit()return render_template('index.html',form=form) if __name__ == '__main__':app.run(debug=True)

控制語句

常用的幾種控制語句:

模板中的if控制語句,模板中的for循環語句

# 列表的遍歷 @blue.route('/scorelist/') def scorelist():content = 'girl'scorelist1 = [100,90,80,70,60]content1 = '<h1>蔡徐坤</h1>'return render_template('scorelist.html',scorelist1=scorelist1,content=content,content1=content1)<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body><ul>{% for s in scorelist1 %} {# {{ loop.index }}:{{ loop.index0 }}#}{% if loop.first %}<li style="color:#3cff3f;">{{ s }}</li>{% elif loop.last %}<li style="color:red;">{{ s }}</li>{% else %}<li style="color:blueviolet;">{{ s }}</li>{% endif %}{% endfor %}</ul>{{ content|upper|reverse }}{{ content1|safe }} </body> </html>

宏、繼承、包含

類似于python中的函數,宏的作用就是在模板中重復利用代碼,避免代碼冗余,然而Django的模板中沒有此定義。

Jinja2支持宏,還可以導入宏,需要在多處重復使用的模板代碼片段可以寫入單獨的文件,再包含在所有模板中,以避免重復。

定義宏

{% macro input() %}<input type="text"name="username"value=""size="30"/> {% endmacro %}

調用宏

{{ input() }}

定義帶參數的宏

{% macro input(name,value='',type='text',size=20) %}<input type="{{ type }}"name="{{ name }}"value="{{ value }}"size="{{ size }}"/> {% endmacro %}

調用宏,并傳遞參數

{{ input(value='name',type='password',size=40)}}

把宏單獨抽取出來,封裝成html文件,其它模板中導入使用

文件名可以自定義macro.html

{% macro function() %}<input type="text" name="username" placeholde="Username"><input type="password" name="password" placeholde="Password"><input type="submit"> {% endmacro %}

在其它模板文件中先導入,再調用

{% import 'macro.html' as func %} {% func.function() %}

模板繼承:

模板繼承是為了重用模板中的公共內容。一般Web開發中,繼承主要使用在網站的頂部菜單、底部。這些內容可以定義在父模板中,子模板直接繼承,而不需要重復書寫。

{% block top %}``{% endblock %}標簽定義的內容,相當于在父模板中挖個坑,當子模板繼承父模板時,可以進行填充。

子模板使用extends指令聲明這個模板繼承自哪?父模板中定義的塊在子模板中被重新定義,在子模板中調用父模板的內容可以使用super()。

父模板:base.html

<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>瞎寫的</title> {#block結構標簽#}{% block ext_css %}{% endblock %} </head> <body>{% block header %}頭部{% endblock %}{% block content %}文本{% endblock %}{% block footer %}底部{% endblock %}{% block ext_js %}js代碼{% endblock %} </body> </html>

子模板繼承:

base_a.html

{% extends 'base.html' %}{% block footer %}為什么今天這么蒙。。。 渾渾噩噩 感覺好象沒洗澡 {% endblock %}{% block header %}蒼滿的天涯是我的愛 {% endblock %}{% block ext_js %}綿綿的青山腳下花正開 {% endblock %}{% block content %}什么歌聲最亞封疆大吏封疆大吏方 {% endblock %}

base_b.html

{% extends 'base_a.html' %}{% block header %}{{ super() }} 調用父類,防止被子類覆蓋;梁博 表態 {% endblock %}{% block footer %}{% include 'include.html' %} {% endblock %}

include.html

<ul><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li> </ul>

模板繼承使用時注意點:

不支持多繼承。為了便于閱讀,在子模板中使用extends時,盡量寫在模板的第一行。不能在一個模板文件中定義多個相同名字的block標簽。當在頁面中使用多個block標簽時,建議給結束標簽起個名字,當多個block嵌套時,閱讀性更好。

包含(Include)

Jinja2模板中,除了宏和繼承,還支持一種代碼重用的功能,叫包含(Include)。它的功能是將另一個模板整個加載到當前模板中,并直接渲染。

示例:

include的使用結果看上面的代碼:

{\% include 'hello.html' %}

包含在使用時,如果包含的模板文件不存在時,程序會拋出TemplateNotFound異常,可以加上ignore missing關鍵字。如果包含的模板文件不存在,會忽略這條include語句。

示例:

include的使用加上關鍵字ignore missing

{\% include 'hello.html' ignore missing %}

宏、繼承、包含:

宏(Macro)、繼承(Block)、包含(include)均能實現代碼的復用。繼承(Block)的本質是代碼替換,一般用來實現多個頁面中重復不變的區域。宏(Macro)的功能類似函數,可以傳入參數,需要定義、調用。包含(include)是直接將目標模板文件整個渲染出來。

Flask中的特殊變量和方法:

在Flask中,有一些特殊的變量和方法是可以在模板文件中直接訪問的。

config 對象:

config 對象就是Flask的config對象,也就是 app.config 對象。{{ config.SQLALCHEMY_DATABASE_URI }}

request 對象:

就是 Flask 中表示當前請求的 request 對象,request對象中保存了一次HTTP請求的一切信息。

request常用的屬性如下:

屬性 說明 類型
data 記錄請求的數據,并轉換為字符串 *
form 記錄請求中的表單數據 MultiDict
args 記錄請求中的查詢參數 MultiDict
cookies 記錄請求中的cookie信息 Dict
headers 記錄請求中的報文頭 EnvironHeaders
method 記錄請求使用的HTTP方法 GET/POST
url 記錄請求的URL地址 string
files 記錄請求上傳的文件 *

{{ request.url }}

url_for 方法:

url_for() 會返回傳入的路由函數對應的URL,所謂路由函數就是被 app.route() 路由裝飾器裝飾的函數。如果我們定義的路由函數是帶有參數的,則可以將這些參數作為命名參數傳入。

{{ url_for('index') }}{{ url_for('post', post_id=1024) }}

get_flashed_messages方法:

返回之前在Flask中通過 flash() 傳入的信息列表。把字符串對象表示的消息加入到一個消息隊列中,然后通過調用 get_flashed_messages() 方法取出。

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

Models模型:

數據模型開發前的初始配置:

數據庫的設置

Web應用中普遍使用的是關系模型的數據庫,關系型數據庫把所有的數據都存儲在表中,表用來給應用的實體建模,表的列數是固定的,行數是可變的。它使用結構化的查詢語言。關系型數據庫的列定義了表中表示的實體的數據屬性。比如:商品表里有name、price、number等。 Flask本身不限定數據庫的選擇,你可以選擇SQL或NOSQL的任何一種。也可以選擇更方便的SQLALchemy,類似于Django的ORM。SQLALchemy實際上是對數據庫的抽象,讓開發者不用直接和SQL語句打交道,而是通過Python對象來操作數據庫,在舍棄一些性能開銷的同時,換來的是開發效率的較大提升。

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

1.數據交互的封裝 2.Flask默認并沒有提供任何數據庫操作的API,Flask中可以自己的選擇數據,用原生語句實現功能,但是原生SQL缺點:代碼利用率低,條件復雜代碼語句越過長,有很多相似語句;一些SQL是在業務邏輯中拼出來的,修改需要了解業務邏輯;也可以選擇ORM方便對數據的操作SQLAlchemyMongoEngine將對象的操作轉換為原生SQL優點:易用性,可以有效減少重復SQL;性能損耗少;設計靈活,可以輕松實現復雜查詢;移植性好; 3.Flask中并沒有提供默認ORM:ORM 對象關系映射通過操作對象,實現對數據的操作使用步驟:1.安裝pip install flask-sqlalchemy包2.在models文件中創建SQLALCHEMY對象db = SQLAlchemy()3.在init文件中進行配置: config中配置SQLALCHEMY_DATABASE_URI dialect+driver://username:password@host:port/database 數據庫 + 驅動 :// 用戶:密碼@ 主機:端口/數據庫 app.config['SQLALCHEMY_DATABASE_URI']='mysql+pymysql://root:1234@localhost:3306/flask1901' app.config['SQLALCHEMY_TRACK_MODIFICATIONS']=False 因為需要db來調用屬性 db.init_app(app=app) db.init_app(app=app)

代碼實現:

1 安裝sqlalchemy pip install flask-sqlalchemy 2 在models.py文件中創建SQLALCHEMY對象 # 模型的創建需要繼承 db.Model # 2個坑 + 1個大坑 # from App import db # db對象 如果放到了方法里,那么是不可以導入到其他模塊的 from flask_sqlalchemy import SQLAlchemy db = SQLAlchemy() # User模型 應該對應著 表 # 模型的屬性應該對應表的字段 class User(db.Model):# flask中 模型必須有主鍵ser could not assemble any primary key columnsid = db.Column(db.Integer,primary_key=True,autoincrement=True)# (in table 'user', column 'name'): VARCHAR requires a length on dialect mysql# flask的模型中的字符串必須有長度name = db.Column(db.String(32))age = db.Column(db.Integer)3.在__init__.py文件進行配置: from flask import Flask from App.models import db def create_app():app = Flask(__name__)# 數據庫的鏈接路徑# dialect + driver: // username: password @ host:port / database# 方言 驅動 用戶名子 密碼 主機 3306 數據庫的名字app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:1234@localhost:3306/flask1901'# 當使用sqlalchemy的時候 啟動服務器會報錯# SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead and will be disabled by default in the future. Set it to True or False to suppress this warning.# 在將來版本更新的過程中可能會遇到的錯誤# 如果還報錯 有2中情況 要么單詞打錯 要么放在了創建對象之后了app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = Falsedb.init_app(app=app)return app4.views.py文件中調用視圖函數: from flask import Blueprint from App import db blue = Blueprint('blue',__name__) # 創建一個模型,然后執行一個方法,就會生成相應的表 @blue.route('/createuser/') def createuser():db.create_all()return '創建成功'5.manager.py from flask_script import Manager from App import create_app from App.views import blue app = create_app() manager = Manager(app=app) app.register_blueprint(blueprint=blue) if __name__ == '__main__':manager.run()使用:定義模型:繼承Sqlalchemy對象中的model定義字段:主鍵一定要添加,所需要字段語法:db.Column( db.類型(),約束 )創建db.create_all()刪除db.drop_all()修改表名__tablename__ = "Worker"數據操作創建對象添加db.session.add(對象)db.session.commit()查詢模型.query.all()

項目拆分

項目拆分開發環境開發環境測試環境演示環境-類似線上環境也叫做預生產環境線上環境 也叫做 生產環境Flask輕量級框架,在一個文件中實現所有功能拆分項目MTV的樣子規劃項目結構manager.py程序入口app的創建Manager (flask-script管理對象)可以接收命令行參數App__init__創建Flask對象app加載settings文件調用init_ext方法調用init_blue方法settingsApp運行的環境配置運行環境ext(擴展的,額外的)用來初始化第三方的各種插件Sqlalchemy對象初始化 數據庫Session初始化views藍圖創建注冊到app上models定義模型__init__.py from flask import Flask from App import settings from App.ext import init_extdef create_app():app = Flask(__name__)# session# app.config['SECRET_KEY']='114'# app.config['SESSION_TYPE']='redis'# Session(app=app)# 建立與settings文件的關鍵# settings.ENV_NAME.get('develop')獲取的是settings文件中的value值# app.config.from_object(DevelopConfig)將類加載過來app.config.from_object(settings.ENV_NAME.get('develop'))# sqlalchemy# db.init_app(app=app)init_ext(app)return appext.py # 用來存放第三方擴展庫 from flask_session import Session from App.models import dbdef init_ext(app):app.config['SECRET_KEY']='114'app.config['SESSION_TYPE']='redis'Session(app=app)db.init_app(app=app)setting.py # 配置數據庫環境 def get_database_uri(DATABASE):dialect = DATABASE.get('dialect') or 'mysql'driver = DATABASE.get('driver') or 'pymysql'username = DATABASE.get('username') or 'root'password = DATABASE.get('password') or '1234'host = DATABASE.get('host') or 'localhost'port = DATABASE.get('port') or '3306'database = DATABASE.get('database') or 'flaskday03'# dialect+driver://username:password@host:port/databasereturn '{}+{}://{}:{}@{}:{}/{}'.format(dialect,driver,username,password,host,port,database)class Config():# 測試環境需要打開Test# 測試環境除外 需要打開DebugTest = FalseDebug = FalseSQLALCHEMY_TRACK_MODIFICATIONS = False# 每一套數據環境 都不一致 鏈接的是不同的數據庫 class DevelopConfig(Config):Debug = TrueDATABASE={'dialect':'mysql','driver':'pymysql','username':'root','password':'1234','host':'localhost','port':'3306','database':'flaskday03'}SQLALCHEMY_DATABASE_URI = get_database_uri(DATABASE)class TestConfig(Config):Test = TrueDATABASE = {'dialect': 'mysql','driver': 'pymysql','username': 'root','password': '1234','host': 'localhost','port': '3306','database': 'flaskday03'}SQLALCHEMY_DATABASE_URI = get_database_uri(DATABASE)class ShowConfig(Config):Debug = TrueDATABASE = {'dialect': 'mysql','driver': 'pymysql','username': 'root','password': '1234','host': 'localhost','port': '3306','database': 'flaskday03'}SQLALCHEMY_DATABASE_URI = get_database_uri(DATABASE)class ProductConfig(Config):Debug = TrueDATABASE = {'dialect': 'mysql','driver': 'pymysql','username': 'root','password': '1234','host': 'localhost','port': '3306','database': 'flaskday03'}SQLALCHEMY_DATABASE_URI = get_database_uri(DATABASE)ENV_NAME = {'develop':DevelopConfig,'test':TestConfig,'show':ShowConfig,'product':ProductConfig }models.py from flask_sqlalchemy import SQLAlchemy db = SQLAlchemy() class Student(db.Model):id = db.Column(db.Integer,primary_key=True,autoincrement=True)name = db.Column(db.String(32))views.py from flask import Blueprintfrom App.models import db, Studentblue = Blueprint('blue',__name__)@blue.route('/') def index():return 'index'@blue.route('/createstudent/') def createstudent():db.create_all()return 'ok'@blue.route('/savestudent/') def savestudent():s = Student()s.name = '小明'db.session.add(s)db.session.commit()return 'ok'manager.py from flask_script import Manager from App import create_app from App.views import blue app = create_app() manager = Manager(app=app) app.register_blueprint(blueprint=blue) if __name__ == '__main__':manager.run()

數據庫基本操作

在Flask-SQLAlchemy中,插入、修改、刪除操作,均由數據庫會話管理。會話用db.session表示。在準備把數據寫入數據庫前,要先將數據添加到會話中然后調用commit()方法提交會話。

數據庫會話是為了保證數據的一致性,避免因部分更新導致數據不一致。提交操作把會話對象全部寫入數據庫,如果寫入過程發生錯誤,整個會話都會失效。

數據庫會話也可以回滾,通過db.session.rollback()方法,實現會話提交數據前的狀態。

在Flask-SQLAlchemy中,查詢操作是通過query對象操作數據。最基本的查詢是返回表中所有數據,可以通過過濾器進行更精確的數據庫查詢。

DDL 數據定義語言 data ding language

專門用來操作表的 create drop alter

DML 數據操縱語言 data make language

專門用來操作記錄的 insert delete update

DQL 數據查詢語言

專門用來查詢記錄的 select

TCL 事務

commit roolback

將數據添加到會話中示例:

user = User(name='python') db.session.add(user) db.session.commit()

在視圖函數中定義模型類

from flask import Flask from flask_sqlalchemy import SQLAlchemyapp = Flask(__name__)#設置連接數據庫的URL app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:mysql@127.0.0.1:3306/Flask_test'#設置每次請求結束后會自動提交數據庫中的改動 app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = Trueapp.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True #查詢時會顯示原始SQL語句 app.config['SQLALCHEMY_ECHO'] = True db = SQLAlchemy(app)class Role(db.Model):# 定義表名__tablename__ = 'roles'# 定義列對象id = db.Column(db.Integer, primary_key=True)name = db.Column(db.String(64), unique=True)us = db.relationship('User', backref='role')#repr()方法顯示一個可讀字符串def __repr__(self):return 'Role:%s'% self.nameclass User(db.Model):__tablename__ = 'users'id = db.Column(db.Integer, primary_key=True)name = db.Column(db.String(64), unique=True, index=True)email = db.Column(db.String(64),unique=True)pswd = db.Column(db.String(64))role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))def __repr__(self):return 'User:%s'%self.name if __name__ == '__main__':db.drop_all()db.create_all()ro1 = Role(name='admin')ro2 = Role(name='user')db.session.add_all([ro1,ro2])db.session.commit()us1 = User(name='wang',email='wang@163.com',pswd='123456',role_id=ro1.id)us2 = User(name='zhang',email='zhang@189.com',pswd='201512',role_id=ro2.id)us3 = User(name='chen',email='chen@126.com',pswd='987654',role_id=ro2.id)us4 = User(name='zhou',email='zhou@163.com',pswd='456789',role_id=ro1.id)db.session.add_all([us1,us2,us3,us4])db.session.commit()app.run(debug=True)

DML模型操作

創建表:

db.create_all()

刪除表

db.drop_all()增db.session.add()將對象增加session中,然后用commit進行提交:@blue.route("/addperson/")def add_person():p = Person()p.p_name = "小明"p.p_age = 15db.session.add(p)db.session.commit()return "添加成功"db.session.add_all()eg:@blue.route("/addpersons/")def app_persons():persons = []for i in range(5):p = Person()p.p_name = "猴子請來的救兵%d" % random.randrange(100)p.p_age = random.randrange(70)persons.append(p)db.session.add_all(persons)db.session.commit()return "添加成功"# 插入一條數據 ro1 = Role(name='admin') db.session.add(ro1) db.session.commit() #再次插入一條數據 ro2 = Role(name='user') db.session.add(ro2) db.session.commit()# 一次插入多條數據 us1 = User(name='wang',email='wang@163.com',pswd='123456',role_id=ro1.id) us2 = User(name='zhang',email='zhang@189.com',pswd='201512',role_id=ro2.id) us3 = User(name='chen',email='chen@126.com',pswd='987654',role_id=ro2.id) us4 = User(name='zhou',email='zhou@163.com',pswd='456789',role_id=ro1.id) db.session.add_all([us1,us2,us3,us4]) db.session.commit()刪除db.session.delete(對象)基于查詢修改db.session.add(對象)基于查詢

常用的SQLAlchemy查詢過濾器

過濾器 說明
filter() 把過濾器添加到原查詢上,返回一個新查詢
filter_by() 把等值過濾器添加到原查詢上,返回一個新查詢
limit 使用指定的值限定原查詢返回的結果
offset() 偏移原查詢返回的結果,返回一個新查詢
order_by() 根據指定條件對原查詢結果進行排序,返回一個新查詢
group_by() 根據指定條件對原查詢結果進行分組,返回一個新查詢

常用的SQLAlchemy查詢執行器

方法 說明
all() 以列表形式返回查詢的所有結果
first() 返回查詢的第一個結果,如果未查到,返回None
first_or_404() 返回查詢的第一個結果,如果未查到,返回404
get() 返回指定主鍵對應的行,如不存在,返回None
get_or_404() 返回指定主鍵對應的行,如不存在,返回404
count() 返回查詢結果的數量
paginate() 返回一個Paginate對象,它包含指定范圍內的結果

查詢:filter_by精確查詢

返回名字等于wang的所有人

User.query.filter_by(name='wang').all()

first()返回查詢到的第一個對象

User.query.first()

all()返回查詢到的所有對象

User.query.all()

filter模糊查詢,返回名字結尾字符為g的所有數據。

User.query.filter(User.name.endswith('g')).all()

get(),參數為主鍵,如果主鍵不存在沒有返回內容

User.query.get()

邏輯非,返回名字不等于wang的所有數據。

User.query.filter(User.name!='wang').all()

邏輯與,需要導入and,返回and()條件滿足的所有數據。

from sqlalchemy import and_ User.query.filter(and_(User.name!='wang',User.email.endswith('163.com'))).all()

邏輯或,需要導入or_

from sqlalchemy import or_ User.query.filter(or_(User.name!='wang',User.email.endswith('163.com'))).all()

not_ 相當于取反

from sqlalchemy import not_ User.query.filter(not_(User.name=='chen')).all()

查詢數據后刪除

user = User.query.first() db.session.delete(user) db.session.commit() User.query.all()

更新數據

user = User.query.first() user.name = 'dong' db.session.commit() User.query.first()

使用update

User.query.filter_by(name='zhang').update({'name':'li'})

關聯查詢示例:角色和用戶的關系是一對多的關系,一個角色可以有多個用戶,一個用戶只能屬于一個角色。

查詢角色的所有用戶:

#查詢roles表id為1的角色 ro1 = Role.query.get(1) #查詢該角色的所有用戶 ro1.us

查詢用戶所屬角色:

#查詢users表id為3的用戶 us1 = User.query.get(3) #查詢用戶屬于什么角色 us1.role獲取單個數據get主鍵值獲取不到不會拋錯person = Person.query.get(3)db.session.delete(person)db.session.commit()firstperson = Person.query.first()獲取結果集xxx.query.allpersons = Person.query.all()xxx.query.filter_bypersons = Person.query.filter_by(p_age=15)xxx.query.filterpersons = Person.query.filter(Person.p_age < 18)persons = Person.query.filter(Person.p_age.__le__(15))persons = Person.query.filter(Person.p_name.startswith("小"))persons = Person.query.filter(Person.p_name.endswith("1"))persons = Person.query.filter(Person.p_name.contains("1"))persons = Person.query.filter(Person.p_age.in_([15, 11]))數據篩選order_bypersons = Person.query.order_by("-p_age")limitpersons = Person.query.limit(5)offsetpersons = Person.query.offset(5).order_by("-id")offset和limit不區分順序,offset先生效persons = Person.query.order_by("-id").limit(5).offset(5)persons = Person.query.order_by("-id").limit(5)persons = Person.query.order_by("-id").offset(17).limit(5)order_by 需要先調用執行persons = Person.query.order_by("-id").offset(17).limit(5)pagination分頁器需要想要的頁碼每一頁顯示多少數據原生persons = Person.query.offset((page_num - 1) * page_per).limit(page_per)參數(page,page_per,False(是否拋異常)persons = Person.query.paginate(page_num, page_per, False).items邏輯運算與and_ filter(and_(條件))huochelist = kaihuoche.query.filter(and_(kaihuoche.id == 1,kaihuoche.name == 'lc'))或or_ filter(or_(條件))huochelist = kaihuoche.query.filter(or_(kaihuoche.id == 1,kaihuoche.name =='lc'))非not_ filter(not_(條件)) 注意條件只能有一個huochelist = kaihuoche.query.filter(not_(kaihuoche.id == 1))inhuochelist = kaihuoche.query.filter(kaihuoche.id.in_([1,2,4]))

常用的SQLAlchemy字段類型

類型名 python中類型 說明
Integer int 普通整數,一般是32位
SmallInteger int 取值范圍小的整數,一般是16位
BigInteger int或long 不限制精度的整數
Float float 浮點數
Numeric decimal.Decimal 普通整數,一般是32位
String str 變長字符串
Text str 變長字符串,對較長或不限長度的字符串做了優化
Unicode unicode 變長Unicode字符串
UnicodeText unicode 變長Unicode字符串,對較長或不限長度的字符串做了優化
Boolean bool 布爾值
Date datetime.date 時間
Time datetime.datetime 日期和時間
LargeBinary str 二進制文件

常用的SQLAlchemy列選項

選項名 說明
primary_key 如果為True,代表表的主鍵
unique 如果為True,代表這列不允許出現重復的值
index 如果為True,為這列創建索引,提高查詢效率
nullable 如果為True,允許有空值,如果為False,不允許有空值
default 為這列定義默認值

常用的SQLAlchemy關系選項

選項名 說明
backref 在關系的另一模型中添加反向引用
primary join 明確指定兩個模型之間使用的聯結條件
uselist 如果為False,不使用列表,而使用標量值
order_by 指定關系中記錄的排序方式
secondary 指定多對多中記錄的排序方式
secondary join 在SQLAlchemy中無法自行決定時,指定多對多關系中的二級聯結條件

自定義模型類

定義模型

模型表示程序使用的數據實體,在Flask-SQLAlchemy中,模型一般是Python類,繼承自db.Model,db是SQLAlchemy類的實例,代表程序使用的數據庫。

類中的屬性對應數據庫表中的列。id為主鍵,是由Flask-SQLAlchemy管理。db.Column類構造函數的第一個參數是數據庫列和模型屬性類型。

如下示例:定義了兩個模型類,作者和書名。

#定義模型類-作者 class Author(db.Model):__tablename__ = 'author'id = db.Column(db.Integer,primary_key=True)name = db.Column(db.String(32),unique=True)email = db.Column(db.String(64))au_book = db.relationship('Book',backref='author')def __str__(self):return 'Author:%s' %self.name#定義模型類-書名 class Book(db.Model):__tablename__ = 'books'id = db.Column(db.Integer,primary_key=True)info = db.Column(db.String(32),unique=True)leader = db.Column(db.String(32))au_book = db.Column(db.Integer,db.ForeignKey('author.id'))def __str__(self):return 'Book:%s,%s'%(self.info,self.lead)

創建表 db.create_all()

查看author表結構 desc author

查看books表結構 desc books

flask-migrate

數據庫遷移

在開發過程中,需要修改數據庫模型,而且還要在修改之后更新數據庫。最直接的方式就是刪除舊表,但這樣會丟失數據。

更好的解決辦法是使用數據庫遷移框架,它可以追蹤數據庫模式的變化,然后把變動應用到數據庫中。

在Flask中可以使用Flask-Migrate擴展,來實現數據遷移。并且集成到Flask-Script中,所有操作通過命令就能完成。

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

首先要在虛擬環境中安裝Flask-Migrate。

pip install flask-migrate

文件:database.py

#coding=utf-8 from flask import Flask from flask_sqlalchemy import SQLAlchemy from flask_migrate import Migrate,MigrateCommand from flask_script import Shell,Managerapp = 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)def __repr__(self):return 'Role:'.format(self.name)#定義用戶 class User(db.Model):__tablename__ = 'users'id = db.Column(db.Integer, primary_key=True)username = db.Column(db.String(64), unique=True, index=True)def __repr__(self):return 'User:'.format(self.username) if __name__ == '__main__':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

回退數據庫

回退數據庫時,需要指定回退版本號,由于版本號是隨機字符串,為避免出錯,建議先使用python database.py db history命令查看歷史版本的具體版本號,然后復制具體版本號執行回退。

python database.py db downgrade 版本號

遷移的具體步驟:

使用安裝pip install flask-migrate初始化創建migrate對象需要使用app 和 db初始化 在ext文件中migrate = Migrate()migrate.init_app(app=app, db=db)懶加載初始化結合flask-script使用在manager文件上添加command (MigrateCommand)manager.add_command("db", MigrateCommand)python manager.py db xxxinit 第一次使用migrate 生成遷移文件不能生成模型定義完成從未調用,創建對象數據庫已經有模型記錄upgrade 升級downgrade 降級創建用戶文件python manager.py db migrate --message ‘創建用戶’

李晶:

__init__.py from flask import Flask from App import settings from App.ext import init_extdef create_app(envname):app = Flask(__name__) app.config.from_object(settings.ENV_NAME.get(envname))init_ext(app)return appext.py ''' flask-script flask-blueprint flask-session flask-sqlalchemy ''' from flask_migrate import Migrate from flask_session import Session from App.models import dbdef init_ext(app):# sessionapp.config['SECRET_KEY']='10010'app.config['SESSION_TYPE']='redis'Session(app=app)# sqlalchemydb.init_app(app=app)# migratemigrate = Migrate()# 注意參數的個數migrate.init_app(app=app,db=db)models.pyfrom flask_sqlalchemy import SQLAlchemydb = SQLAlchemy()# orm 對象關系映射 # 通過python代碼能創建表 刪除表 # flask-migrate 數據庫遷移 --》通過一些命令行參數 去直接的操作模型 不再書寫視圖函數class Animal(db.Model):id = db.Column(db.Integer,primary_key=True,autoincrement=True)name = db.Column(db.String(32))class Goods(db.Model):id = db.Column(db.Integer, primary_key=True, autoincrement=True)name = db.Column(db.String(32))class Person(db.Model):id = db.Column(db.Integer, primary_key=True, autoincrement=True)name = db.Column(db.String(32))p_age = db.Column(db.Integer,default=18)setting.pydef get_database_uri(DATABASE):dialect = DATABASE.get('dialect') or 'mysql'driver = DATABASE.get('driver') or 'pymysql'username = DATABASE.get('username') or 'root'password = DATABASE.get('password') or '1234'host = DATABASE.get('host') or 'localhost'port = DATABASE.get('port') or '3306'database = DATABASE.get('database') or 'flaskday03'return '{}+{}://{}:{}@{}:{}/{}'.format(dialect,driver,username,password,host,port,database)class Config():Test = FalseDebug = FalseSQLALCHEMY_TRACK_MODIFICATIONS = Falseclass DevelopConfig(Config):Debug = TrueDATABASE = {'dialect':'mysql','driver':'pymysql','username':'root','password':'1234','host':'127.0.0.1','port':'3306','database':'flaskday03'}# SQLALCHEMY_DATABASE_URI是數據庫鏈接的路徑SQLALCHEMY_DATABASE_URI = get_database_uri(DATABASE)class TestConfig(Config):Test = TrueDATABASE = {'dialect': 'mysql','driver': 'pymysql','username': 'root','password': '1234','host': '127.0.0.1','port': '3306','database': 'flaskday03'}SQLALCHEMY_DATABASE_URI = get_database_uri(DATABASE)class ShowConfig(Config):Debug = TrueDATABASE = {'dialect':'mysql','driver':'pymysql','username':'root','password':'1234','host':'127.0.0.1','port':'3306','database':'flaskday03'}SQLALCHEMY_DATABASE_URI = get_database_uri(DATABASE)class ProductConfig(Config):Debug = TrueDATABASE = {'dialect':'mysql','driver':'pymysql','username':'root','password':'1234','host':'127.0.0.1','port':'3306','database':'flaskday03'}SQLALCHEMY_DATABASE_URI = get_database_uri(DATABASE)ENV_NAME = {'develop':DevelopConfig,'test':TestConfig,'show':ShowConfig,'product':ProductConfig,'default':DevelopConfig, }views.py from _operator import not_from flask import Blueprint, request, render_templatefrom App.models import Goods, Person, dbblue = Blueprint('blue',__name__)@blue.route('/') def hello_world():goods = Goods()person = Person()return 'Hello World!'@blue.route('/saveperson/') def saveperson():p = Person()p.name = '蔡徐坤'p.age = 18db.session.add(p)# flask的增刪改 一定要commitdb.session.commit()return 'ok'@blue.route('/savelist/') def savelist():p = Person()p.name = '趙麗穎'p1 = Person()p1.name = '大牛'p_list = []p_list.append(p)p_list.append(p1)db.session.add_all(p_list)db.session.commit()return 'ok'@blue.route('/deleteperson/') def deleteperson():p = Person.query.get(1)db.session.delete(p)db.session.commit()return 'ok'@blue.route('/updateperson/') def updateperson():p = Person.query.get(2)p.name = '大妞'db.session.add(p)db.session.commit()return 'ok'# 查詢單個數據 @blue.route('/getone/') def getone(): # 查詢 模型.query.xxx # 返回的是模型類型 # p = Person.query.get(3) # print(p.name) # print(type(p))p = Person.query.first()print(p.name)print(type(p))return 'getone'# 獲取結果集 @blue.route('/getresult/') def getresult():# 獲取的全部數據# ps = Person.query.all()# for p in ps:# print(p.name)# print(ps)# # list# print(type(ps))# return 'ok' # 條件查詢 filter filter_by # filter 和 filter_by的使用 # filter在參數中需要使用模型.屬性 == 值 # filter_by在參數中需要使用 屬性 = 值 # flask中條件過濾的方法返回值類型是 basequery對象 # django中條件過濾的方法的返回值類型是 queryset # p1 = Person.query.filter(Person.age == 18) # print(p1) # print(type(p1)) # p = Person.query.filter_by(age = 18) # print(p) # print(type(p)) # # lt ==> little than # lte ==> litte than equals # gt ==> great than # gte ==> great than equals # p = Person.query.filter(Person.age.__lt__(20)) # p = Person.query.filter(Person.age.__le__(20)) # p = Person.query.filter(Person.age.__gt__(20)) # p = Person.query.filter(Person.age.__ge__(20)) # p = Person.query.filter(Person.name.startswith('張')) # p = Person.query.filter(Person.name.endswith('三')) # p = Person.query.filter(Person.name.contains('王'))p = Person.query.filter(Person.name.in_(['李四','王五']))for p1 in p:print(p1.name,p1.age)print(p)print(type(p))return 'ok'# 數據篩選 @blue.route('/getselect/') def getselect():# p = Person.query.order_by('age')# 降序問題。。。。。。。# p = Person.query.order_by('-p_age')# 取前幾個數據# p = Person.query.limit(5)# 除了前幾個的數據# p = Person.query.offset(5)# 當offset和limit同時使用的時候 永遠都是offset在前 limit在后# 空的 跳過前三個 取3個# p = Person.query.limit(3).offset(3)# 跳過前三個 取3個# p = Person.query.offset(3).limit(3)# 語法錯誤# p = Person.query.offset(3).limit(3).order_by('p_age')# 當order_by 和 offset limit連用的時候 那么 order_by 必須放在前面# p = Person.query.order_by('p_age').offset(3).limit(3)# 降序# p = Person.query.order_by(db.desc(Person.p_age))p = Person.query.order_by(-Person.p_age)for p1 in p:print(p1.id,p1.name,p1.p_age)return 'ok'# 分頁 # page 第幾頁 per_page/pagesize 頁的大小@blue.route('/getpage/') def getpage():# 在瀏覽器的地址欄上輸入 127.0.0.1:5000/getpage?page=1&per_page=3page = int(request.args.get('page'))per_page = int(request.args.get('per_page'))# 1 0 3# 2 3 3# 3 6 3# offset(0).limit(3) 123# offset(3).limit(3) 456# offset(6).limit(3) 789# persons = Person.query.offset((page-1)*per_page).limit(per_page)# paginate方法的返回值類型是 pagination對象 該對象不可以直接用于迭代 如果# 想遍歷該對象 那么需要調用items# persons = Person.query.paginate(page,per_page,False).itemspersons = Person.query.paginate(page=page,per_page=per_page,error_out=False)return render_template('personlist.html',persons=persons,page=page,per_page=per_page)# 邏輯運算 @blue.route('/getlogic/') def getlogic():# persons = Person.query.filter(or_(Person.p_age==18,Person.p_age==19))# persons = Person.query.filter(and_(Person.p_age==18,Person.name=='大牛'))persons = Person.query.filter(not_(Person.id == 5))print(persons)print(type(persons))for person in persons:print(person.name,person.p_age)return 'ok'manager.py from flask_migrate import MigrateCommand from flask_script import Managerfrom App import create_app from App.views import blueapp = create_app('develop')manager = Manager(app=app)manager.add_command('db',MigrateCommand)app.register_blueprint(blueprint=blue)if __name__ == '__main__':manager.run()數據庫遷移flask-migrate使用步驟:1 pip install flask-migrate2 migrate = Migrate()3 migrate.init_app(app=app,db=db)4 manager.add_command('db',MigrateCommand)5 開始遷移python manager.py db initpython manager.py db migratepython manager.py db upgradepython manager.py db downgrade當不能生成遷移文件的時候:1 引用模型2 刪除遷移的表和遷移文件personlist.py<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body><ul>{% for person in persons %}<li>{{ person.name }}</li>{% endfor %}</ul><a href="{{ url_for('blue.getpage') }}?page={{ page - 1 }}&per_page={{ per_page }}">上一頁</a><a href="{{ url_for('blue.getpage') }}?page={{ page + 1 }}&per_page={{ per_page }}">下一頁</a> </body> </html>

模型關系

一對多 class Parent(db.Model):id=db.Column(db.Integer,primary_key=True,autoincrement=True)name=db.Column(db.String(30),unique=True)children=db.relationship("Child",backref="parent",lazy=True)def __init__(self):name=self.nameclass Child(db.Model):id = db.Column(db.Integer, primary_key=True)name = db.Column(db.String(30), unique=True)parent_id = db.Column(db.Integer, db.ForeignKey('parent.id'))def __init__(self):name = self.namerelationship函數sqlalchemy對關系之間提供的一種便利的調用方式,關聯不同的表;backref參數對關系提供反向引用的聲明,在Address類上聲明新屬性的簡單方法,之后可以在my_address.person來獲取這個地址的person; lazy參數'select'(默認值)SQLAlchemy 會在使用一個標準 select 語句時一次性加載數據;'joined'讓 SQLAlchemy 當父級使用 JOIN 語句是,在相同的查詢中加載關系;'subquery'類似 'joined' ,但是 SQLAlchemy 會使用子查詢;'dynamic':SQLAlchemy 會返回一個查詢對象,在加載這些條目時才進行加載數據,大批量數據查詢處理時推薦使用。 ForeignKey參數代表一種關聯字段,將兩張表進行關聯的方式,表示一個person的外鍵,設定上必須要能在父表中找到對應的id值添加eg:@blue.route('/add/')def add():p = Parent()p.name = '張三'c = Child()c.name = '張四'c1 = Child()c1.name = '王五'p.children = [c,c1]db.session.add(p)db.session.commit()return 'add success' 查eg:主查從 --》 Parent--》Child@blue.route('/getChild/')def getChild():clist = Child.query.filter(Parent.id == 1)for c in clist:print(c.name)return 'welcome to red remonce'從查主@blue.route('/getParent/')def getParent():p = Parent.query.filter(Child.id == 2)print(type(p))print(p[0].name)return '開洗'一對一一對一需要設置relationship中的uselist=Flase,其他數據庫操作一樣。多對多 class User(db.Model):id = db.Column(db.Integer,primary_key=True,autoincrement=True)name = db.Column(db.String(32))age = db.Column(db.Integer,default=18)class Movie(db.Model):id = db.Column(db.Integer,primary_key=True,autoincrement=True)name = db.Column(db.String(32))class Collection(db.Model):id = db.Column(db.Integer,primary_key=True,autoincrement=True)u_id = db.Column(db.Integer,db.ForeignKey(User.id))m_id = db.Column(db.Integer,db.ForeignKey(Movie.id))購物車添加@blue.route('/getcollection/')def getcollection():u_id = int(request.args.get('u_id'))m_id = int(request.args.get('m_id'))c = Collection.query.filter(Collection.u_id == u_id).filter_by(m_id = m_id)if c.count() > 0:print(c.first().u_id,c.first().m_id)# print(c)# print(type(c))# print('i am if')return '已經添加到了購物車中'else:c1 = Collection()c1.u_id = u_idc1.m_id = m_iddb.session.add(c1)db.session.commit()return 'ok'

總結

以上是生活随笔為你收集整理的Flask视图、模板、模型的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

欧美在线不卡一区 | 国产精品久久久久影院 | 狠狠狠干 | 国产成人精品一区二区三区在线观看 | 视频直播国产精品 | 91亚洲精 | 在线观看激情av | 国产尤物一区二区三区 | 久久av在线播放 | 日b视频在线观看网址 | 国产91aaa| 久久激情电影 | 欧洲一区二区在线观看 | 国产精品视频久久 | 午夜电影久久 | 欧美一区成人 | 91精品视频在线免费观看 | 日韩一级黄色片 | 日韩电影在线观看一区二区三区 | 国产精品视频观看 | 最新中文字幕在线观看视频 | 国产黄色片在线 | 亚洲精品免费观看视频 | 天天操天天爽天天干 | 九九久久久久久久久激情 | 黄色的片子| 九色视频网址 | 91成人精品一区在线播放 | 亚洲精品视频在线观看网站 | 日韩精品不卡在线观看 | 黄色av影视 | 不卡电影免费在线播放一区 | 久久se视频 | 欧美久久综合 | 日韩乱理 | 超级碰碰碰视频 | 久热免费在线观看 | 国产视频色 | 国产美女在线观看 | 亚洲国产成人在线 | 干干夜夜 | 狠狠色伊人亚洲综合网站色 | .精品久久久麻豆国产精品 亚洲va欧美 | 又黄又刺激视频 | 国产视频中文字幕 | 超碰在线观看av | 中文字幕专区高清在线观看 | 欧美天天干 | 激情视频免费在线 | 国产精品美女久久久久久久久久久 | 97超碰人| 丁香视频在线观看 | 午夜精选视频 | 亚洲va综合va国产va中文 | 免费a v观看 | 免费观看午夜视频 | 久热久草在线 | 蜜臀久久99精品久久久久久网站 | 久久精品99国产国产 | 在线免费av观看 | 日韩黄色中文字幕 | 日韩中文字幕国产 | 国产69久久久 | 三级a毛片 | а天堂中文最新一区二区三区 | 视频在线91 | 国产做aⅴ在线视频播放 | 成人天堂网| 欧美国产精品一区二区 | 98久久| 天天干天天干天天 | 欧亚日韩精品一区二区在线 | av一本久道久久波多野结衣 | 四虎影视成人精品 | 免费黄在线观看 | 99看视频在线观看 | 色无五月 | 中文字幕电影一区 | 日韩av影视在线观看 | 日韩欧美有码在线 | www久久com| 国产成人资源 | 在线观看亚洲国产 | 国产中年夫妇高潮精品视频 | 狠狠狠色狠狠色综合 | 操操操天天操 | 天天操夜夜干 | 97在线成人 | 久久天天操 | 私人av| 国产成人久久av | 亚洲精品国产精品国自产观看浪潮 | 欧美日韩在线播放 | 欧美极品少妇xbxb性爽爽视频 | 天天曰天天射 | 国产a级片免费观看 | 久久久午夜视频 | av在线观| 久久精品国产第一区二区三区 | 狠狠狠色丁香综合久久天下网 | www.久热| 操操操日日 | 91成人久久 | 91在线视频在线观看 | 最新的av网站 | 国产精品成人免费精品自在线观看 | 国产精品99久久久精品 | 国产亚洲久一区二区 | 国产精品国产自产拍高清av | 69久久夜色精品国产69 | 国产视频第二页 | 精品在线一区二区三区 | 亚洲午夜精品久久久久久久久久久久 | 日韩在线观看网站 | 九九九电影免费看 | 免费亚洲黄色 | 国产精品精品国产婷婷这里av | 亚洲欧美日本一区二区三区 | 91精品国产麻豆 | 国产一区二区高清视频 | 在线观看蜜桃视频 | 日韩欧美高清在线 | 激情五月婷婷激情 | av片子在线观看 | 天天弄天天操 | 亚洲天天综合 | 九九视频精品在线 | 日韩免费三级 | 91av久久 | 91福利视频久久久久 | 久久黄色免费观看 | 伊人电影天堂 | 日韩三级不卡 | 天天干天天想 | 黄a在线观看| 99久久99视频只有精品 | 久久综合九色综合欧美就去吻 | 午夜av在线播放 | 国产精品视屏 | 日韩色在线观看 | 久久久久久97三级 | 国内三级在线 | 在线观看一区 | 欧美在线你懂的 | 免费看的黄色的网站 | 精品久久久久久久久久久久久久久久久久 | 在线精品视频在线观看高清 | 久久国产视频网站 | 夜夜躁天天躁很躁波 | 欧美日韩在线精品一区二区 | 天天操天天操 | 在线中文字幕视频 | 成年人国产精品 | 国产日韩视频在线播放 | 国产一区二区手机在线观看 | 激情大尺度视频 | 中文字幕在线视频一区二区 | 91最新视频 | 在线免费黄色 | av不卡中文字幕 | 国产91在线 | 美洲 | 九九久久免费 | 国产欧美精品在线观看 | 久久精品美女视频 | 69国产精品成人在线播放 | 在线观看视频97 | 成年人电影免费看 | 亚洲免费在线观看视频 | 国产在线2020| 午夜丁香视频在线观看 | 91香蕉视频 | 亚洲视频axxx | 麻豆视频免费在线播放 | 亚洲免费观看在线视频 | 九九激情视频 | 国产精品久久久久久久婷婷 | 黄色精品视频 | 麻豆高清免费国产一区 | 久久国产色 | 在线观看黄色国产 | 精品国产一区二区三区噜噜噜 | 亚洲欧洲精品视频 | 91视频久久久 | 超碰成人av | 久久国产精品精品国产色婷婷 | 91九色性视频 | 国产一区精品在线观看 | 99精品观看| 日韩网站在线看片你懂的 | 五月天精品视频 | 国产高清视频免费最新在线 | 精品自拍av | 97av精品 | 91经典在线| 波多野结衣精品在线 | 婷婷丁香色综合狠狠色 | 午夜美女福利直播 | 欧美成人在线网站 | 青春草视频在线播放 | www.五月婷婷.com | 日韩在线一二三区 | 久久96| 三级午夜片 | 国产高清99 | 久久五月天综合 | 亚洲天堂色婷婷 | 最近免费中文视频 | 97精品国产97久久久久久粉红 | 欧美精品乱码久久久久久 | 免费高清无人区完整版 | 日本视频精品 | 国产亚洲精品福利 | 日韩精品一区二区在线观看视频 | 日本精品在线 | 亚洲综合色丁香婷婷六月图片 | 天天操天天舔天天爽 | 亚洲午夜精品一区二区三区电影院 | 国产精品白虎 | 在线免费三级 | 最新av在线播放 | 亚洲精品国偷自产在线99热 | 96久久久 | 日日日日| 国产成人亚洲在线观看 | 久久久久国产精品免费免费搜索 | 午夜视频导航 | 日韩二区三区在线 | 中文视频在线播放 | 欧洲精品久久久久毛片完整版 | 国产片网站 | 手机看片久久 | 激情www| 狠狠躁夜夜a产精品视频 | 亚洲一级黄色 | 青青草国产免费 | 天天天综合 | 久久www免费人成看片高清 | 亚洲激情 在线 | 五月天九九 | 天天做日日爱夜夜爽 | 亚洲国产丝袜在线观看 | 91看片麻豆 | 999久久久免费视频 午夜国产在线观看 | 伊人中文在线 | 精品视频在线看 | 欧美一区二区在线看 | 色多视频在线观看 | 亚洲精品乱码久久久久久蜜桃91 | 国产高清成人 | 五月婷婷视频 | 亚洲精品99久久久久久 | 欧美日韩高清一区二区 国产亚洲免费看 | 奇米影视8888在线观看大全免费 | 97超碰福利久久精品 | 444av| 国产精品18久久久久久vr | 国产精品6 | 婷婷六月天在线 | 国产精品一区二区三区免费看 | 国产午夜一级毛片 | 欧美精品在线观看 | 在线中文字幕视频 | 一区二区三区在线看 | 亚洲午夜在线视频 | 久久久久久97三级 | 天天干,天天射,天天操,天天摸 | 精品影院一区二区久久久 | 成人福利在线 | 国产精品成人免费一区久久羞羞 | 国产在线播放观看 | 中文字幕频道 | 国产亚洲综合在线 | 国产婷婷vvvv激情久 | 久久久精品国产免费观看一区二区 | 免费麻豆网站 | 精品国产伦一区二区三区观看方式 | 97超碰在线久草超碰在线观看 | 色婷婷五| 狠狠的操 | 免费一级片在线观看 | 麻豆视频免费在线 | 亚洲黄色av| 亚洲最大激情中文字幕 | 九九综合久久 | 国产黄免费在线观看 | 日韩免费电影网 | 久久久久国产精品午夜一区 | 国产精品女同一区二区三区久久夜 | 色综合久久中文字幕综合网 | 亚洲一区二区观看 | 亚洲婷婷伊人 | 狠狠干 狠狠操 | 国产97视频 | 婷婷丁香九月 | 欧美激情精品 | 久久久香蕉视频 | 日韩在线观看不卡 | 亚洲精品视频在线看 | 深夜免费福利 | a视频在线看 | 免费高清无人区完整版 | 五月天综合色 | 欧美日韩中文字幕在线视频 | 在线免费观看黄色 | 精品国产欧美一区二区三区不卡 | 涩涩网站在线播放 | 久久久精品福利视频 | 精品在线观看一区二区三区 | 久久午夜免费观看 | 91亚洲永久精品 | 久久久久久久影视 | 成人三级网址 | 99视频 | 婷婷伊人综合亚洲综合网 | 又色又爽又黄 | 中文一区二区三区在线观看 | 国产精品三级视频 | 丝袜精品视频 | 黄色成人免费电影 | 国产人成在线视频 | av免费在线免费观看 | av在线8 | 国产精品高潮久久av | 97超碰超碰久久福利超碰 | a黄在线观看 | 中文字幕第一页在线 | 天天操操操操操操 | 国产成人精品免费在线观看 | aav在线| 日日操日日操 | 九色精品免费永久在线 | 国产成人精品在线观看 | 一区二区电影在线观看 | 国产精品爽爽久久久久久蜜臀 | 日韩av电影手机在线观看 | 久久久久久免费网 | 欧美精品999 | 91在线免费看片 | 91av中文字幕 | 在线观看视频91 | 日韩电影中文,亚洲精品乱码 | 久久国产欧美日韩 | 九色91在线视频 | 高清不卡一区二区三区 | 999久久国精品免费观看网站 | 最新av网址在线观看 | 精品国产一二三 | 香蕉国产91| 免费在线国产视频 | 国产中文字幕在线看 | 久久免费视频这里只有精品 | 亚洲精品国产成人 | 麻豆精品国产传媒 | 成人影视免费看 | 婷婷九月激情 | 999在线视频 | 五月天电影免费在线观看一区 | 欧美成人性战久久 | 激情五月婷婷激情 | 久久久久综合 | 91麻豆传媒 | 国产精品视频免费看 | 国产小视频在线免费观看视频 | 欧美永久视频 | 日本精品视频一区 | 日韩特黄av | 欧美精品首页 | 国产手机视频在线播放 | 久久国产精品色婷婷 | 黄色片网站免费 | 欧美网站黄色 | 亚洲午夜精品久久久 | 久草精品电影 | 久久久免费毛片 | 97在线观看免费观看高清 | 四虎永久精品在线 | 色狠狠久久av五月综合 | 不卡视频国产 | 美女免费视频网站 | 在线观看精品黄av片免费 | 亚洲资源 | 亚洲国产一区在线观看 | 久久久久久麻豆 | 国产免费嫩草影院 | 日韩在线看片 | 亚洲一级片av | 亚洲成人黄色在线 | 国产偷v国产偷∨精品视频 在线草 | 亚洲国内精品 | 99视频这里只有 | 中文字幕久久亚洲 | 久久99九九99精品 | 美女又爽又黄 | 九九有精品 | 91精品国产成 | 日韩精品一区二区在线视频 | 久99视频 | 狠狠干狠狠操 | 粉嫩一二三区 | 亚洲一区二区视频 | 国产女人免费看a级丨片 | 黄色一级免费 | 伊人国产在线播放 | 中文字幕在线一二 | 精品亚洲午夜久久久久91 | 国产精品黄色 | 亚洲1区在线 | 国产精品一区二区三区在线播放 | 国产亚洲精品久久久久久无几年桃 | 97超碰.com | 在线视频婷婷 | 国产精品原创 | 国产精品 中文在线 | 欧美亚洲一级片 | 精品黄色片 | 99久久超碰中文字幕伊人 | 最新中文字幕在线播放 | 亚洲另类视频在线 | 伊人www22综合色 | 久久在线电影 | 亚洲精品乱码久久久久久蜜桃欧美 | 亚洲成人第一区 | 中文字幕电影网 | 精品一区二区在线免费观看 | 日韩av手机在线看 | 欧美日韩在线免费视频 | 成人精品一区二区三区中文字幕 | 激情黄色av | 一区二区网 | av线上看| 精品欧美乱码久久久久久 | 最近乱久中文字幕 | av丝袜天堂| 狠狠色噜噜狠狠 | 最近中文字幕 | 国产在线观看中文字幕 | 精品国产视频在线 | 欧美成人va| 婷婷www| 国产性xxxx | 亚洲视频精选 | 欧美精品一二 | 欧美国产日韩中文 | 天天爱天天射 | 国产一区 在线播放 | 国产精品一区二区吃奶在线观看 | 亚洲免费在线播放视频 | 亚洲精品91天天久久人人 | 色资源中文字幕 | 夜夜躁日日躁 | 91精品一区二区三区久久久久久 | 97超碰网| 欧美性超爽 | 久久久久久久久久久福利 | 免费高清av在线看 | 国产高清中文字幕 | 97超碰国产精品女人人人爽 | 黄色av网站在线观看免费 | 天天摸夜夜添 | 日韩在线视频线视频免费网站 | 亚洲自拍偷拍色图 | 国产麻豆电影 | 日韩在线视频播放 | 久久综合狠狠 | 国产日产精品一区二区三区四区的观看方式 | 激情网综合 | 久久人人爽人人片 | 国产精品99免视看9 国产精品毛片一区视频 | 九九视频这里只有精品 | 国产黄免费看 | 国产成人一区在线 | 中文在线字幕免 | 夜夜操网站 | 久久久五月天 | 天天做日日爱夜夜爽 | 一区中文字幕在线观看 | 国产群p | 日韩免费网站 | 美国人与动物xxxx | 日日综合网 | 久久久久久久久久久影视 | 国产亚洲精品无 | av色综合网 | 国产欧美精品一区二区三区 | 欧美专区国产专区 | 欧美激情视频久久 | 国产精品va在线观看入 | 日韩三区在线观看 | 国产一区二三区好的 | 97超级碰 | 日本激情视频中文字幕 | 国产999视频在线观看 | 中文字幕在线资源 | 极品久久久久久久 | 久久激情精品 | 久久高清视频免费 | 亚洲一区精品人人爽人人躁 | 综合激情伊人 | 91在线观看欧美日韩 | 久久国内精品视频 | 深爱激情久久 | 国产精品麻豆免费版 | 日韩成人免费观看 | 国产又粗又猛又爽 | 黄色三级免费网址 | 99精品美女| 97超碰在线视 | 黄色免费大片 | 国产成人精品久久亚洲高清不卡 | 精品国产诱惑 | 亚洲激情电影在线 | 麻豆国产网站 | 免费看一级特黄a大片 | 久久精品久久99精品久久 | 亚洲精品乱码久久久久久蜜桃91 | 激情影院在线观看 | 国产黄色a | 超碰个人在线 | 国产精品1区2区在线观看 | 久久试看 | 国产成人一区二区三区 | 免费观看的黄色 | www最近高清中文国语在线观看 | 日韩,精品电影 | 免费在线观看黄网站 | 九九在线视频免费观看 | 日韩av偷拍 | 国产一区二区在线精品 | 亚洲二区精品 | 99re国产视频| 天堂在线v | 国产丝袜一区二区三区 | 性色av一区二区三区在线观看 | 免费日韩 精品中文字幕视频在线 | 国模吧一区 | 国产精品第一页在线观看 | 激情五月婷婷综合网 | 成人a视频在线观看 | 婷婷视频在线观看 | 久草视频视频在线播放 | 国产黄色精品视频 | se婷婷| 超碰99在线 | 天天曰 | 久久这里只有精品视频99 | 国产精品久久久久久久免费观看 | 亚洲精品免费在线观看 | 91亚洲在线观看 | 最近2019年日本中文免费字幕 | 久久午夜精品影院一区 | 人人爽人人爽人人爽 | 久草视频首页 | 色婷婷久久久 | 在线免费高清视频 | 18久久久久| free. 性欧美.com | 免费进去里的视频 | 在线精品观看国产 | 96精品视频 | 在线看一区二区 | 久久激情日本aⅴ | 久久婷婷开心 | 国产在线观看一 | 1区2区3区在线观看 三级动图 | a资源在线 | 成人黄色小说视频 | 欧美日韩国产一区二 | 色福利网站 | 欧美视频不卡 | 最近中文字幕 | 中文字幕日韩高清 | 中字幕视频在线永久在线观看免费 | 成人av动漫在线观看 | 色综合色综合久久综合频道88 | 99精品视频在线观看视频 | 中文字字幕在线 | 波多野结衣日韩 | 黄色软件在线看 | 91久久一区二区 | 国产xxxx | www视频在线免费观看 | 日韩国产精品久久 | 久久久国产精品一区二区三区 | 国产视频导航 | 久久久久女人精品毛片 | 国产不卡在线观看视频 | www91在线观看| 久草在线资源免费 | 奇米四色影狠狠爱7777 | 狠狠综合久久 | 欧美午夜寂寞影院 | 深夜免费福利 | 一级片视频在线 | 欧美综合国产 | 国产无遮挡又黄又爽馒头漫画 | 永久中文字幕 | 免费黄色在线网址 | 伊色综合久久之综合久久 | 国产成人在线观看 | 久章操 | 国产美女免费看 | 免费看色的网站 | 免费在线国产 | 国产精品成人一区二区 | 免费视频97 | 中文字幕国产视频 | 波多野结衣在线观看一区二区三区 | 九九视频网 | 成人一区二区三区中文字幕 | 999成人免费视频 | 国产精品爽爽久久久久久蜜臀 | 国产精品一区在线播放 | 综合在线亚洲 | 久久久这里有精品 | 五月综合激情网 | 久久午夜电影网 | 日日日爽爽爽 | 精品一区91| 美女网站视频色 | 美女网站视频久久 | 日韩精品免费一区二区三区 | 日韩精品一区二区三区免费观看视频 | 91亚洲欧美 | 五月综合色婷婷 | 日韩一区精品 | 免费观看一级一片 | 97超级碰| 狂野欧美激情性xxxx欧美 | 欧美色婷婷 | 亚洲国产久 | 久久久久久久av麻豆果冻 | 麻豆精品国产传媒 | 免费色视频网址 | 久久神马影院 | 成在线播放 | 免费视频一区二区 | 欧美久久久久久久久久久久 | 中文字幕 国产视频 | 三级在线播放视频 | 99精品国产免费久久久久久下载 | 国产91在线 | 美洲 | 国产第一页福利影院 | 亚洲综合视频网 | 在线精品观看 | 成人午夜电影在线播放 | 久久精品婷婷 | 日韩电影一区二区三区在线观看 | 热久久视久久精品18亚洲精品 | 亚洲三级黄色 | 久久久免费看视频 | 久久精品视频2 | 五月天综合网站 | 欧美不卡在线 | 日韩 精品 一区 国产 麻豆 | 日韩久久久久久久久久 | www..com毛片| 欧美日韩1区2区 | 国产成人一区二区三区在线观看 | 天天色天天射天天综合网 | 成人资源在线 | 日本视频网 | 99产精品成人啪免费网站 | 久久久久久国产精品美女 | 狠狠干2018 | 久久免费视频1 | av免费福利 | 精品特级毛片 | 成年人看片网站 | 精品美女视频 | 色香网| 久久综合中文字幕 | 天海冀一区二区三区 | 国产高清在线免费 | 人人爱在线视频 | 色综合久久综合中文综合网 | 中文字幕黄色网址 | 操操色 | 精品久久久久久久久久岛国gif | 天天躁日日躁狠狠躁 | 999超碰| 色五月色开心色婷婷色丁香 | 干天天| 欧美在线视频一区二区 | 五月天久久精品 | 91色一区二区三区 | 久久久国产精华液 | 成人精品一区二区三区电影免费 | 日韩精品一区二区三区不卡 | 亚洲黄在线观看 | 日韩午夜小视频 | 久久久在线免费观看 | 色婷婷狠狠操 | 久久国产精品免费看 | 婷婷精品国产一区二区三区日韩 | 国产高清在线免费 | 99久久99久久免费精品蜜臀 | 99九九热只有国产精品 | 天天色天| 精久久久久 | 日韩午夜电影 | 成年人免费看片网站 | 国产精品剧情 | 深爱五月激情五月 | 黄色字幕网 | 亚洲精品字幕在线 | 天天操天天爱天天干 | 黄网站色成年免费观看 | 天无日天天操天天干 | 欧洲激情在线 | 欧美小视频在线 | 国产午夜三级一区二区三 | 日本mv大片欧洲mv大片 | 97碰视频| 亚洲97在线 | av观看在线观看 | 一本一本久久aa综合精品 | 中文字幕一区二区三区在线观看 | 亚洲国产经典视频 | 美女又爽又黄 | 97电影院在线观看 | 91av在线免费看 | 国产中文字幕一区 | 五月天av在线 | av电影免费 | 国产精品欧美久久久久三级 | 亚洲不卡av一区二区三区 | 麻豆国产精品永久免费视频 | 天天综合视频在线观看 | 99色| 国产乱码精品一区二区三区介绍 | 久久黄色小说视频 | 久久精品com | av黄网站 | 免费久久久久久 | 成年人免费观看国产 | 91精品爽啪蜜夜国产在线播放 | 国产91精品在线播放 | 亚洲欧美日韩国产精品一区午夜 | 久久艹国产 | 色噜噜狠狠狠狠色综合 | 国语精品免费视频 | 色狠狠干| 天天色天天综合 | 超碰免费成人 | 亚洲综合色播 | 婷婷色在线 | 日韩国产在线观看 | 中文字幕在线精品 | 亚洲精品乱码久久久久久高潮 | 国产一区久久 | 国产精品免费久久久久影院仙踪林 | 色婷婷www | 黄网站色视频免费观看 | 性色av一区二区三区在线观看 | 中文字幕日韩精品有码视频 | 麻豆精品在线视频 | 97超碰国产精品女人人人爽 | 日韩午夜网站 | 激情综合五月婷婷 | 国产视频一区二区在线播放 | 色综合中文综合网 | 97av视频在线观看 | 日韩中文字 | 日韩欧美在线观看一区二区三区 | 国产亚洲亚洲 | 久久久久久久久久久黄色 | 中文字幕在线一二 | 国产精品理论在线观看 | 激情影院在线 | 91精品国产欧美一区二区成人 | 日韩在线视频一区二区三区 | 免费一级特黄录像 | 国产精品亚洲片在线播放 | 国产一区二区在线免费播放 | 久久99精品国产91久久来源 | 中文在线中文资源 | 国产伦精品一区二区三区无广告 | 中国美女一级看片 | 四虎国产精品永久在线国在线 | 亚洲精品456在线播放乱码 | 欧美日韩中文在线观看 | 在线激情电影 | 久久久久久久久久久免费av | 视频 天天草 | 超碰97人人射妻 | 91精品人成在线观看 | 99情趣网视频 | 久久精品国产免费 | 日本一区二区三区视频在线播放 | 久久久亚洲精品 | av一本久道久久波多野结衣 | 国产成人久久久77777 | 久久夜色精品国产欧美乱极品 | 欧美精品在线一区 | 在线免费精品视频 | 久久久99精品免费观看乱色 | 亚洲欧美综合精品久久成人 | 日本黄色免费在线观看 | 日韩一级黄色片 | 国内精品久久久久久久久久清纯 | 国产 日韩 在线 亚洲 字幕 中文 | 国产午夜麻豆影院在线观看 | 成年人免费电影在线观看 | 在线草 | www.com在线观看 | 久久久精品日本 | 操操日 | 日本大片免费观看在线 | 国产在线探花 | 综合色婷婷 | 97视频精品| 久久久久区| 香蕉影视app| 亚洲午夜精品电影 | 国内精品久久久久久久久久久久 | 亚洲一区二区三区毛片 | 久久久久久久久久久高潮一区二区 | 黄色网在线播放 | 久久国产区 | 99精品美女 | 人人干在线 | 国产激情电影综合在线看 | 免费福利在线观看 | 射久久久 | 国产免费激情久久 | 国产69精品久久久久99尤 | 免费毛片一区二区三区久久久 | 国产成人精品免高潮在线观看 | 奇米网网址| 毛片3| 国产欧美高清 | 日韩精品一区二区在线观看 | 96视频在线| 亚洲做受高潮欧美裸体 | 日韩电影在线观看一区 | 久久久网 | 欧美一区二视频在线免费观看 | 毛片随便看 | 九九交易行官网 | 欧美日韩精品久久久 | 国产精品99久久免费黑人 | 人人插人人草 | 欧美日韩一区二区在线观看 | 日韩精品资源 | 久久久久电影 | 日韩高清不卡一区二区三区 | 日韩高清精品一区二区 | 成年人网站免费在线观看 | 中文伊人 | 五月婷婷六月综合 | 中文字幕av专区 | 五月婷婷综合久久 | 欧美色久 | 久久永久免费 | 日本精品久久久久影院 | 久久有精品| 激情av五月婷婷 | 日韩二区三区在线观看 | 天天干天天色2020 | 91麻豆精品国产 | 91精品国自产在线观看欧美 | 亚洲男男gaygay无套同网址 | 天天干,天天射,天天操,天天摸 | 毛片美女网站 | 国产色视频网站 | 国产精品久久三 | 最新av中文字幕 | 狠色狠色综合久久 | 天天操天天射天天爽 | 国产精品女同一区二区三区久久夜 | 久草a视频| 精品国产一区二区三区男人吃奶 | 国产精品国产亚洲精品看不卡 | 国产亚洲成av人片在线观看桃 | 欧美韩国在线 | 婷婷亚洲五月色综合 | 亚洲国产婷婷 | 黄色片亚洲| av电影在线观看完整版一区二区 | 久亚洲| 欧美专区国产专区 | 国产麻豆精品传媒av国产下载 | 亚洲最新视频在线 | 午夜丁香视频在线观看 | 成人丝袜 | 天天干天天干天天干天天干天天干天天干 | 婷婷狠狠操 | 黄色在线观看免费 | 波多野结衣电影一区二区 | 蜜臀av夜夜澡人人爽人人桃色 | 91爱看片 | 国产精品视频区 | 超碰在线97国产 | 国产成人一区二区精品非洲 | 国产精品18久久久久久久久久久久 | 国产精品免费成人 | 久久激情影院 | 正在播放国产精品 | 国产原厂视频在线观看 | 看全黄大色黄大片 | 天堂av在线7 | 91豆麻精品91久久久久久 | 免费中午字幕无吗 | 超级碰碰视频 | 国产一区成人在线 | 久久免费播放视频 | 国产短视频在线播放 | 亚洲日日射 | 亚洲视频免费在线观看 | 91精品国自产拍天天拍 | 久久理论影院 | 国产一区播放 | 在线视频日韩欧美 | 日韩在线国产精品 | 色婷婷国产 | 99热超碰| 少妇精69xxtheporn | 91精品久久香蕉国产线看观看 | 伊在线视频 | 国产无套精品久久久久久 | 欧美电影黄色 | 日本精品午夜 | 天天操偷偷干 | 麻豆小视频在线观看 | 在线网址你懂得 | 免费看色网站 | 久久激情电影 | 深爱开心激情 | 韩国视频一区二区三区 | 四虎影视8848dvd | 免费观看91视频大全 | 一区二区激情 | 射九九| 欧美精彩视频 | 国产成视频在线观看 | a天堂一码二码专区 | 久久精品亚洲一区二区三区观看模式 | 亚洲免费永久精品国产 | 成人av在线看 | 五月婷婷影院 | www.夜夜干.com | 久久久久久福利 | 天天综合色 | 一区在线观看视频 | 日韩在线观看你懂得 | 一级做a爱片性色毛片www | 日韩在线免费小视频 | www.午夜视频| 久久a免费视频 | 九九九在线观看 | 国产精品免费在线视频 | 国产九九热视频 | 国产伦精品一区二区三区四区视频 | 亚洲视频2 | 成人av中文字幕 | 在线电影中文字幕 | 在线不卡中文字幕播放 | 黄色资源在线观看 | 又污又黄的网站 | 国产正在播放 | 在线播放 日韩专区 | 久久成人欧美 | 亚洲精品免费在线播放 | 成人小视频在线免费观看 | 免费情趣视频 | 国内久久久| 99在线免费观看视频 | 99久久夜色精品国产亚洲 | 日日夜夜狠狠干 | av福利在线导航 | 丁香六月激情 | 欧美一区日韩一区 | 92精品国产成人观看免费 | 久久国产精品偷 | 在线观看视频免费大全 | 在线欧美小视频 | 国产高清精 | 亚洲成人免费观看 | 日日夜夜精品网站 | 久久久久久久18 | 成 人 黄 色 视频免费播放 | 天天天干夜夜夜操 | 天天天干天天天操 | 国产精品免费成人 | 特黄特色特刺激视频免费播放 | sesese图片 | 色91av| 91高清免费观看 | 亚洲精品一区中文字幕乱码 | 日韩av在线看 | 国产精品久久久久久久久免费看 | 91香蕉视频黄 | 色婷婷综合五月 | 中文字幕在线专区 | 色婷婷国产精品一区在线观看 | 亚洲精品ww| 国产不卡免费 | 伊人久久精品久久亚洲一区 |