Flask紀要

flask學習過程javascript

1框架基礎
2redis高性能key-value數據庫
3視圖具備裝飾器的路由函數
4模板html文件 面向對象操做數據庫orm
5藍圖
6單元測試
7GitHub
8項目
html

學習的目的
1清晰的業務邏輯 2解決bug的能力-->先定位bug 3面向百度變成-->搜索問題的精準程度StackOverflow 4可以靈活應用知識點-->內置函數,列表推導式 java


web的本質world wide web 利用互聯網進行數據交流
客戶端 瀏覽器 ios Android發送請求(用戶輸入,a,img,Ajax,爬蟲) 渲染頁面
服務端 服務器腳本->WSGI->應用程序 接收請求 解析請求 路由分發 業務邏輯 返回響應 python

使用web框架 的目的 安全性 高併發 提升開發效率 穩定 拓展性強mysql

框架--協助開發的功能代碼-->按照規定的要求 在指定位置上寫業務代碼ios

flask 快速完成項目
djiango 商城項目完備 齊全
tornado 異步框架web

flask的wsgi werkzeug-->路由正則 jinja2-->模板的渲染redis

虛擬環境 爲了在同一臺電腦上使每一個項目都有屬於本身的python環境 否則會形成環境覆蓋算法

搭建虛擬環境
sudo pip install virtualenv
sudo pip install virtualenvwrappersql

建立建python2版本虛擬環境
mkvirtualenv 環境名 在使用python 在左側會顯示虛擬環境的名字 deacative退出虛擬環境
rmvirtualenv 環境名 刪除虛擬環境

建立python3版本虛擬環境
mkvirtualenv -p python3 環境名

進入已建立的虛擬環境 workon tab tab 選擇 回車

source activate原生啓動虛擬環境

pip install flask == 版本號 安裝響應版本的flask

查看已安裝的包 pip list

which python 查看python或虛擬環境的所在路徑


from flask import Flask
static_path 訪問靜態文件的路徑 已棄用
static_url_path 訪問靜態文件的路徑 指定爲none 系統默認爲/static
templates_folder 模板文件的文件夾 默認是 templates
static_folder 靜態文件的問價夾
host 萬能端口=" 0.0.0.0" 只要是本臺電腦上的網卡均可以訪問改ip


flask程序
from flask import Flask # 遵循PEP8的編碼格式 使用哪一個模塊就import指定名稱

app = Flask(__name__#是當前模塊的名字,指定模塊的絕對路徑
static_url_path="/static"# 靜態文件的路徑
static_folder = "static",#指定靜態文件夾的名字
template_folder = "templates"#指定模板文件夾的名字)


#加載配置
#1從類對象中加載
class Config(object):
DEBUG = True--->類屬性必須是大寫內置函數規定的小寫不知足條件
app.config.from_object(Config)

#2從配置文件中加載 config 相似於字典類型
在靜態文件夾中建立一個配置文件 裏面寫上DEBUG = True
app.config.from_pyfile("配置文件的路徑")

#3環境變量
新建環境變量 裏面新建配置文件
app.config.from_envvar("環境變量的名字")

4app.debug = true

5app.config["DEBUG"] = True
獲取配置
app.config.get("DEBUG")

路由的本質是 url綁定 @app.route()裝飾器用於把一個函數綁定到一個url上
視圖函數的返回的響應能夠是包含html的簡單字符串 也能夠是複雜的表單
同一路由指向多個不一樣的函數,在匹配過程當中,至上而下依次匹配下面的函數並不執行


# 視圖函數和路由
@app.route("/",methods = ["POST","GET"]) # 路由
def index(): # 視圖函數
return "hello python"

瀏覽器只能獲取get請求方式 post須要使用postman插件

# 動態路由 (帶參數的路由)
@app.route("/user1/<int:user_id>"])# int:轉換器

#自定義轉換器 匹配6位數字
1起轉換器的名字regex
2定義一個類繼承自父類
class RegexConverter(BaseConverter):
regex = r"[0-9]{6}"
3把名字做爲鍵 類對象做爲值添加到系統默認的轉換器字典中
app.url_map.converters ["regex"]= RegexConverter

向上查找能夠找到父類被調用的地方

request接收請求報文的數據,進行處理
request.data.decode() 接收到的數據是二進制 須要解碼
request.form.get("鍵")-->獲取值
request.args.get("鍵")--.獲取值
pic_obj = request.files.get("文件名")-->pic_obj.save("新的名字")

def index(user_id):

字典數據名 = {}
# json_data= json.dumps(字典名)--->字典數據轉換位json數據--不經常使用
# 字典數據 = json.load(json數據)-->json數據轉換爲字典數據--不經常使用

return jsonify(字典數據名)--->將字典數據轉換爲json數據內容格式也會改變 須要導入jsonify庫--最經常使用
return redirect(url_for("函數名"))-->經過函數名找到對應函數return"的網址" 須要導入url_for模塊
return user_id
return '狀態碼爲 666', 666

if __name__ == '__main__':
app.run()
app.run(host="主機ip" port=端口 debug=True-->1重啓修改後的代碼2拋出異常的位置利於開發)


子類重寫父類的init方法會實例化子類,我本身稱之爲實例類對象

獲取類中的全部實例對象 使用dir()內置函數

 

狀態保持 session(服務端) 與 cookie(客戶端)

cookie 是由服務端給客戶端的,存儲在客戶端(一般是加密的),存儲cookie的key:value,是純文本信息 不能存儲敏感信息
不一樣域名的cookie不能共用
cookie同源策略 同域名,同路徑,如天貓和淘寶cookie共享
瀏覽器保存的cookie最大隻能由4kb 服務器保存session不限量

cookie
設置cookie response.set_cookie("name", "老王")
cookie 設置過時時間 在設置cookie時設置過時時間 set_cookie(max_age=秒數)
獲取cookie request.cookie.get("cookie的名字")
刪除cookie在退出時刪除cookie delete.cookie("name")


session
session_id 服務器加密以後以cookie的方式發送給瀏覽器 ,瀏覽器沒法解析,只有服務器能認識session_id
設置session session["name"] = "laowang"設置session須要加密 使用app.secret_key = "aaa"#session祕鑰
獲取session session.get("name",None)若是沒有獲取到就返回none
刪除session session.pop("name",None) 刪除name對應的鍵值對,沒有name就返回None


請求鉤子 對全部的視圖函數進行管理
@app.before_first_request # 在第一次請求以前會執行 應用場景:連接數據庫 由於只須要連接一次
def before_first_request():

@app.before_request # 在每一次請求以前都會執行 能夠作權限驗證工做
def before_request(): v'c'c'c'c'c'c'c'c'c'c'c'c'c'c'c'c'c'c

@app.after_request # 在每一次請求以後都會執行 能夠作掃尾工做 設置響應報文的內容必須接收一個參數做爲響應
def after_request(respose):
return response

@app.terar_down_request # 在每一次請求以後都會執行 會捕獲到響應中的異常 能夠作捕獲異常的操做
def terar_down_request(e):

只要訪問視圖函數都須要走這幾步驟 只有第一次連接會觸發before_first_request()二次以後就不會

 

主動拋出異常 abort()
http異常的主動拋出 abort()方法
若是是非法id訪問 能夠拋出404 -->參數是符合http協議的狀態碼 404 500 302等
能夠主動攔截非法ip


捕獲異常
@app.errorhandler(符合http協議的狀態碼-也能夠捕獲異常類名)<--若是視圖函數出現()裏的異常就執行下面的代碼
def exception_handle(e):全局捕獲
return ""


上下文 在代碼執行到某一時刻時,根據以前的代碼所作的操做以及下文即將要執行的邏輯,能夠決定在當前時刻下可使用到的變量,或者能夠完成的事情

請求上下文 request context -包含->request(封裝了http請求的內容) 和 session(記錄請求會話中的用戶信息) 保存了c s交互的數據

應用上下文 application context -->current_app 和 g變量 flask程序運行過程當中保存了一些配置信息

current_app -->獲取模塊名,獲取debug的值 和app的功能是相似的
g變量 全局臨時變量 g對象-->能夠動態添加屬性 把g放在請求鉤子中全部視圖函數都能使用
---!!只能設置在當前文件中


flask script -->sys.argv
app.run 不能經過命令行指定端口,主機ip 以及其它參數 經過flask-script就能夠
使用步驟
1 pip install flask-script
2 from flask-script import Manager
3 manager = Manager(app)
4 manager.run()
5 在Edit中添加runserver 參數

 

 

 

模板----->templates
jinja2 是由python開發的一個模板引擎
把變量名給模板替換模板裏的內容

建立一個文件夾templates
寫一個視圖函數進行渲染
指定templates文件夾爲templates文件夾
配置模板語言爲jinjia2

在視圖函數中渲染到模板
return render_template("demo1.html",)多個變量之間使用,隔開


過濾器的本質就是一個函數 |
能夠鏈式操做如 {{ my_str | reverse | lower}}
safe 讓帶標籤的字符串按找格式被瀏覽器渲染出來

自定義過濾器
1.定義一個過濾器的名字
lireverse
2.定義一個過濾器函數
def do_lireverse(li):
temp = list(li)
retern list(reversed(temp))
3.將名字做爲鍵,函數的引用做爲值添加到系統默認的過濾器中
app.add_template_filter(do_lireverse:"lireverse")

f 函數的引用


控制代碼塊{% for my_dict in my_list%}
{% if loop.index == 1 %}
<li style="background-color: orange">{{ my_dict.value }}</li>
{%endfor%}


模板的繼承
父類模板中須要挖坑{% block contentBlock%}{%endblock%} 用來放子類模板本身的內容

子類模板中
{%extens "父類模板名"%}
{% block contentBlock%}
{{super()繼承父類他本身的內容}}
子類模板本身的內容
{%endblock%}

 

相同格式的頁面抽取父類模板

1 將兩個頁面進行對比
2 去異存同 將不一樣之處刪除 再刪除的位置挖坑 {%block 名字Block%}{% endblock %}
3 起名字可利用標籤名或者class的屬性進行起名 以便於在繼承時進行一一對應
4 抽取完成的頁面就是父類模板
5 在子類頁面中繼承父類模板 {% extends "base_news.html" %}
6 取出父類模板中的坑在裏面填上子類模板本身的內容
{% block scriptBlock %}<script type="text/javascript" src="../static/news/js/detail.js"></script>
{% endblock %} 相同的內容繼承自父類模板


導入一個模塊中的多個類 在被導的模塊中使用__all__ = ["類名","類名"]指定要導入的類 使用import * 導入時就是列表中的中

也能夠添加一個字典aa = {} 導入時導字典名


模板中特有的變量和函數
config 能夠經過模板直接獲取
request
session
url_for 經過函數的名字獲取函數對應的url


CSRF 跨站請求僞造

訪問a進行轉帳 a未退出cookie存在 去訪問b b中訪問a的轉帳(cookie存在) a沒法判別訪問本身的對象

防止csrf攻擊
肯定攻擊實在哪一個代碼塊產生
區分請求是究竟是誰(在轉帳界面設置cookie)
在webA中的轉帳界面生成隨機數存入cookie中
將隨機的cookie傳到界面的form表單的隱藏標籤中
在轉帳界面對cookie和界面中的cookie進行比較 相等才能進行轉帳
因爲webB沒法獲取到webA的隱藏標籤,獲取它的表單中的cookie值時顯示爲None 值不等 沒法執行轉帳操做

實際開發中引入CSRFProected 便可


flask 數據庫 ORM 將表關係對應到類中 sql語句對應到類的方法
Flask-SQLAlchemy
建立數據庫 create database 數據庫名(test) chartset utf8

pycharm 建立數據庫
點擊右側database -->點擊右側.QL-->建立數據庫 create database 數據庫名 cahrtset utf8-->刷新-->在下面More中選擇添加-->雙擊使用

連接數據庫 數據庫 用戶名: 密碼 地址 端口 數據庫名
app.config["SQLALCHEMY_DATABASE_URI"] = "mysql://root:mysql@127.0.0.1:3306/test"

跟蹤動態跟蹤修改設置,未設置只會提示警告
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = True

查詢時顯示原生SQL語句
app.config['SQLALCHEMY_ECHO'] = True

實例化數據庫sqlalchemy工具對象
db = SQLALchemy(app)


建立模型類
class BookInfo(db.Model): -->繼承自實例對象的Model
__tablename__ = "book-info" # 若是不指明就是類名的小寫
# 表結構
name = db.Column(db.String(64), unique=True, nullable=Flask)
pub_date = db.Column(db.String(128), nullable=True)
is_delete = db.Column(db.Boolean, default=False)
id = db.Column(db.Integer, primary_key=True)

# 返回對象的name屬性 不寫的話返回的是對象的地址引用
def __repr__(self):
return self.name


表操做

建立表
db.create_all()

刪除表
db.drop_all()

新增一條數據
建立對象,設置屬性
book1 = BookInfo()
book1.name = "西遊記"
book1.pub_date = "2001-1-1"

添加到數據庫中
db.session.add(book1)-----add_all()能夠添加多個對象

真正提交數據
db.session.commit()

刪除數據
user = PeopleInfo.query.first() -->查詢出一個信息
db.session.delete(user)-->刪除這條數據
db.session.commit()-->提交刪除的操做到數據庫
PeopleInfo.query.all()-->查詢所有信息確認是否刪除成功


更新數據
user = PeopleInfo.query.first() -->查詢出一個信息
user.name = "新的值"-->將對象的屬性設爲新的值
db.session.commit()-->將更新的操做提交到數據庫
PeopleInfo.query.first()-->再次查詢這條數據確認修改爲功

 

----------------------------------------------------->>>>>

查詢 可使用ipython3進行查詢,須要導入
from 模塊名 import *
類名.query.filter().all()
filter()返回的是一個查詢集
all()以列表的方式返回查詢的全部結果
類名.query.filter(條件).all()-->模糊查詢
類名.query.filter_by(條件).first()-->精確查詢


多表聯合查詢
查詢出書下面的全部人物
方式-
book = BookInfo.query.filter(BookInfo.name =="三國演義").first()-->查詢出book對象
PeopleInfo.query.filter(PeopleInfo.book_id == book.id).all()-->以列表顯示的結果

方式二
book = BookInfo.query.filter(BookInfo.name =="三國演義").first()-->查詢出book對象
people = db.relationship("PeopleInfo")-->須要在BookInfo表中添加和PeopleInfo的relationshhip

book.people


backref="book"動態的給PeopleInfo添加了一個book屬性-->反向引用
等同於book = db.relationship("BookInfo")-->須要在PeopleInfo表中添加和BookInfo的relationshhip

lazy ="dynamic"--->book.people是一個查詢集佔用小內存使用,能夠近一步操做
------------------------------------------------------>>>>>>>>>

使用閃現flash()須要設置密鑰 它依賴與session

 

狀態保持
請求鉤子
csrf
圖書管理案例

 

數據庫遷移 -->能夠記錄版本信息 能夠反悔操做 必須先備份數據 不刪除表就能夠修改表結構防止數據丟失
跟蹤設置數據結構 原始數據能夠恢復 監聽對數據庫的操做 必須先備份數據! ! ! ! !

from flask_migrate import Migrate,MigrateCommand
from flask_migrate import Manager,Shell

在flask 中對數據庫進行遷移配置
manager = Manager(app)

migrate= Manager(app,db)-->關聯
manager.add_command("db", MigrateCommand)


遷移的命令
python xx.py db init 建立一個遷移文件夾(遷移文件的倉庫)

python xx.py db migrate -m "註明(修改的內容)"-->生成一個版本文件 將模型添加到遷移文件中 遷移腳本

python xx.py db upgrade 更新數據庫中的表 執行遷移 觀察表結構

返回舊版本
python xx.py db history 查看歷史版本(遷移記錄)

python xx.py db downgrade 版本號 -->返回指定舊版本

 

多對多案例分析 如:學生 學科 第三表
1 建立數據庫
2 數據模型 繼承(db.Model)
表名
字段= 約束
關係
創建鏈接
數據模型 繼承(db.Model)
表名
字段= 約束
關係
創建鏈接
3 第三表創建關係 直接建立一張表不須要使用模型類
tb_student_course = db.Table(
"表名",
db.Colum("字段名",約束))

4 添加數據

 


藍圖
爲何使用藍圖--->對程序進行模塊化管理--進行協同開發--能夠解決循環導入問題--下降耦合度
模塊化管理-->根據必定的條件對函數進行分類(按照功能,按照開發人員等)


步驟
1.在藍圖對象模塊中導入Blueprint
2.實例化藍圖對象 = Blueprint("名字",__name__,url_prefix = "/路由的開頭 區分模塊之間的路由,防止衝突")
3.定以路由
4.導入藍圖對象
5.將藍圖註冊到app中(藍圖實例對象)

一個藍圖模塊能夠註冊屢次

 

單元測試--->嚴謹性特別高的程序使用單元測試

向功能單一的模塊進行測試
斷言assert 判斷一個函數或方法的一個功能是否符合預期結果
在單元測試中使用
在本身寫的工具類供別人使用使用須要斷言

例如:
def func(a,b):
assert isinstance(a,int),"參數爲int類型"
return a/b
func(參數,參數)


單元測試測試接口------------------------------------數據庫測試-------晚間實現

1使用代碼模擬瀏覽器發送一個post請求
2判斷errorcode是否存在字典中
3判斷errorcode是否爲-1

測試代碼的步驟
import unittest
import app
import json

class LoginTest(unittest.TestCase):
def setUp(self): # 至關於__init__ 作初始化工做
app.testing = True--->被測試代碼有異常會直接拋出
定義模擬客戶端=app.test_clicent()

def test_empty_username_password(self):
app.test_clicent().post("url",data={}) #模擬瀏覽器發送一個post請求
json_data = responde.data
dict_data = josn_data.loads()
print(dict_data)
self.assertIn("鍵",容器,"返回的消息")
self.assertEqual(數據的[鍵],狀態碼,"返回的消息")

def tearDown(): # 相似與__del__()-->析構方法
數據庫的斷開操做

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

 


nosql--->以鍵值對存儲數據
sql------>適用於關係複雜的數據查詢場景

redis-->數據存貯在內存中 速度極快 也支持磁盤存儲 重啓再次加載使用
支持 list set zset hash 數據據結構存儲 支持主從配置 master--slave

應用場景
緩存
社交類應用
session共享 購物車

安裝redis
wget url 下載
解壓
移動到指定目錄
今日redis目錄
sudo make 進行編譯
sudo make test 測試
sudo make install 安裝命令
將配置文件.conf加載到etc目錄下

客戶端啓動 redis-cli
select 0-15 選擇使用的數據庫
服務端啓動 redis-serve

redis 端口 6379

設置守護進程 demaonize yes

默認16個數據庫 0-15


redis 哈希 基本的數據類型
哈希槽 利用哈希算法計算出來的存儲數據的空間

主從 讀寫分離 數據備份

相關文章
相關標籤/搜索