柚子快報(bào)激活碼778899分享:鴻蒙 面試 學(xué)習(xí) 上傳文件
柚子快報(bào)激活碼778899分享:鴻蒙 面試 學(xué)習(xí) 上傳文件
網(wǎng)上學(xué)習(xí)資料一大堆,但如果學(xué)到的知識(shí)不成體系,遇到問題時(shí)只是淺嘗輒止,不再深入研究,那么很難做到真正的技術(shù)提升。
需要這份系統(tǒng)化的資料的朋友,可以戳這里獲取
一個(gè)人可以走的很快,但一群人才能走的更遠(yuǎn)!不論你是正從事IT行業(yè)的老鳥或是對(duì)IT行業(yè)感興趣的新人,都?xì)g迎加入我們的的圈子(技術(shù)交流、學(xué)習(xí)資源、職場吐槽、大廠內(nèi)推、面試輔導(dǎo)),讓我們一起學(xué)習(xí)成長!
基于方法的類視圖,是根據(jù)請(qǐng)求的 method 來執(zhí)行不同的方法的。 如果用戶是發(fā)送的 get 請(qǐng)求,那么將會(huì)執(zhí)行這個(gè)類的 get 方法。 如果用戶發(fā)送的是 post 請(qǐng)求,那么將會(huì)執(zhí)行這個(gè)類的 post 方法。其他 的method類似,比如 delete 、 put 這種方式,可以讓代碼更加簡潔。所有和 get 請(qǐng)求相關(guān)的代碼都放 在 get 方法中,所有和 post 請(qǐng)求相關(guān)的代碼都放在 post 方法中。就不 需要跟之前的函數(shù)一樣,通過 request.method == ‘GET’
class LoginView(views.MethodView): def get(self,error=None): return render_template(‘login.html’,error=error) def post(self): #模擬實(shí)現(xiàn) #拿到前端頁面?zhèn)鬟^來的 賬號(hào) 和密碼 去數(shù)據(jù)庫做查詢操作 查詢到 (跳轉(zhuǎn)主頁面) ,反之跳轉(zhuǎn)到login.html頁面并給出錯(cuò)誤提示信息 uname = request.form[‘uname’] pwd = request.form[‘pwd’] if uname==“sxt” and pwd ==“123”: return render_template(‘index.html’) else: return self.get(error=“用戶名或者密碼錯(cuò)誤”)
注冊類視圖
app.add_url_rule(‘/login/’,view_func=LoginVi ew.as_view(‘my_login’))
HTML
賬號(hào):密碼: {# {{ error }}#} {# 優(yōu)化寫法 :判斷 #} {% if error %} {{ error }} {% endif %}
裝飾器
簡言之,python裝飾器就是用于拓展原來函數(shù)功能的一種函數(shù),這 個(gè)函數(shù)的特殊之處在于它的返回值也是一個(gè)函數(shù), 使用python裝飾器的好處就是在不用更改原函數(shù)的代碼前提下給函 數(shù)增加新的功能。
在視圖函數(shù)中使用自定義裝飾器,那么自己定義的裝飾器必須放 在 app.route 下面。 否則這個(gè)裝飾器就起不到任何作用。
案例1
需求:查看設(shè)置個(gè)人信息時(shí),只有檢測到用戶已經(jīng)登錄了才能查 看,若沒有登錄,則無法查看并給出提示信息
定義裝飾器
def login_required(func): @wraps(func) def wrapper(*arg,**kwargs): uname = request.args.get(‘uname’) pwd = request.args.get(‘pwd’) if uname == ‘zs’ and pwd == ‘123’: logging.info(f’{uname}:登錄成功’) return func(*arg,**kwargs) else: logging.info(f’{uname}:嘗試登錄,但沒成功’) return ‘請(qǐng)先登錄’ return wrapper
使用裝飾器
@app.route(‘/settings/’) @login_requierd def settings(): return ‘這是設(shè)置界面’
在類視圖中使用裝飾器,需要重寫類視圖的一個(gè)類屬性 decorators , 這個(gè)類屬性是一個(gè)列表或者元組都可以,里面裝的就是所有的裝飾 器。
案例2
需求: 查看設(shè)置個(gè)人信息時(shí),只有檢測到用戶已經(jīng)登錄了才能查看, 若沒有登錄,則無法查看并給出提示信息
使用裝飾器
class ProfileView(views.View): decorators = [login_requierd] def dispatch_request(self): return ‘這是個(gè)人中心界面’
app.add_url_rule(‘/profile/’,view_func=ProfileView.as_view(‘profile’))
藍(lán)圖介紹
在Flask中,使用藍(lán)圖Blueprint來分模塊組織管理。 藍(lán)圖實(shí)際可以理解為是存儲(chǔ)一組視圖方法的容器對(duì)象,其具有如下 特點(diǎn):
一個(gè)應(yīng)用可以具有多個(gè)Blueprint可以將一個(gè)Blueprint注冊到任何一個(gè)未使用的URL下比如 “/user” 、 “/goods”Blueprint可以單獨(dú)具有自己的模板、靜態(tài)文件或者其它的通用操作方法,它并不是必須要實(shí)現(xiàn)應(yīng) 用的視圖和函數(shù)的在一個(gè)應(yīng)用初始化時(shí),就應(yīng)該要注冊需要使用的Blueprint
注意
Blueprint并不是一個(gè)完整的應(yīng)用,它不能獨(dú)立于應(yīng)用運(yùn)行,而 必須要注冊到某一個(gè)應(yīng)用中
使用方式
使用藍(lán)圖可以分為三個(gè)步驟
1 創(chuàng)建一個(gè)藍(lán)圖對(duì)象
user_bp=Blueprint(‘user’,name)
在這個(gè)藍(lán)圖對(duì)象上,
@user_bp.route(‘/’) def user_profile(): return ‘user_profile’
在應(yīng)用對(duì)象上注冊這個(gè)藍(lán)圖對(duì)象
app.register_blueprint(user_bp)
指定藍(lán)圖的url前綴
在應(yīng)用中注冊藍(lán)圖時(shí)使用 url_prefix 參數(shù)指定
app.register_blueprint(user_bp,url_prefix=‘/user’) app.register_blueprint(goods_bp,url_prefix=‘/goods’)
藍(lán)圖的目錄結(jié)構(gòu)
為了讓項(xiàng)目代碼更加清晰,可以通過將代碼分在不同的文件里進(jìn)行 管理
根據(jù)功能模塊
對(duì)于一個(gè)打算包含多個(gè)文件的藍(lán)圖,通常將創(chuàng)建藍(lán)圖對(duì)象放到 Python包的 __init__.py 文件中
--------- project # 工程目錄 |------ main.py # 啟動(dòng)文件 |------ user #用戶藍(lán)圖 | |— init.py # 此處創(chuàng)建藍(lán)圖對(duì)象 | |— view.py | |— … |------ goods # 商品藍(lán)圖 | |— init.py | |— … |…
根據(jù)技術(shù)模塊
--------- project # 工程目錄 |------ main.py # 啟動(dòng)文件 |------ view #用戶藍(lán)圖 | |— user.py # 此處創(chuàng)建藍(lán)圖對(duì)象 | |— item.py | |— view.py | |— … |…
藍(lán)圖中模版文件
尋找規(guī)則
如果項(xiàng)目中的templates文件夾中有相應(yīng)的模版文件,就直接使 用了。如果項(xiàng)目中的templates文件夾中沒有相應(yīng)的模版文件,那么就 到在定義藍(lán)圖的時(shí)候指定的路徑中尋找。
并且藍(lán)圖中指定的路徑可以為相對(duì)路徑,相對(duì)的是當(dāng)前這個(gè)藍(lán)圖文件所在的目錄
因?yàn)檫@個(gè)藍(lán)圖文件是在user/view.py,那么就會(huì)到blueprints這個(gè) 文件夾下的user_page文件夾中尋找模版文件。
小總結(jié): 常規(guī):藍(lán)圖文件在查找模版文件時(shí),會(huì)以templates為根目錄進(jìn)行 查找
注意
1 個(gè)性化coder喜歡在【創(chuàng)建藍(lán)圖對(duì)象的時(shí)候】 指定 模版文 件的查找路徑,如下 news_bp =Blueprint(‘news’,__name__,url_prefix=‘/news’,template_folder=‘news_page’)2 只有確定templates目錄下沒有對(duì)應(yīng)的 html文件名的時(shí)候, 才會(huì)去藍(lán)圖文件指定的目錄下查找,指定才會(huì)生效3 若templates目錄下,有一個(gè)與藍(lán)圖文件指定的目錄下同名 的一個(gè) html文件時(shí),優(yōu)先走templates目錄下的東西
藍(lán)圖中靜態(tài)文件
藍(lán)圖內(nèi)部靜態(tài)文件 藍(lán)圖對(duì)象創(chuàng)建時(shí)不會(huì)默認(rèn)注冊靜態(tài)目錄的路由。需要我們在創(chuàng)建時(shí) 指定 static_folder 參數(shù)。 下面的示例將藍(lán)圖所在目錄下的 static_admin 目錄設(shè)置為靜態(tài)目錄:
user=Blueprint(“user”,name,static_folder=‘user_static’) app.register_blueprint(admin,url_prefix=‘/user’)
也可通過 static_url_path 改變訪問路徑
user =Blueprint(‘user’,name,template_folder=‘user_page’,static_folder=‘user_static’,static_u rl_path=‘/static’) app.register_blueprint(user,url_prefix=‘/user’)
總結(jié) 【掌握】查找方式1:查找靜態(tài)文件時(shí),正常情況下,會(huì)以 static為根目錄進(jìn)行查找 【了解】查找方式2:查找靜態(tài)文件時(shí),非正常情況下,需要用 url_for(‘藍(lán)圖的名字.static’),然后會(huì)去藍(lán)圖對(duì)象在創(chuàng)建時(shí)指定的 靜態(tài)文件夾目錄下 去查找靜態(tài)文件
藍(lán)圖url_for函數(shù)
如果使用藍(lán)圖,那么以后想要反轉(zhuǎn)藍(lán)圖中的視圖函數(shù)為url,就應(yīng)該 在使用url_for的時(shí)候指定這個(gè)藍(lán)圖名字。 app類中、模版中、同一個(gè)藍(lán)圖類中都是如此。否則就找不到這個(gè) endpoint
html文件中
新聞列表 OK寫法 {# 新聞列表 no Ok寫法#}
python文件中
from flask import Blueprint,render_template,url_for user_bp=Blueprint(‘news’,name,url_prefix=‘/user’,template_folder=‘user_page’,static_folder=‘user_static’) @user_bp.route(‘/list/’) def user_list(): #如下寫法:才找得到 url_for(‘藍(lán)圖名稱.方法名’) print(url_for(‘user.user_list’)) #/user/list/ print(url_for(‘user.user_detail’)) #/user/detail/ return render_template(‘user_list.html’) @user_bp.route(‘/detail/’) def user_detail(): return ‘用戶詳情頁面’
子域名實(shí)現(xiàn)
藍(lán)圖實(shí)現(xiàn)子域名:
使用藍(lán)圖技術(shù)。 在創(chuàng)建藍(lán)圖對(duì)象的時(shí)候,需要傳遞一個(gè) subdomain 參數(shù),來指定這 個(gè)子域名的前綴。
cms_bp=Blueprint(‘cms’,name,subdomain=‘cms’)
需要在主app文件中,需要配置app.config的SERVER_NAME參 數(shù)。例如:
app.config[‘SERVER_NAME’]=‘baidu.com:5000’
在windows: C:\Windows\System32\drivers\etc 下,找到hosts文件,然后添 加域名與本機(jī)的映射。Linux: /etc/hosts 域名和子域名都需要做映射
注意 ip地址不能有子域名 localhost也不能有子域名
Flask高級(jí)
Flask設(shè)置Cookie
設(shè)置
設(shè)置cookie是在Response的對(duì)象上設(shè)置。 flask.Response 對(duì)象有一個(gè) set_cookie 方法,可以通過這個(gè)方法來設(shè)置 cookie 信息。
key,value形式設(shè)置信息
from flask import Flask, make_response app = Flask(name) @app.route(‘/cookie’) def set_cookie(): resp = make_response(‘set cookie ok’) resp.set_cookie(‘uname’, ‘itbaizhan’) return resp
查看Cookie
在Chrome瀏覽器中查看cookie的方式:
方式1:借助于 開發(fā)調(diào)式工具進(jìn)行查看
方式2:在Chrome的設(shè)置界面->高級(jí)設(shè)置->內(nèi)容設(shè)置->所有 cookie->找到當(dāng)前域名下的cookie。
from flask import request @app.route(‘/get_cookie’) def get_cookie(): resp = request.cookies.get(‘uname’) return resp
刪除cookie
方式1:通過 Response對(duì)象.delete_cookie ,指定cookie的key,就可以刪 除cookie了。
from flask import request @app.route(‘/delete_cookie’) def delete_cookie(): response = make_response(‘helloworld’) response.delete_cookie(‘uname’) return response
方式2:在客戶端瀏覽器人為的刪除(清除瀏覽器瀏覽歷史記錄 后,很多網(wǎng)站之前免密登錄的都不好使了)
Cookie的有效期
默認(rèn)的過期時(shí)間:如果沒有顯示的指定過期時(shí)間,那么這個(gè)cookie 將會(huì)在瀏覽器關(guān)閉后過期。 max_age:以秒為單位,距離現(xiàn)在多少秒后cookie會(huì)過期。
expires:為datetime類型。這個(gè)時(shí)間需要設(shè)置為格林尼治時(shí)間, 相對(duì)北京時(shí)間來說 會(huì)自動(dòng)+8小時(shí) 如果max_age和expires都設(shè)置了,那么這時(shí)候以max_age為標(biāo) 準(zhǔn)。
注意
max_age在IE8以下的瀏覽器是不支持的。 expires雖然在新版的HTTP協(xié)議中是被廢棄了,但是到目前為 止,所有的瀏覽器都還是能夠支持,所以如果想要兼容IE8以下 的瀏覽器,那么應(yīng)該使用expires,否則可以使用max_age。
from flask import Flask,Response app = Flask(name) @app.route(‘/’) def index(): return ‘Hello!!’ @app.route(‘/create_cookie/defualt/’) def create_cookie1(): resp = Response(‘通過默認(rèn)值,設(shè)置cookie有效期’)
如果沒有設(shè)置有效期,默認(rèn)會(huì)在瀏覽器關(guān)閉的時(shí)候,讓cookie過期
resp.set_cookie(‘uname’,‘zs’) return resp @app.route(‘/create_cookie/max_age/’) def create_cookie2(): resp = Response(‘通過max_age,設(shè)置cookie有效期’)
max_age以秒為單位設(shè)置cookie的有效期
age = 60602
resp.set_cookie(‘uname’,‘zs’,max_age=age) return resp from datetime import datetime @app.route(‘/create_cookie/expires/’) def create_cookie3(): resp = Response(‘通過expires,設(shè)置cookie有效期’)
expires 以指定時(shí)間為cookie的有效期
16+8 == 24
tmp_time = datetime(2021, 11,11,hour=18,minute=0,second=0)
resp.set_cookie(‘uname’,‘python’,expires=tmp_time) return resp from datetime import timedelta @app.route(‘/create_cookie/expires2/’) def create_cookie4(): resp = Response(‘通過expires,設(shè)置cookie有效期’)
expires 以指定時(shí)間為cookie的有效期
tmp_time = datetime.now() +timedelta(days=2) resp.set_cookie(‘uname’,‘python_sql’,expires=tmp_time) return resp @app.route(‘/create_cookie/exp_max/’) def create_cookie5(): resp = Response(‘通過expires與max_age,設(shè)置cookie有效期’)
expires 與max_age同時(shí)設(shè)置了,會(huì)以max_age為準(zhǔn)
tmp_time = datetime.now() +timedelta(days=2)
resp.set_cookie(‘uname’,‘python_sql’,expires=tmp_time,max_age = 60602) return resp if name == ‘main’: app.run(debug=True)
Flask中使用Session
需要先設(shè)置SECRET_KEY
class DefaultConfig(object): SECRET_KEY = ‘fih9fh9eh9gh2’ app.config.from_object(DefaultConfig)
或者直接設(shè)置
app.secret_key=‘xihwidfw9efw’
設(shè)置、修改
from flask import session @app.route(‘/set_session/’) def set_session(): session[‘username’] = ‘zs’ return ‘set session ok’
讀取
@app.route(‘/get_session/’) def get_session(): username = session.get(‘username’) return ‘get session username {}’.format(username)
刪除
@app.route(‘/del_session/’) def delete_session(): #刪除指定的key的session
session.pop(‘uname’)
#刪除session中的所有的key 【刪除所有】 session.clear() return ‘刪除成功’
Flask設(shè)置Session的有效期
如果沒有設(shè)置session的有效期。那么默認(rèn)就是瀏覽器關(guān)閉后過期。 如果設(shè)置session.permanent=True,那么就會(huì)默認(rèn)在31天后過 期。 如果不想在31天后過期,按如下步驟操作。
1 session.permanent=True
2 可以設(shè)置 app.config[‘PERMANENT_SESSION_LIFETIME’] = timedelta(hour=2) 在兩個(gè)小時(shí)后過期。
from flask import Flask,session from datetime import timedelta app = Flask(name) app.secret_key = ‘sdfdfdsfsss’ app.config[‘PERMANENT_SESSION_LIFETIME’] = timedelta(days=2) @app.route(‘/’) def index(): return ‘Hello??!’ @app.route(‘/set_session/’) def set_session():
設(shè)置session的持久化,默認(rèn)是增加了31天
session.permanent = True session[‘uname’] = ‘10001’ return ‘設(shè)置一個(gè)Session的信息’ @app.route(‘/get_session/’) def get_session():
如果服務(wù)器關(guān)閉掉了,session的有效期,依然是之前系統(tǒng)保存日期
如果secret_key設(shè)置是一個(gè)固定的值,那么服務(wù)器重啟不會(huì)影響session的有效器
如果secret_key設(shè)置不是一個(gè)固定的值,那么服務(wù)器之前設(shè)置的session將全部過期
return session.get(‘uname’) if name == ‘main’: app.run(debug=True)
Session實(shí)戰(zhàn)
login.html
Document
賬號(hào):密碼: {% if msg %} {{ msg }} {% endif %}
from flask import Flask, session, request,redirect,url_for,views,render_template app = Flask(name)
定義一個(gè)基于方法調(diào)度的 類視圖
class LoginView(views.MethodView): def __jump(self,msg=None): return render_template(‘login.html’,msg = msg) def get(self): msg = request.args.get(‘msg’) return self.__jump(msg) def post(self): uname = request.form.get(‘uname’) pwd = request.form.get(‘pwd’) if uname == “zs” and pwd == “123”: session[‘uname’] = uname return render_template(‘index.html’) else: return self.__jump(msg=“用戶名或者密碼錯(cuò)誤”) @app.route(‘/index/’) def index(): uname = session.get(‘uname’) if uname: return ‘這個(gè)是主頁!?。 ?return redirect(url_for(‘login’,msg=‘請(qǐng)先登錄’))
注冊類視圖
app.add_url_rule(‘/login/’,view_func=LoginView.as_view(‘login’)) if name == ‘main’: app.secret_key = ‘xihwidfw9efw’ app.run(debug=True)
Local對(duì)象
需求
要實(shí)現(xiàn)并發(fā)效果, 每一個(gè)請(qǐng)求進(jìn)來的時(shí)候我們都開啟一個(gè)進(jìn)程, 這顯然是不合理的, 于是就可以使用 線程 那么線程中數(shù)據(jù)互相不隔離,存在修改數(shù)據(jù)的時(shí)候數(shù)據(jù)不安全的問題
Local對(duì)象
在Flask中,類似于 request 對(duì)象,其實(shí)是綁定到了一個(gè) werkzeug.local.Local 對(duì)象上。 這樣,即使是同一個(gè)對(duì)象,那么在多個(gè)線程中都是隔離的。類似的 對(duì)象還有 session 對(duì)象。
ThreadLocal變量
Python提供了ThreadLocal 變量,它本身是一個(gè)全局變量, 但是每個(gè)線程卻可以利用它來保存屬于自己的私有數(shù)據(jù), 這些私有數(shù)據(jù)對(duì)其他線程也是不可見的。
from threading import Thread,local local =local() local.request = ‘具體用戶的請(qǐng)求對(duì)象’ class MyThread(Thread): def run(self): local.request = ‘zs’ print(‘子線程:’,local.request) mythread = MyThread() mythread.start() mythread.join() print(‘主線程:’,local.request)
from werkzeug.local import Local local = Local() local.request = ‘具體用戶的請(qǐng)求對(duì)象’ class MyThread(Thread): def run(self): local.request = ‘sxt’ print(‘子線程:’,local.request) mythread = MyThread() mythread.start() mythread.join() print(‘主線程:’,local.request)
總結(jié)
只要滿足綁定到"local"或"Local"對(duì)象上的屬性,在每個(gè)線程中都是 隔離的,那么他就叫做 ThreadLocal 對(duì)象,也叫’ThreadLocal’變量。
Flask_app上下文
App上下文,也叫應(yīng)用上下文
上下文(感性的理解)
每一段程序都有很多外部變量,只有像add這種簡單的函數(shù)才是 沒有外部變量的。 一旦一段程序有了外部變量,這段程序就不 完整,不能獨(dú)立運(yùn)行。為了能讓這段程序可以運(yùn)行,就要給所 有的外部變量一個(gè)一個(gè)設(shè)置一些值。就些值所在的集合就是叫 上下文。 并且上下文這一概念在中斷任務(wù)的場景下具有重大意義,其中 任務(wù)在被中斷后,處理器保存上下文并提供中斷處理,因些在 這之后,任務(wù)可以在同一個(gè)地方繼續(xù)執(zhí)行。(上下文越小,延遲 越小)
舉例
運(yùn)行的Flask項(xiàng)目,每一個(gè)路由映射的內(nèi)容片段,都不可以單獨(dú) 拿出來使用.
當(dāng)獲取到了APP_Context以后,就可以直接通過程序映射的地 址訪問邏輯,并且可以重復(fù)使用。
上下文的一個(gè)典型應(yīng)用場景就是用來緩存一些我們需要在發(fā)生請(qǐng)求 之前或者要使用的資源。舉個(gè)例子,比如數(shù)據(jù)庫連接。當(dāng)我們在應(yīng) 用上下文中來存儲(chǔ)東西的時(shí)候你得選擇一個(gè)唯一的名字,這是因?yàn)?應(yīng)用上下文為 Flask 應(yīng)用和擴(kuò)展所共享。
應(yīng)用上下文:
應(yīng)用上下文是存放到一個(gè) LocalStack 的棧中。和應(yīng)用app相關(guān)的操作就 必須要用到應(yīng)用上下文
比如:
通過 current_app 獲取當(dāng)前的這個(gè) app 名字。
注意
在視圖函數(shù)中,不用擔(dān)心應(yīng)用上下文的問題。因?yàn)橐晥D函數(shù)要 執(zhí)行,那么肯定是通過訪問url的方式執(zhí)行的, 那么這種情況下,F(xiàn)lask底層就已經(jīng)自動(dòng)的幫我們把應(yīng)用上下文 都推入到了相應(yīng)的棧中。
如果想要在視圖函數(shù)外面執(zhí)行相關(guān)的操作, 比如: 獲取當(dāng)前的app名稱,那么就必須要手動(dòng)推入應(yīng)用上下文
第一種方式:便于理解的寫法
from flask import Flask,current_app app = Flask(name) #app上下文 app_context = app.app_context() app_context.push() print(current_app.name) @app.route(‘/’) def hello_world(): print(current_app.name) #獲取應(yīng)用的名稱 return ‘Hello World!’ if name == ‘main’: app.run(debug=True)
第二種方式:用with語句
from flask import Flask,current_app app = Flask(name) #app上下文 #換一種寫法 with app.app_context(): print(current_app.name) @app.route(‘/’) def hello_world(): print(current_app.name) #獲取應(yīng)用的名稱 return ‘Hello World!’ if name == ‘main’: app.run(debug=True)
Flask_線程隔離的g對(duì)象
保存為全局對(duì)象g對(duì)象的好處:
g對(duì)象是在整個(gè)Flask應(yīng)用運(yùn)行期間都是可以使用的。 并且也跟request一樣,是線程隔離的。 這個(gè)對(duì)象是專門用來存儲(chǔ)開發(fā)者自己定義的一些數(shù)據(jù),方便在整個(gè) Flask程序中都可以使用。 一般使用就是,將一些經(jīng)常會(huì)用到的數(shù)據(jù)綁定到上面,以后就直接 從g上面取就可以了,而不需要通過傳參的形式,這樣更加方便。
g對(duì)象使用場景:
有一個(gè)工具類utils.py 和 用戶辦理業(yè)務(wù):
def funa(uname): print(f’funa {uname}‘) def funb(uname): print(f’funb {uname}’) def func(uname): print(f’func {uname}')
用戶辦理業(yè)務(wù)
from flask import Flask,request from utils import funa,funb,func app = Flask(name) #Flask_線程隔離的g對(duì)象使用詳解 @app.route(“/profile/”) def my_profile(): #從url中取參 uname = request.args.get(‘uname’) #調(diào)用功能函數(shù)辦理業(yè)務(wù) funa(uname) funb(uname) func(uname) #每次都得傳參 麻煩,引入g對(duì)象進(jìn)行優(yōu)化 return “辦理業(yè)務(wù)成功” if name == ‘main’: app.run(debug=True)
優(yōu)化工具類utils.py
from flask import g def funa(): print(f’funa {g.uname}‘) def funb(): print(f’funb {g.uname}’) def func(): print(f’func {g.uname}')
Flask_鉤子函數(shù)介紹
鉤子函數(shù)概念
在Flask中鉤子函數(shù)是使用特定的裝飾器裝飾的函數(shù)。 為什么叫做鉤子函數(shù)呢,是因?yàn)殂^子函數(shù)可以在正常執(zhí)行的代碼 中,插入一段自己想要執(zhí)行的代碼。 那么這種函數(shù)就叫做鉤子函數(shù)。
常見的鉤子函數(shù)
before_first_request:處理項(xiàng)目的第一次請(qǐng)求之前執(zhí)行。
@app.before_first_request def first_request(): print(‘first time request’)
before_request:在每次請(qǐng)求之前執(zhí)行。通??梢杂眠@個(gè)裝飾 器來給視圖函數(shù)增加一些變量。請(qǐng)求已經(jīng)到達(dá)了Flask,但是還 沒有進(jìn)入到具體的視圖函數(shù)之前調(diào)用。一般這個(gè)就是在視圖函數(shù) 之前,我們可以把一些后面需要用到的數(shù)據(jù)先處理好,方便視圖 函數(shù)使用。
@app.before_request def before_request(): if not hasattr(g,‘glo1’): setattr(g,‘glo1’,‘想要設(shè)置的’)
teardown_appcontext:不管是否有異常,注冊的函數(shù)都會(huì)在 每次請(qǐng)求之后執(zhí)行。
@app.teardown_appcontext def teardown(exc=None): if exc is None: db.session.commit() else: db.session.rollback() db.session.remove()
template_filter:在使用Jinja2模板的時(shí)候自定義過濾器。
@app.template_filter(“upper”) def upper_filter(s): return s.upper()
context_processor:上下文處理器。使用這個(gè)鉤子函數(shù),必須 返回一個(gè)字典。這個(gè)字典中的值在所有模版中都可以使用。這個(gè) 鉤子函數(shù)的函數(shù)是,如果一些在很多模版中都要用到的變量,那 么就可以使用這個(gè)鉤子函數(shù)來返回,而不用在每個(gè)視圖函數(shù)中 的 render_template 中去寫,這樣可以讓代碼更加簡潔和好維護(hù)。
@app.context_processor def context_processor(): if hasattr(g,‘user’): return {“current_user”:g.user} else: return {}
errorhandler:errorhandler接收狀態(tài)碼,可以自定義返回這 種狀態(tài)碼的響應(yīng)的處理方法。在發(fā)生一些異常的時(shí)候,比如404 錯(cuò)誤,比如500錯(cuò)誤,那么如果想要優(yōu)雅的處理這些錯(cuò)誤,就可以 使用 errorhandler 來出來。
@app.errorhandler(404) def page_not_found(error): return ‘This page does not exist’,404
Flask_信號(hào)機(jī)制
信號(hào)機(jī)制
大白話來說,類似于兩方屬于敵對(duì)關(guān)系時(shí),某人在敵對(duì)方陣營進(jìn)行 交談,一旦遇到特殊情況,某人便會(huì)發(fā)送信號(hào),他的同伙接收(監(jiān) 聽)到他發(fā)的信號(hào)后,同伙便會(huì)做出一系列的應(yīng)對(duì)策略(進(jìn)攻|撤 退)。 flask中的信號(hào)使用的是一個(gè)第三方插件,叫做blinker。通過pip list看一下,如果沒有安裝,通過以下命令即可安裝blinker
pip install blinker
自定義信號(hào)步驟
自定義信號(hào)可分為3步來完成。
第一是創(chuàng)建一個(gè)信號(hào),第二是監(jiān)聽一個(gè)信號(hào),第三是發(fā)送一個(gè)信 號(hào)。
以下將對(duì)這三步進(jìn)行講解:
創(chuàng)建信號(hào):定義信號(hào)需要使用到blinker這個(gè)包的Namespace類來創(chuàng)建一個(gè)命名空間。比如定義一 個(gè)在訪問了某個(gè)視圖函數(shù)的時(shí)候的信號(hào)。示例代碼如下:
Namespace的作用:為了防止多人開發(fā)的時(shí)候,信號(hào)名字
沖突的問題 from blinker import Namespace mysignal = Namespace() signal1 = mysignal.signal(‘信號(hào)名稱’)
監(jiān)聽信號(hào):監(jiān)聽信號(hào)使用signal1對(duì)象的connect方法,在這個(gè)方法中需要傳遞一個(gè)函數(shù),用來監(jiān)聽 到這個(gè)信號(hào)后做該做的事情。示例代碼如下:
def func1(sender,uname): print(sender) print(uname) signal1.connect(func1)
發(fā)送信號(hào):發(fā)送信號(hào)使用signal1對(duì)象的send方法,這個(gè)方法可以傳遞一些其他參數(shù)過去。示例代 碼如下:
signal1.send(uname=‘momo’)
Flask信號(hào)使用場景_存儲(chǔ)用戶登錄日志
信號(hào)使用場景
定義一個(gè)登錄的信號(hào),以后用戶登錄進(jìn)來以后 就發(fā)送一個(gè)登錄信號(hào),然后能夠監(jiān)聽這個(gè)信號(hào) 在監(jiān)聽到這個(gè)信號(hào)以后,就記錄當(dāng)前這個(gè)用戶登錄的信息 用信號(hào)的方式,記錄用戶的登錄信息即登錄日志。
編寫一個(gè)signals.py文件創(chuàng)建登錄信號(hào)
from blinker import Namespace from datetime import datetime from flask import request,g namespace = Namespace() #創(chuàng)建登錄信號(hào) login_signal = namespace.signal(‘login’) def login_log(sender):
用戶名 登錄時(shí)間 ip地址
now = datetime.now() ip = request.remote_addr log_data = “{uname}{now}{ip}”.format(uname=g.uname, now=now, ip=ip) with open(‘login_log.txt’,‘a(chǎn)’) as f: f.write(log_data + “\n”) f.close() #監(jiān)聽信號(hào) login_signal.connect(login_log)
使用信號(hào)存儲(chǔ)用戶登錄日志
from flask import Flask,request,g from signals import login_signal app = Flask(name) @app.route(‘/login/’) def login():
通過查詢字符串的形式來傳遞uname這個(gè)參數(shù)
uname = request.args.get(‘uname’) if uname: g.uname = uname
發(fā)送信號(hào)
login_signal.send() return ‘登錄成功!’ else: return ‘請(qǐng)輸入用戶名!’ if name == ‘main’: app.run(debug=True)
Flask_內(nèi)置信號(hào)
Flask內(nèi)置了10個(gè)常用的信號(hào):
1 template_rendered:模版渲染完成后的信號(hào)。
2 before_render_template:模版渲染之前的信號(hào)。
3 request_started:請(qǐng)求開始之前,在到達(dá)視圖函數(shù)之前發(fā)送信號(hào)。
4 request_finished:請(qǐng)求結(jié)束時(shí),在響應(yīng)發(fā)送給客戶端之前發(fā)送信號(hào)。
5 request_tearing_down:請(qǐng)求對(duì)象被銷毀時(shí)發(fā)送的信號(hào),即使在請(qǐng)求過程中發(fā)生異常也會(huì)發(fā)送信 號(hào)。
6 got_request_exception:在請(qǐng)求過程中拋出異常時(shí)發(fā)送信號(hào),異常本身會(huì)通過exception傳遞到訂 閱(監(jiān)聽)的函數(shù)中。一般可以監(jiān)聽這個(gè)信號(hào),來記錄網(wǎng)站異常信息。
7 appcontext_tearing_down:應(yīng)用上下文被銷毀時(shí)發(fā)送的信號(hào)。
8 appcontext_pushed:應(yīng)用上下文被推入到棧上時(shí)發(fā)送的信號(hào)。
9 appcontext_popped:應(yīng)用上下文被推出棧時(shí)發(fā)送的信號(hào)。
10 message_flashed:調(diào)用了Flask的 flash 方法時(shí)發(fā)送的信號(hào)。
WTForms介紹和基本使用
WTForms介紹
這個(gè)插件庫主要有兩個(gè)作用。 第一個(gè)是做表單驗(yàn)證,將用戶提交上來的數(shù)據(jù)進(jìn)行驗(yàn)證是否符合系 統(tǒng)要求。 第二個(gè)是做模版渲染。 (了解即可) 官網(wǎng):https://wtforms.readthedocs.io/en/latest/index.html
Flask-WTF是簡化了WTForms操作的一個(gè)第三方庫。WTForms表單 的兩個(gè)主要功能是驗(yàn)證用戶提交數(shù)據(jù)的合法性以及渲染模板。而 Flask-WTF還包括一些其他的功能:CSRF保護(hù),文件上傳等。 安裝Flask-WTF默認(rèn)也會(huì)安裝WTForms,因此使用以下命令來安裝 Flask-WTF和WTForms:
pip install flask-wtf
WTForms表單驗(yàn)證的基本使用
1 自定義一個(gè)表單類,繼承自wtforms.Form類。
2 定義好需要驗(yàn)證的字段,字段的名字必須和模版中那些需要驗(yàn)證的input標(biāo)簽的name屬性值保持一 致。
3 在需要驗(yàn)證的字段上,需要指定好具體的數(shù)據(jù)類型。
4 在相關(guān)的字段上,指定驗(yàn)證器。
5 以后在視圖函數(shù)中,只需要使用這個(gè)表單類的對(duì)象,并且把需要驗(yàn)證的數(shù)據(jù),也就是request.form 傳給這個(gè)表單類,再調(diào)用表單類對(duì)象.validate()方法進(jìn)行,如果返回True,那么代表用戶輸入的數(shù) 據(jù)都是符合格式要求的,F(xiàn)lase則代表用戶輸入的數(shù)據(jù)是有問題的。如果驗(yàn)證失敗了,那么可以通 過表單類對(duì)象.errors來獲取具體的錯(cuò)誤信息。
from flask import Flask,render_template,request from wtforms import Form,StringField from wtforms.validators import Length,EqualTo app = Flask(name) @app.route(‘/’) def index(): return ‘Hello! ’ class RegisterForm(Form): uname = StringField(validators=[Length(min=2,max=10,message=‘用戶名長度2-10之間’)]) pwd = StringField(validators=[Length(min=2,max=10)]) pwd2 = StringField(validators=[Length(min=2,max=10),EqualTo(‘pwd’,message=‘2次密碼不一致’)]) @app.route(’/register/‘, methods=[‘GET’,‘POST’]) def register(): if request.method == ‘GET’: return render_template(‘register.html’) else: form = RegisterForm(request.form) if form.validate(): # 驗(yàn)證成功:True,失?。篎alse return ‘驗(yàn)證成功!’ else: return f’驗(yàn)證失敗!{form.errors}’ if name == ‘main’: app.run(debug=True)
WTForms常用驗(yàn)證器
頁面把數(shù)據(jù)提交上來,需要經(jīng)過表單驗(yàn)證,進(jìn)而需要借助驗(yàn)證器來 進(jìn)行驗(yàn)證,以下是常用的內(nèi)置驗(yàn)證器:
Length:字符串長度限制,有min和max兩個(gè)值進(jìn)行限制。
username = StringField(validators=[Length(min=3,max=10,message=“用戶名長度必須在3到10位之間”)])
EqualTo:驗(yàn)證數(shù)據(jù)是否和另外一個(gè)字段相等,常用的就是密碼 和確認(rèn)密碼兩個(gè)字段是否相等。
password_repeat = StringField(validators=[Length(min=6,max=10),EqualTo(“password”)])
Email:驗(yàn)證上傳的數(shù)據(jù)是否為郵箱數(shù)據(jù)格式 如:223333@qq. com。
email = StringField(validators=[Email()])
InputRequired:驗(yàn)證該項(xiàng)數(shù)據(jù)為必填項(xiàng),即要求該項(xiàng)非空。
username = StringField(validators=[input_required()])
NumberRange:數(shù)值的區(qū)間,有min和max兩個(gè)值限制,如果 處在這兩個(gè)數(shù)字之間則滿足。
age = IntegerField(validators=[NumberRange(12,18)])
Regexp:定義正則表達(dá)式進(jìn)行驗(yàn)證,如驗(yàn)證手機(jī)號(hào)碼。
phone = StringField(validators=[Regexp(r’1[34578]\d{9}')])
URL:必須是URL的形式 如http://www.bjsxt.com。
home_page = StringField(validators=[URL()])
UUID:驗(yàn)證數(shù)據(jù)是UUID類型。
uuid = StringField(validators=[UUID()])
WTForms自定義驗(yàn)證器
只有當(dāng)WTForms內(nèi)置的驗(yàn)證器不夠使的時(shí)候,才需要使用自定義驗(yàn) 證器。 如果想要對(duì)表單中的某個(gè)字段進(jìn)行更細(xì)化的驗(yàn)證,那么可以針對(duì)這 個(gè)字段進(jìn)行單獨(dú)的驗(yàn)證。
自定義驗(yàn)證器步驟如下:
1 定義一個(gè)方法,方法的名字規(guī)則是: validate_字段名(self,field) 。
2 在方法中,使用 field.data 可以獲取到這個(gè)字段的具體的值。
3 驗(yàn)證時(shí),如果數(shù)據(jù)滿足條件,那么可以什么都不做。如果驗(yàn)證失敗,那么應(yīng)該拋出一個(gè) wtforms.validators.ValidationError 的異常,并且把驗(yàn)證失敗 的信息傳到這個(gè)異常類中。
場景:驗(yàn)證碼實(shí)現(xiàn)
關(guān)鍵代碼演示:(實(shí)現(xiàn)驗(yàn)證碼 驗(yàn)證)
from flask import session from wtforms import Form,StringField,IntegerField from wtforms.validators import Length,EqualTo,Email,InputRequired,NumberRan ge,Regexp,URL,UUID,ValidationError class RegisterForm2(Form): email = StringField(validators=[Email()]) uname = StringField(validators=[InputRequired()]) age = IntegerField(validators=[NumberRange(18,40)]) phone = StringField(validators=[Regexp(r’1[34578]\d{9}')]) phomepage = StringField(validators=[URL()]) uuid = StringField(validators=[UUID()]) code = StringField(validators=[Length(4,4)]) #取到的值 和服務(wù)器上 session上存儲(chǔ)的值對(duì)比 def validate_code(self,field): print(field.data,session.get(‘code’)) if field.data !=session.get(‘code’): raise ValidationError(‘驗(yàn)證碼不一致!’)
Flask安全上傳文件
上傳文件步驟:
在模版html中,表單需要指定 enctype=‘multipart/form-data’ 才能上傳文 件。 在后臺(tái)如果想要獲取上傳的文件,那么應(yīng)該使用 request.files.get(‘文件 名’) 來獲取。 保存文件之前,先要使用 werkzeug.utils.secure_filename 來對(duì)上傳上來的文 件名進(jìn)行一個(gè)過濾。能保證不會(huì)有安全問題。 獲取到上傳上來的文件后,使用 文件對(duì)象.save(路徑) 方法來保存文件。 路徑=完整路徑=路徑名+文件名
upload.html頁面
上傳文件
頭像:描述:
app.py文件
from flask import Flask,request,render_template import os from werkzeug.utils import secure_filename app = Flask(name) UPLOAD_PATH = os.path.join(os.path.dirname(file),‘images’) @app.route(‘/upload/’,methods=[‘GET’,‘POST’]) def upload(): if request.method == ‘GET’: return render_template(‘upload.html’) else: desc = request.form.get(“desc”) pichead = request.files.get(“pichead”) filename = secure_filename(pichead.filename) #包裝一下 保證文件安全 #pichead.save(os.path.join(UPLOAD_PATH,pichead.filename)) #可優(yōu)化 pichead.save(os.path.join(UPLOAD_PATH,filename)) #已優(yōu)化 print(desc) return ‘文件上傳成功’
if name == ‘main’: app.run(debug=True)
訪問文件
從服務(wù)器上讀取文件,應(yīng)該定義一個(gè)url與視圖函數(shù),來獲取指定的 文件。 在這個(gè)視圖函數(shù)中,使用 send_from_directory(文件的目錄,文件名) 來獲取。
from flask import Flask import os from flask import send_from_directory app = Flask(name) UPLOAD_PATH = os.path.join(os.path.dirname(file),‘images’) @app.route(‘/images//’) def get_image(filename): return send_from_directory(UPLOAD_PATH,filename) if name == ‘main’: app.run(debug=True)
利用flask-wtf驗(yàn)證上傳的文件
關(guān)鍵點(diǎn):
1 定義驗(yàn)證表單類的時(shí)候,對(duì)文件類型的字段,需要采用 FileField 這個(gè)類型,即wtforms.FileField 2 驗(yàn)證器需要從 flask_wtf.file 中導(dǎo)入。 flask_wtf.file.FileRequired 和 flask_wtf.file.FileAllowed
3 flask_wtf.file.FileRequired 是用來驗(yàn)證文件上傳不能為空。
4 flask_wtf.file.FileAllowed 用來驗(yàn)證上傳的文件的后綴名, 如常見圖片后綴 .jpg 和.png以及.gif等。
5 在視圖函數(shù)中,需要使用 from werkzeug.datastructures import CombinedMultiDict 來把 request.form 與 request.files 來進(jìn)行合并。
6 最后使用 表單驗(yàn)證對(duì)象.validate()進(jìn)行驗(yàn)證。
代碼如下:
upload.html頁面
上傳文件
頭像:描述:
formscheck.py文件
from wtforms import Form,FileField,StringField from wtforms.validators import InputRequired
flask_wtf
from flask_wtf.file import FileRequired,FileAllowed class UploadForm(Form): pichead = FileField(validators= [FileRequired(),FileAllowed([‘jpg’,‘png’,‘gif’])]) desc = StringField(validators=[InputRequired()])
app.py文件
from flask import Flask,request,render_template import os from werkzeug.utils import secure_filename from formscheck import UploadForm from werkzeug.datastructures import CombinedMultiDict app = Flask(name) UPLOAD_PATH = os.path.join(os.path.dirname(file),‘images’) #利用flask-wtf驗(yàn)證上傳的文件 @app.route(‘/upload/’,methods=[‘GET’,‘POST’]) def upload(): if request.method == ‘GET’: return render_template(‘upload.html’) else: form = UploadForm(CombinedMultiDict([request.form,request.files])) if form.validate():
desc = request.form.get(“desc”)
pichead = request.files.get(“pichead”)
desc = form.desc.data pichead = form.pichead.data filename = secure_filename(pichead.filename) pichead.save(os.path.join(UPLOAD_PATH,filename)) print(desc) return ‘文件上傳成功’ else: print(form.errors) return “文件上傳失敗” if name == ‘main’: app.run(debug=True)
Restful介紹
1.Restful接口規(guī)范
REST 指的是一組架構(gòu)約束條件和原則。滿足這些約束條件和原則的 應(yīng)用程序或設(shè)計(jì)就是 RESTful。 RESTful是一種軟件架構(gòu)風(fēng)格、設(shè)計(jì)風(fēng)格,而不是標(biāo)準(zhǔn),只是提供了 一組設(shè)計(jì)原則和約束條件。 它主要用于客戶端和服務(wù)器交互類的軟件。基于這個(gè)風(fēng)格設(shè)計(jì)的軟 件可以更簡潔,更有層次。 RESTful接口規(guī)范是用于在前端與后臺(tái)進(jìn)行通信的一套規(guī)范。使用這 個(gè)規(guī)范可以讓前后端開發(fā)變得更加輕松。
**2.適用場景:**一個(gè)系統(tǒng)的數(shù)據(jù)庫數(shù)據(jù),展現(xiàn)的平臺(tái)有PC端、移動(dòng) 端、app端、ios端。 前端工程師:都遵循RESTful編程規(guī)范 后端工程師:都遵循RESTful編程規(guī)范 最終結(jié)果:開發(fā)效率高,便于管理。
**3.協(xié)議:**用http或者h(yuǎn)ttps協(xié)議。
4.數(shù)據(jù)傳輸格式: 數(shù)據(jù)傳輸?shù)母袷綉?yīng)該都用json格式。
**5.url鏈接規(guī)則:**url鏈接中,不能有動(dòng)詞,只能有名詞。 并且對(duì)于一些名詞,如果出現(xiàn)復(fù)數(shù),那么應(yīng)該在后面加s。 比如:獲取新聞列表,應(yīng)該使用 /news/ ,而不應(yīng)該使用/get_news/
6.HTTP請(qǐng)求方式: GET:從服務(wù)器上獲取資源。 POST:在服務(wù)器上新增或者修改一個(gè)資源。 PUT:在服務(wù)器上更新資源。(客戶端提供所有改變后的數(shù)據(jù)) PATCH:在服務(wù)器上更新資源。(客戶端只提供需要改變的屬性) DELETE:從服務(wù)器上刪除資源。
7.狀態(tài)碼:
Restful的基本使用
1.介紹:
優(yōu)勢: Flask-Restful是一個(gè)專門用來寫restful api的一個(gè)插件。 使用它可以快速的集成restful api接口功能。 在系統(tǒng)的純api的后臺(tái)中,這個(gè)插件可以幫助我們節(jié)省很多時(shí)間。
缺點(diǎn): 如果在普通的網(wǎng)站中,這個(gè)插件就沒有優(yōu)勢了,因?yàn)樵谄胀ǖ木W(wǎng)站 開發(fā)中,是需要去渲染HTML代碼的, 而Flask-Restful在每個(gè)請(qǐng)求中都是返回json格式的數(shù)據(jù)。
**2.安裝:**pip install flask-restful
3.基本使用:
定義Restful的類視圖:
從 flask_restful 中導(dǎo)入 Api ,來創(chuàng)建一個(gè) api 對(duì)象。 寫一個(gè)類視圖,讓他繼承自 Resource 類,然后在這個(gè)里面,使用 你想要的請(qǐng)求方式來定義相應(yīng)的方法,比如你想要將這個(gè)類視圖只 能采用 post 請(qǐng)求,那么就定義一個(gè) post 方法。 使用 api.add_resource 來添加類視圖與 url 。
from flask import Flask,url_for
pip install flask-restful
from flask_restful import Resource,Api app = Flask(name)
建立Api對(duì)象,并綁定應(yīng)用APP
api = Api(app) class LoginView(Resource): def get(self): return {“flag”:True} def post(self): return {“flag”:False}
建立路由映射
api.add_resource(LoginView,‘/login/’)
api.add_resource(LoginView,‘/login/’,‘/login2/’,endpoint=‘login’) with app.test_request_context():
werkzeug.routing.BuildError: Could not build url for endpoint ‘LoginView’.
Did you mean ‘loginview’ instead?
默認(rèn)沒有寫endpoint反向url_for函數(shù)通過小寫函數(shù)名
如果有多個(gè)url,會(huì)返回第1個(gè)URL
print(url_for(‘loginview’))
print(url_for(‘login’)) if name == ‘main’: app.run(debug=True)
注意
1 如果你想返回json數(shù)據(jù),那么就使用flask_restful,如果你是想渲染模版,那么還是采用之前 的方式,就是 app.route 的方式。
2 url還是跟之前的一樣,可以傳遞參數(shù)。也跟之前的不一樣,可以指定多個(gè)url。
3 endpoint是用來給url_for反轉(zhuǎn)url的時(shí)候指定的。如果不寫endpoint,那么將會(huì)使用視圖的 名字的小寫來作為endpoint。
4 add_resource的第二個(gè)參數(shù)是訪問這個(gè)視圖函數(shù)的url,這個(gè)url可以跟之前的route一樣,可 以傳遞參數(shù),并且還有一點(diǎn)不同的是,這個(gè)方法可以傳遞多個(gè)url來指定這個(gè)視圖函數(shù)。
Flask_RESTful參數(shù)驗(yàn)證
參數(shù)驗(yàn)證
參數(shù)驗(yàn)證也叫參數(shù)解析 Flask-Restful插件提供了類似WTForms來驗(yàn)證提交的數(shù)據(jù)是否合法 的包,叫做reqparse。
基本用法
1 通過 flask_restful.reqparse 中 RequestParser 建立解析器
2 通過 RequestParser 中的 add_argument 方法定義字段與解析規(guī)則
3 通過 RequestParser 中的 parse_args 來解析參數(shù)
1 解析正確,返回正確參數(shù)
2 解析錯(cuò)誤,返回錯(cuò)誤信息給前端
from flask import Flask from flask_restful import Api,Resource from flask_restful.reqparse import RequestParser app = Flask(name) api = Api(app) class RegisterView(Resource): def post(self):
建立解析器
parser = RequestParser()
定義數(shù)據(jù)的解析規(guī)則
parser.add_argument(‘uname’,type=str,required=True,help=‘用戶名驗(yàn)證錯(cuò)誤’,trim=True)
解析數(shù)據(jù)
args = parser.parse_args()
正確,直接獲取參數(shù)
print(args)
錯(cuò)誤,回饋到前端
響應(yīng)數(shù)據(jù)
return {‘msg’:‘注冊成功??!’}
建立映射關(guān)系
api.add_resource(RegisterView,‘/register/’) if name == ‘main’: app.run(debug=True)
add_argument方法參數(shù)詳解
add_argument方法可以指定這個(gè)字段的名字,這個(gè)字段的數(shù)據(jù)類 型等,驗(yàn)證錯(cuò)誤提示信息等,具體如下:
**1 default:**默認(rèn)值,如果這個(gè)參數(shù)沒有值,那么將使用這個(gè)參數(shù) 指定的默認(rèn)值。
2 required:是否必須。默認(rèn)為False,如果設(shè)置為True,那么這 個(gè)參數(shù)就必須提交上來。
**3 type:**這個(gè)參數(shù)的數(shù)據(jù)類型,如果指定,那么將使用指定的數(shù) 據(jù)類型來強(qiáng)制轉(zhuǎn)換提交上來的值??梢允褂胮ython自帶的一些 數(shù)據(jù)類型(如str或者int),也可以使用flask_restful.inputs下的一 些特定的數(shù)據(jù)類型來強(qiáng)制轉(zhuǎn)換。
url:會(huì)判斷這個(gè)參數(shù)的值是否是一個(gè)url,如果不是,那么就會(huì)拋出異常。
regex:正則表達(dá)式。
date:將這個(gè)字符串轉(zhuǎn)換為datetime.date數(shù)據(jù)類型。如果轉(zhuǎn)換不成功,則會(huì)拋出一個(gè)異常.
4 choices:固定選項(xiàng)。提交上來的值只有滿足這個(gè)選項(xiàng)中的值才 符合驗(yàn)證通過,否則驗(yàn)證不通過。
5 help:錯(cuò)誤信息。如果驗(yàn)證失敗后,將會(huì)使用這個(gè)參數(shù)指定的 值作為錯(cuò)誤信息。
**6 trim:**是否要去掉前后的空格
from flask import Flask from flask_restful import Api,Resource,inputs from flask_restful.reqparse import RequestParser app = Flask(name) api = Api(app) class RegisterView(Resource): def post(self):
建立解析器
parser = RequestParser()
定義解析規(guī)則
parser.add_argument(‘uname’,type=str,required=True,trim=True,help=‘用戶名不符合規(guī)范’) parser.add_argument(‘pwd’,type=str,help=‘密碼錯(cuò)誤’,default=‘123456’) parser.add_argument(‘a(chǎn)ge’,type=int,help=‘年齡驗(yàn)證錯(cuò)誤!’) parser.add_argument(‘gender’,type=str,choices=[‘男’, ‘女’,‘保密’],help=‘性別驗(yàn)證錯(cuò)誤’) parser.add_argument(‘birthday’,type=inputs.date,help=‘生日驗(yàn)證錯(cuò)誤’) parser.add_argument(‘phone’,type=inputs.regex(‘^1[356789]\d{9}$’),help=‘電話驗(yàn)證錯(cuò)誤’) parser.add_argument(‘homepage’,type=inputs.url,help=‘個(gè)人主頁驗(yàn)證錯(cuò)誤’)
解析數(shù)據(jù)
args = parser.parse_args() print(args) return {‘msg’:‘注冊成功!’}
api.add_resource(RegisterView,‘/register/’) if name == ‘main’: app.run(debug=True)
Flask_SQLAlchemy
SQLAlchemy的使用
數(shù)據(jù)庫是一個(gè)網(wǎng)站的基礎(chǔ)。 比如MySQL、MongoDB、SQLite、PostgreSQL等,這里我們以 MySQL為例進(jìn)行講解。 SQLAlchemy是一個(gè)ORM框架。
對(duì)象關(guān)系映射(英語:Object Relational Mapping,簡稱 ORM,或O/RM,或O/R mapping),是一種程序設(shè)計(jì)技術(shù), 用于實(shí)現(xiàn)面向?qū)ο缶幊陶Z言里不同類型系統(tǒng)的數(shù)據(jù)之間的轉(zhuǎn) 換。 從效果上說,它其實(shí)是創(chuàng)建了一個(gè)可在編程語言里使用的“虛擬 對(duì)象數(shù)據(jù)庫”。
大白話 對(duì)象模型與數(shù)據(jù)庫表的映射
為什么要有SQLAlchemy?
隨著項(xiàng)目的越來越大,采用寫原生SQL的方式在代碼中會(huì)出現(xiàn)大量 重復(fù)的SQL語句,那么,問題就出現(xiàn)了:
1.SQL語句重復(fù)利用率不高,越復(fù)雜的SQL語句條件越多,代碼越長,會(huì)出現(xiàn)很多相近的SQL語句。
2.很多SQL語句 是在業(yè)務(wù)邏輯中拼接出來的,如果數(shù)據(jù)庫需要更改,就要去修改這些邏輯,這會(huì)容易 漏掉對(duì)某些SQL語句的修改。
3 寫SQL時(shí)容易忽略web安全問題,造成隱患。
而ORM可以通過類的方式去操作數(shù)據(jù)庫而不用再寫原生的SQL語 句,通過把表映射成類,把行作為實(shí)例(一條數(shù)據(jù)),把字段作為屬 性,ORM在執(zhí)行對(duì)象操作的時(shí)候最終還是會(huì)把對(duì)象的操作轉(zhuǎn)換為數(shù) 據(jù)庫的原生語句,但使用ORM有許多優(yōu)點(diǎn):
1.易用性:使用ORM做數(shù)據(jù)庫開發(fā)可以有效減少重復(fù)SQL語句的概率,寫出來的模型也更加直觀、清 晰
2.性能損耗?。篛RM轉(zhuǎn)換成底層數(shù)據(jù)庫操作指令確實(shí)會(huì)有一些開銷。但是從實(shí)際情況來看,這種性能 損耗很少(不足5%),只要不是針對(duì)性能有嚴(yán)苛的要求,綜合考慮開發(fā)效率、代碼閱讀性,帶來 的好處遠(yuǎn)大于性能損耗,而且項(xiàng)目越大作用越明顯。
3 設(shè)計(jì)靈活:可以輕松的寫出復(fù)雜的查詢。
4.可移植性:SQLAlchemy封裝了底層的數(shù)據(jù)庫實(shí)現(xiàn),支持多個(gè)關(guān)系數(shù)據(jù)庫引擎,包括流行的 Mysql、PostgreSQL和SQLite,可以非常輕松的切換數(shù)據(jù)庫。
使用ORM操作數(shù)據(jù)庫將變得非常簡單!
class Person: name = ‘xx’ age = 18 country =‘xx’
Person類 -> 數(shù)據(jù)庫中的一張表
Person類中的屬性 -> 數(shù)據(jù)庫中一張表字段
Person類的一個(gè)對(duì)象 -> 數(shù)據(jù)庫中表的一條數(shù)據(jù)
p = Person(‘xx’,xx)
p.save()
insert into table values (‘xx’,xx)
我們會(huì)以 MySQL+ SQLAlchemy 組合進(jìn)行講解。
在操作數(shù)據(jù)庫操作之前,先確保你已經(jīng)安裝了以下軟件:
mysql:如果是在windows上,到官網(wǎng)下載pymysql:pymysql是用Python來操作mysql的包 pip install pymysqlpip install pymysqlSQLAlchemy:SQLAlchemy是一個(gè)數(shù)據(jù)庫的ORM框架,我們在 后面會(huì)用到
pip install pymysql
pip install SQLAlchemy
SQLAlchemy操作數(shù)據(jù)庫
連接數(shù)據(jù)庫
from sqlalchemy import create_engine def conn_db1():
數(shù)據(jù)庫的變量
HOST = ‘192.168.30.151’ # 127.0.0.1/localhost PORT = 3306 DATA_BASE = ‘flask_db’ USER = ‘root’ PWD = ‘123’
DB_URI = f’數(shù)據(jù)庫的名+驅(qū)動(dòng)名://{USER}:{PWD}@{HOST}:{PORT}/{DATA_BASE}’
DB_URI = f’mysql+pymysql://{USER}:{PWD}@{HOST}:{PORT}/{DATA_BASE}’ engine = create_engine(DB_URI)
執(zhí)行一個(gè)SQL
sql = ‘select 2;’ conn = engine.connect() rs = conn.execute(sql) print(rs.fetchone())
執(zhí)行原生SQL
def conn_db2():
數(shù)據(jù)庫的變量
HOST = ‘192.168.30.151’ # 127.0.0.1/localhost PORT = 3306 DATA_BASE = ‘flask_db’ USER = ‘root’ PWD = ‘123’
DB_URI = f’數(shù)據(jù)庫的名+驅(qū)動(dòng)名://{USER}:{PWD}@{HOST}:{PORT}/{DATA_BASE}’
DB_URI = f’mysql+pymysql://{USER}:{PWD}@{HOST}:{PORT}/{DATA_BASE}’ ‘’’
創(chuàng)建一個(gè)引擎,專門鏈接數(shù)據(jù)庫用的
engine = create_engine(DB_URI) sql = ‘create table t_user(id int primary key auto_increment, name varchar(32));’
鏈接數(shù)據(jù)庫
conn = engine.connect()
執(zhí)行SQL即可
conn.execute(sql) ‘’’ def conn_db3():
數(shù)據(jù)庫的變量
HOST = ‘192.168.30.151’ # 127.0.0.1/localhost PORT = 3306 DATA_BASE = ‘flask_db’ USER = ‘root’ PWD = ‘123’
DB_URI = f’數(shù)據(jù)庫的名+驅(qū)動(dòng)名://{USER}:{PWD}@{HOST}:{PORT}/{DATA_BASE}’
DB_URI = f’mysql+pymysql://{USER}:{PWD}@{HOST}:{PORT}/{DATA_BASE}’
創(chuàng)建一個(gè)引擎,專門鏈接數(shù)據(jù)庫用的
engine = create_engine(DB_URI) sql = ‘create table t_user1(id int primary key auto_increment, name varchar(32));’
鏈接數(shù)據(jù)庫
with engine.connect() as conn:
執(zhí)行SQL即可
conn.execute(sql)
ORM模型映射到數(shù)據(jù)庫中
用 declarative_base 根據(jù) engine 創(chuàng)建一個(gè)ORM基類
from sqlalchemy.ext.declarative import declarative_base engine = create_engine(DB_URI) Base = declarative_base(engine)
用這個(gè) Base 類作為基類來寫自己的ORM類。要定義 __tablename__ 類 屬性,來指定這個(gè)模型映射到數(shù)據(jù)庫中的表名
class Person(Base): tablename =‘t_person’
創(chuàng)建屬性來映射到表中的字段,所有需要映射到表中的屬性都應(yīng) 該為Column類型
class Person(Base): tablename =‘t_person’
在這個(gè)ORM模型中創(chuàng)建一些屬性,來跟表中的字段進(jìn)行一一映射。
這些屬性必須是sqlalchemy給我們提供好的數(shù)據(jù)類型
id = Column(Integer,primary_key=True,autoincrement=True) name = Column(String(50)) age = Column(Integer) country = Column(String(50))
使用 Base.metadata.create_all() 來將模型映射到數(shù)據(jù)庫中
Base.metadata.create_all()
注意
一旦使用 Base.metadata.create_all() 將模型映射到數(shù)據(jù)庫中后,即使改變 了模型的字段,也不會(huì)重新映射了
SQLAlchemy對(duì)數(shù)據(jù)的增刪改查操作
構(gòu)建session對(duì)象
所有和數(shù)據(jù)庫的ORM操作都必須通過一個(gè)叫做 session 的會(huì)話對(duì)象 來實(shí)現(xiàn),通過以下代碼來獲取會(huì)話對(duì)象
from sqlalchemy.orm import sessionmaker engine = create_engine(DB_URI) Base = declarative_base(engine) session = sessionmaker(engine)()
添加對(duì)象
def create_data_one(): with Session() as session: p1 = Person(name = ‘劉備’,age = 6 ,country=‘北京’) session.add(p1) session.commit() def create_data_many(): with Session() as session: p2 = Person(name = ‘呂布’,age = 19 ,country=‘北京’) p3 = Person(name = ‘貂蟬’,age = 18 ,country=‘北京’) session.add_all([p2,p3]) session.commit()
查找對(duì)象
def query_data_all(): with Session() as session: all_person = session.query(Person).all() for p in all_person: print(p.name) def query_data_one(): with Session() as session: p1 = session.query(Person).first() print(p1.name) def query_data_by_params(): with Session() as session:
p1 = session.query(Person).filter_by(name=‘呂布’).first()
p1 = session.query(Person).filter(Person.name == ‘呂布’).first() print(p1.age)
修改對(duì)象
def update_data(): with Session() as session: p1 = session.query(Person).filter(Person.name == ‘呂布’).first() p1.age = 20
提交事務(wù)
session.commit()
刪除對(duì)象
將需要?jiǎng)h除的數(shù)據(jù)從數(shù)據(jù)庫中查找出來,然后使用 session.delete 方法將 這條數(shù)據(jù)從session中刪除,最后做commit操作就可以了
def delete_data(): with Session() as session: p1 = session.query(Person).filter(Person.name == ‘貂蟬’).first() session.delete(p1) session.commit()
SQLAlchemy常用數(shù)據(jù)類型
Integer:整形,映射到數(shù)據(jù)庫中是int類型。
Float:浮點(diǎn)類型,映射到數(shù)據(jù)庫中是float類型。他占據(jù)的32 位。
Double:雙精度浮點(diǎn)類型,映射到數(shù)據(jù)庫中是double類型,占 據(jù)64位 (SQLALCHEMY中沒有)。 String:可變字符類型,映射到數(shù)據(jù)庫中是varchar類型.
Boolean:布爾類型,映射到數(shù)據(jù)庫中的是tinyint類型。
DECIMAL:定點(diǎn)類型。是專門為了解決浮點(diǎn)類型精度丟失的問 題的。在存儲(chǔ)錢相關(guān)的字段的時(shí)候建議大家都使用這個(gè)數(shù)據(jù)類 型。
這個(gè)類型使用的時(shí)候需要傳遞兩個(gè)參數(shù),第一個(gè)參數(shù)是用來標(biāo)記這個(gè)字段總能能存儲(chǔ)多少個(gè)數(shù) 字,第二個(gè)參數(shù)表示小數(shù)點(diǎn)后有多少位。
Enum:枚舉類型。指定某個(gè)字段只能是枚舉中指定的幾個(gè)值, 不能為其他值。在ORM模型中,使用Enum來作為枚舉,示例代 碼如下:
class News(Base): tablename = ‘t_news’ tag = Column(Enum(“python”,‘flask’,‘django’))
在Python3中,已經(jīng)內(nèi)置了enum這個(gè)枚舉的模塊,我們也可以 使用這個(gè)模塊去定義相關(guān)的字段。示例代碼如下:
class TagEnum(enum.Enum): python = “python” flask = “flask” django = “django” class News(Base): tablename = ‘t_news’ id = Column(Integer,primary_key=True,autoincrement=True) tag = Column(Enum(TagEnum))
Date:存儲(chǔ)時(shí)間,只能存儲(chǔ)年月日。映射到數(shù)據(jù)庫中是date類 型。在Python代碼中,可以使用 datetime.date 來指定。
DateTime:存儲(chǔ)時(shí)間,可以存儲(chǔ)年月日時(shí)分秒毫秒等。映射到 數(shù)據(jù)庫中也是datetime類型。在Python代碼中,可以使用 datetime.datetime 來指定。
Time:存儲(chǔ)時(shí)間,可以存儲(chǔ)時(shí)分秒。映射到數(shù)據(jù)庫中也是time 類型。在Python代碼中,可以使用 datetime.time 來至此那個(gè)。示例 代碼如下:
class News(Base): tablename = ‘t_news’ create_time = Column(Time) news =News(create_time=time(hour=11,minute=11,second=11))
Text:存儲(chǔ)長字符串。一般可以存儲(chǔ)6W多個(gè)字符。如果超出了 這個(gè)范圍,可以使用LONGTEXT類型。映射到數(shù)據(jù)庫中就是text 類型。
LONGTEXT:長文本類型,映射到數(shù)據(jù)庫中是longtext類型。
from sqlalchemy import create_engine,Column,Integer,String,Float,Enum,Boolean,DECIMAL,Text,Date,DateTime,Time from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.dialects.mysql import LONGTEXT from sqlalchemy.orm import sessionmaker import enum from datetime import date from datetime import datetime from datetime import time
準(zhǔn)備數(shù)據(jù)庫的一堆信息 ip port user pwd 數(shù)據(jù)庫的名稱 按要求組織格式
HOSTNAME = ‘127.0.0.1’ PORT = ‘3306’ DATABASE = ‘first_sqlalchemy’ USERNAME = ‘root’ PASSWORD = ‘root’
dialect+driver://username:password@host:port/database?charset=utf8
按照上述的格式來 組織數(shù)據(jù)庫信息
DB_URI =“mysql+pymysql://{username}:{password}@{host}:{port}/{db}?charset=utf8”.format(username=USERNAME,password=PASSWORD, host=HOSTNAME,port=PORT,db=DATABASE)
創(chuàng)建數(shù)據(jù)庫引擎
engine = create_engine(DB_URI)
創(chuàng)建會(huì)話對(duì)象
session = sessionmaker(engine)()
定義一個(gè)枚舉類
class TagEnum(enum.Enum): python=“PYHTON2” flask=“FLASK2” django =“DJANGO”
創(chuàng)建一個(gè)ORM模型 說明基于sqlalchemy 映射到mysql數(shù)據(jù)庫的常用字段類型有哪些?
Base = declarative_base(engine) class News(Base): tablename=‘news’ id = Column(Integer,primary_key=True,autoincrement=True) price1 = Column(Float) #存儲(chǔ)數(shù)據(jù)時(shí)存在精度丟失問題 price2 = Column(DECIMAL(10,4)) title = Column(String(50)) is_delete =Column(Boolean) tag1 =Column(Enum(‘PYTHON’,‘FLASK’,‘DJANGO’)) # 枚舉常規(guī)寫法 tag2 =Column(Enum(TagEnum)) #枚舉另一種寫法 create_time1=Column(Date) create_time2=Column(DateTime) create_time3=Column(Time) content1 =Column(Text) content2 =Column(LONGTEXT)
Base.metadata.drop_all()
Base.metadata.create_all()
新增數(shù)據(jù)到表news中
a1 = News(price1=1000.0078,price2=1000.0078,title =‘測試數(shù)據(jù)’,is_delete=True,tag1=“PYTHON”,tag2=TagEnum.flask,
create_time1=date(2018,12,12),create_time2=datetime(2019,2,20,12,12,30),create_time3=time(hour=11,minute=12,second=13),
content1=“hello”,content2=“hello hi nihao”)
a1 = News(price1=1000.0078,price2=1000.0078,title=‘測試數(shù)據(jù)’ ,is_delete=False,tag1=“PYTHON”,tag2=TagEnum.python,
create_time1=date(2018,12,12),create_time2=datetime(2019,2,20,12,12,30),create_time3=time(hour=11,minute=12,second=13), content1=“hello”,content2=“hello hi nihao”) session.add(a1) session.commit()
Column常用參數(shù)
primary_key:True設(shè)置某個(gè)字段為主鍵。
autoincrement:True設(shè)置這個(gè)字段為自動(dòng)增長的。
default:設(shè)置某個(gè)字段的默認(rèn)值。在發(fā)表時(shí)間這些字段上面經(jīng) 常用。
nullable:指定某個(gè)字段是否為空。默認(rèn)值是True,就是可以為 空。
unique:指定某個(gè)字段的值是否唯一。默認(rèn)是False。
onupdate:在數(shù)據(jù)更新的時(shí)候會(huì)調(diào)用這個(gè)參數(shù)指定的值或者函 數(shù)。在第一次插入這條數(shù)據(jù)的時(shí)候,不會(huì)用onupdate的值,只 會(huì)使用default的值。常用于是 update_time 字段(每次更新數(shù)據(jù)的 時(shí)候都要更新該字段值)。
name:指定ORM模型中某個(gè)屬性映射到表中的字段名。如果不 指定,那么會(huì)使用這個(gè)屬性的名字來作為字段名。如果指定了, 就會(huì)使用指定的這個(gè)值作為表字段名。這個(gè)參數(shù)也可以當(dāng)作位置 參數(shù),在第1個(gè)參數(shù)來指定。
title = Column(String(50),name=‘title’,nullable=False) title = Column(‘my_title’,String(50),nullable=False)
from datetime import datetime from sqlalchemy import Column,Integer,DateTime,String from db_util import Base,Session class News(Base): tablename = ‘t_news2’ id = Column(Integer,primary_key = True,autoincrement = True) phone = Column(String(11),unique = True) title = Column(String(32),nullable = False) read_count = Column(Integer,default=1) create_time = Column(DateTime,default = datetime.now) update_time = Column(DateTime,default = datetime.now, onupdate =datetime.now ) # 當(dāng)數(shù)據(jù)更新后,參數(shù)的內(nèi)容才會(huì)更改
def create_data(): new1 = News(phone=‘16866666666’,title=‘測試列參數(shù)’) with Session() as session: session.add(new1) session.commit() def create_data2():
new1 = News(phone=‘16866666666’,title=‘測試列參數(shù)’) # 不允許重復(fù)
new1 = News(phone=‘16866666668’) # title不能為空
with Session() as session:
session.add(new1)
session.commit()
with Session() as session: new1 = session.query(News).first() new1.read_count = 2 session.commit() if name == ‘main’:
Base.metadata.create_all()
create_data()
create_data2()
query函數(shù)的使用
模型名。指定查找這個(gè)模型中所有的屬性(對(duì)應(yīng)查詢表為全表查 詢)模型中的屬性??梢灾付ㄖ徊檎夷硞€(gè)模型的其中幾個(gè)屬性聚合函數(shù)
func.count:統(tǒng)計(jì)行的數(shù)量。func.avg:求平均值。func.max:求最大值。func.min:求最小值。func.sum:求和。
提示
func 上,其實(shí)沒有任何聚合函數(shù)。但是因?yàn)樗讓幼隽艘恍?魔術(shù),只要mysql中有的聚合函數(shù),都可以通過func調(diào)用
from random import randint from sqlalchemy import Column,Integer,String,func from db_util import Base,Session class Item(Base): tablename = ‘t_item’ id = Column(Integer,primary_key = True,autoincrement = True) title = Column(String(32)) price = Column(Integer) def create_data(): with Session() as ses: for i in range(10): item = Item(title = f’產(chǎn)品:{i+1}',price=randint(1,100)) ses.add(item) ses.commit() def query_model_name():
獲取所有的字段
with Session() as ses: rs = ses.query(Item).all() for r in rs: print(r.title) def query_model_attr():
獲取指定的字段
with Session() as ses: rs = ses.query(Item.title,Item.price).all() for r in rs: print(r.price) def query_by_func():
統(tǒng)計(jì)指定的列數(shù)據(jù)
with Session() as ses:
rs =ses.query(func.count(Item.id)).first()
rs =ses.query(func.max(Item.price)).first()
rs =ses.query(func.avg(Item.price)).first()
rs =ses.query(func.sum(Item.price)).first() print(rs)
if name ==‘main’:
Base.metadata.create_all()
create_data()
query_model_name()
query_model_attr()
query_by_func()
filter過濾數(shù)據(jù)
過濾是數(shù)據(jù)提取的一個(gè)很重要的功能,以下對(duì)一些常用的過濾條件 進(jìn)行解釋,并且這些過濾條件都是只能通過filter方法實(shí)現(xiàn)的:
equals
news=session.query(News).filter(News.title ==“title1”).first()
not equals
query(User).filter(User.name != ‘ed’)
like & ilike [不區(qū)分大小寫]
query(User).filter(User.name.like(‘%ed%’))
in
query(User).filter(User.name.in_([‘ed’,‘wendy’,‘jack’]))
not in
query(User).filter(~User.name.in_([‘ed’,‘wendy’,‘jack’]))
is null
query(User).filter(User.name==None)
或者是 query(User).filter(User.name.is_(None))
is not null
query(User).filter(User.name != None)
或者是query(User).filter(User.name.isnot(None))
and
query(User).filter(and_(User.name==‘ed’,User.fullname==‘Ed Jones’))
或者是傳遞多個(gè)參數(shù)
query(User).filter(User.name==‘ed’,User.fullname==‘Ed Jones’)
或者是通過多次filter操作
query(User).filter(User.name==‘ed’).filter(User.fullname==‘Ed Jones’)
or
query(User).filter(or_(User.name==‘ed’,User.name==‘wendy’))
如果想要查看orm底層轉(zhuǎn)換的sql語句,可以在filter方法后面不要再 執(zhí)行任何方法直接打印就可以看到了。比如:
news =session.query(News).filter(or_(News.title==‘a(chǎn)bc’,News.content==‘a(chǎn)bc’)) print(news)
from random import randint from uuid import uuid4 from sqlalchemy import Column,Integer,String,Float,Text,and_,or_ from db_util import Base,Session class Article(Base): tablename = ‘t_article’ id =Column(Integer,primary_key=True,autoincrement=True) title =Column(String(50),nullable=False) price = Column(Float,nullable=False) content = Column(Text)
def repr(self): return f"
rs =ses.query(Article).filter_by(id=1).first()
rs =ses.query(Article).filter(Article.id ==1).first() print(rs) def query_data_equal(): with Session() as ses: rs =ses.query(Article).filter(Article.title ==‘title2’).first() print(rs) def query_data_not_equal(): with Session() as ses: rs =ses.query(Article).filter(Article.title !=‘title2’).all() print(rs) def query_data_like(): with Session() as ses:
select * from t_article wheretitle like ‘title%’;
rs =ses.query(Article).filter(Article.title.like(‘title%’)).all() for r in rs: print? def query_data_in(): with Session() as ses: rs=ses.query(Article).filter(Article.title.in_([‘title1’,‘title3’,‘title6’])).all() for r in rs: print? def query_data_not_in(): with Session() as ses: rs=ses.query(Article).filter(~Article.title.in_([‘title1’,‘title3’,‘title6’])).all() for r in rs: print? def query_data_null(): with Session() as ses: rs =ses.query(Article).filter(Article.content== None).all() for r in rs: print? def query_data_not_null(): with Session() as ses: rs =ses.query(Article).filter(Article.content!= None).all() for r in rs: print? def query_data_and(): with Session() as ses:
rs =ses.query(Article).filter(Article.title!=‘title4’ and Article.price>8).all()
rs =ses.query(Article).filter(Article.title!=‘title4’,Article.price >50 ).all()
rs =ses.query(Article).filter(and_(Article.title !=‘title4’,Article.price >50)).all() for r in rs: print? def query_data_or(): with Session() as ses: rs =ses.query(Article).filter(or_(Article.title==‘title4’,Article.price >50) ).all() for r in rs: print? if name == ‘main’:
Base.metadata.create_all()
create_data()
query_data()
query_data_equal()
query_data_not_equal()
query_data_like()
query_data_in()
query_data_not_in()
query_data_null()
query_data_not_null()
query_data_and()
query_data_or()
表關(guān)系
表之間的關(guān)系存在三種:一對(duì)一、一對(duì)多、多對(duì)多。 而SQLAlchemy中的ORM也可以模擬這三種關(guān)系。 因?yàn)橐粚?duì)一其實(shí)在SQLAlchemy中底層是通過一對(duì)多的方式模擬 的,所以先來看下一對(duì)多的關(guān)系:
外鍵: 使用SQLAlchemy創(chuàng)建外鍵非常簡單。在從表中增加一個(gè)字段,指 定這個(gè)字段外鍵的是哪個(gè)表的哪個(gè)字段就可以了。從表中外鍵的字 段,必須和主表的主鍵字段類型保持一致。
class User(Base): tablename = ‘t_user’ id =Column(Integer,primary_key=True,autoincrement=True) uname =Column(String(50),nullable=False,name=‘name’) class News(Base): tablename = ‘t_news’ id =Column(Integer,primary_key=True,autoincrement=True) title =Column(String(50),nullable=False) content = Column(Text,nullable=False) uid =Column(Integer,ForeignKey(‘t_user.id’,)
外鍵約束有以下幾項(xiàng):
RESTRICT:若子表中有父表對(duì)應(yīng)的關(guān)聯(lián)數(shù)據(jù),刪除父表對(duì)應(yīng)數(shù) 據(jù),會(huì)阻止刪除。默認(rèn)項(xiàng)
NO ACTION:在MySQL中,同RESTRICT。
CASCADE:級(jí)聯(lián)刪除。
SET NULL:父表對(duì)應(yīng)數(shù)據(jù)被刪除,子表對(duì)應(yīng)數(shù)據(jù)項(xiàng)會(huì)設(shè)置為 NULL。
from sqlalchemy import Column,Integer,String,Text,ForeignKey from db_util import Base,Session class User(Base): tablename = ‘t_user’ id =Column(Integer,primary_key=True,autoincrement=True) uname =Column(String(50),nullable=False,name=‘name’) class News(Base): tablename = ‘t_news’ id =Column(Integer,primary_key=True,autoincrement=True) title =Column(String(50),nullable=False) content = Column(Text,nullable=False)
uid =Column(Integer,ForeignKey(‘t_user.id’)) # 默認(rèn)不讓刪主表數(shù)據(jù)
uid =Column(Integer,ForeignKey(‘t_user.id’,ondelete = ‘RESTRICT’)) # 默認(rèn)的策略
uid =Column(Integer,ForeignKey(‘t_user.id’,ondelete = ‘NO ACTION’)) # 默認(rèn)的策略
uid =Column(Integer,ForeignKey(‘t_user.id’,ondelete = ‘CASCADE’)) # 級(jí)聯(lián)刪除,發(fā)主表的數(shù)據(jù)被刪除,子表的里數(shù)據(jù)也會(huì)刪除
uid =Column(Integer,ForeignKey(‘t_user.id’,ondelete = ‘SET NULL’)) # 發(fā)現(xiàn)主表數(shù)據(jù)被刪除時(shí),子表的數(shù)據(jù)列會(huì)清空 def create_data(): user = User(uname = ‘zs’) news1 =News(title=‘python’,content=‘flask’,uid = 1) news2 =News(title=‘MySQL’,content=‘SQL’,uid = 1) with Session() as ses: ses.add(user) ses.commit() with Session() as ses: ses.add(news1) ses.add(news2) ses.commit() if name == ‘main’: Base.metadata.create_all() create_data()
ORM關(guān)系之一對(duì)多
mysql級(jí)別的外鍵,還不夠爽,必須拿到一個(gè)表的外鍵,然后通過 這個(gè)外鍵再去另外一張表中查找,這樣太麻煩了。
SQLAlchemy提供了一個(gè) relationship ,這個(gè)類可以定義屬性,以后在訪 問相關(guān)聯(lián)的表的時(shí)候就直接可以通過屬性訪問的方式就可以訪問得 到了。 另外,可以通過 backref 來指定反向訪問的屬性名稱。newss是指有多 篇新聞。他們之間的關(guān)系是一個(gè)“一對(duì)多”的關(guān)系
from sqlalchemy import Column,Integer,String,Text,ForeignKey from sqlalchemy.orm import relationship from db_util import Base,Session class User(Base): tablename = ‘t_user’ id =Column(Integer,primary_key=True,autoincrement=True) uname =Column(String(50),nullable=False,name=‘name’)
news = relationship(‘News’) # 不友好
def repr(self): return f’
1對(duì)多 ForeignKey的關(guān)鍵字要建立在 多一邊
class News(Base): tablename = ‘t_news’ id =Column(Integer,primary_key=True,autoincrement=True) title =Column(String(50),nullable=False) content = Column(Text,nullable=False) uid =Column(Integer,ForeignKey(‘t_user.id’))
user =relationship(‘User’,backref=‘news’) # 將主表的數(shù)據(jù)注入到這個(gè)字段 def repr(self): return f’
news1 = ses.query(News).first()
print(news1)
select u.id u.uname from t_news n left join t_user u n.uid = u.id where n.id =
1; news1 = ses.query(News).first() uid = news1.uid user = ses.query(User).first() print(user) def query_data2():
通地子表查詢主表的數(shù)據(jù)
with Session() as ses: news1 = ses.query(News).first() print(news1.user) def query_data3():
通地主表查找子表的數(shù)據(jù)
with Session() as ses: user1 = ses.query(User).first() print(user1.news) if name == ‘main’:
Base.metadata.create_all()
create_data()
query_data()
query_data2()
query_data3()
ORM關(guān)系之一對(duì)一
在sqlalchemy中,如果想要將兩個(gè)模型映射成一對(duì)一的關(guān)系,那么 應(yīng)該在父模型中,指定引用的時(shí)候,要傳遞一個(gè) uselist=False 這個(gè)參數(shù) 進(jìn)去。 就是告訴父模型,以后引用這個(gè)從模型的時(shí)候,不再是一個(gè)列表 了,而是一個(gè)對(duì)象了
class LoginUser(Base): tablename = ‘t_user_login’ id =Column(Integer,primary_key=True,autoincrement=True) uname =Column(String(32),nullable=False) passwd =Column(String(32),nullable=False)
創(chuàng)建1對(duì)1的關(guān)系, 創(chuàng)建一個(gè)字段來做別一個(gè)表的標(biāo)識(shí)(外鍵)
class User(Base): tablename = ‘t_user’ id =Column(Integer,primary_key=True,autoincrement=True) name =Column(String(32),nullable=False,name=‘name’) gender = Column(String(1)) address = Column(String(64)) login_id =Column(Integer,ForeignKey(‘t_user_login.id’)) login_user =relationship(‘LoginUser’,backref=backref(‘user’,uselist=False))
ORM關(guān)系之多對(duì)多
多對(duì)多的關(guān)系需要通過一張中間表來綁定他們之間的關(guān)系。先把兩個(gè)需要做多對(duì)多的模型定義出來使用Table定義一個(gè)中間表,中間表一般就是包含兩個(gè)模型的外 鍵字段就可以了,并且讓他們兩個(gè)來作為一個(gè)“復(fù)合主鍵”在兩個(gè)需要做多對(duì)多的模型中隨便選擇一個(gè)模型,定義一個(gè) relationship屬性,來綁定三者之間的關(guān)系,在使用relationship 的時(shí)候,需要傳入一個(gè)secondary=中間表對(duì)象名
from sqlalchemy import Column,Integer,String,ForeignKey from sqlalchemy import Table from sqlalchemy.orm import relationship,backref from db_util import Base,Session
創(chuàng)建第3張表,來建立多對(duì)多關(guān)系
放到2個(gè)模型之上
news_tag = Table( ‘t_news_tag’, Base.metadata, Column(‘news_id’,Integer,ForeignKey(‘t_news.id’),primary_key = True), Column(‘tag_id’,Integer,ForeignKey(‘t_tag.id’),primary_key = True),) class News(Base): tablename = ‘t_news’ id =Column(Integer,primary_key=True,autoincrement=True) title =Column(String(32),nullable=False) tags =relationship(‘Tag’,backref=‘newss’,secondary= news_tag) def repr(self): return f’
news =relationship(‘News’,backref=‘tags’,secondary= news_tag)
def repr(self): return f’
if name == ‘main’:
Base.metadata.create_all()
create_data()
query_data()
ORM層面刪除數(shù)據(jù)注意事項(xiàng)
ORM層面刪除數(shù)據(jù),會(huì)無視mysql級(jí)別的外鍵約束。 直接會(huì)將對(duì)應(yīng)的數(shù)據(jù)刪除,然后將從表中的那個(gè)外鍵設(shè)置為NULL, 也就是數(shù)據(jù)庫的 SET NULL 。 如果想要避免這種行為,應(yīng)該將從表中的外鍵的 nullable=False 。
from sqlalchemy import Column, Integer, String, ForeignKey from sqlalchemy.orm import relationship from db_util import Base, Session class User(Base): tablename = ‘t_user’ id = Column(Integer, primary_key=True,autoincrement=True) name = Column(String(32)) class Article(Base): tablename = ‘t_article’ id = Column(Integer, primary_key=True,autoincrement=True) title = Column(String(32)) uid = Column(Integer,ForeignKey(“t_user.id”))
uid = Column(Integer,ForeignKey(“t_user.id”),nullable = False)
user =relationship(‘User’,backref=‘a(chǎn)rticles’) def create_data(): Base.metadata.drop_all() # 刪除已有的表 Base.metadata.create_all() # 創(chuàng)建表
初始化數(shù)據(jù)
user = User(name=‘zs’) art1 = Article(title=‘Python’, uid=1) art2 = Article(title=‘MySQL’, uid=1) user.articles.append(art1) user.articles.append(art2) with Session() as ses: ses.add(user) ses.commit()
def delete_data():
默認(rèn)刪除主表數(shù)據(jù)時(shí),會(huì)將子表的引用主表數(shù)據(jù)的外鍵設(shè)置Null
with Session() as ses: user = ses.query(User).first() ses.delete(user) ses.commit() if name == ‘main’:
create_data()
delete_data()
ORM層面的relationship方法中cascade
在SQLAlchemy,只要將一個(gè)數(shù)據(jù)添加到session中,和他相關(guān)聯(lián)的 數(shù)據(jù)都可以一起存入到數(shù)據(jù)庫中了。 這些是怎么設(shè)置的呢?其實(shí)是通過relationship的時(shí)候,有一個(gè)關(guān)鍵 字參數(shù)cascade可以設(shè)置這些屬性,
cascade屬性值為:
save-update:默認(rèn)選項(xiàng)。在添加一條數(shù)據(jù)的時(shí)候,會(huì)把其他和他 相關(guān)聯(lián)的數(shù)據(jù)都添加到數(shù)據(jù)庫中。這種行為就是save-update屬性 影響的。
delete:表示當(dāng)刪除某一個(gè)模型中的數(shù)據(jù)的時(shí)候,是否也刪掉使用 relationship和他關(guān)聯(lián)的數(shù)據(jù)。 delete-orphan:表示當(dāng)對(duì)一個(gè)ORM對(duì)象解除了父表中的關(guān)聯(lián)對(duì)象 的時(shí)候,自己便會(huì)被刪除掉。當(dāng)然如果父表中的數(shù)據(jù)被刪除,自己 也會(huì)被刪除。這個(gè)選項(xiàng)只能用在一對(duì)多上,并且還需要在子模型中 的relationship中,增加一個(gè)single_parent=True的參數(shù)。
merge:默認(rèn)選項(xiàng)。當(dāng)在使用session.merge,合并一個(gè)對(duì)象的時(shí) 候,會(huì)將使用了relationship相關(guān)聯(lián)的對(duì)象也進(jìn)行merge操作。
expunge:移除操作的時(shí)候,會(huì)將相關(guān)聯(lián)的對(duì)象也進(jìn)行移除。這個(gè) 操作只是從session中移除,并不會(huì)真正的從數(shù)據(jù)庫中刪除。
all:是對(duì)save-update, merge, refresh-expire, expunge, delete 幾種的縮寫。
from sqlalchemy import Column, Integer, String, ForeignKey from sqlalchemy.orm import relationship,backref from db_util import Base, Session class User(Base): tablename = ‘t_user’ id = Column(Integer, primary_key=True,autoincrement=True) name = Column(String(32))
articles =relationship(‘Article’,backref=‘user’,cascade=‘’)
articles =relationship(‘Article’,backref=‘user’,cascade=‘save-update’) # 默認(rèn)cascade的值是saveupdate
articles =relationship(‘Article’,backref=‘user’,cascade=‘save-update,delete’) #delete可以幫助刪除關(guān)聯(lián)表的數(shù)據(jù)
articles =relationship(‘Article’,backref=‘user’,cascade=‘save-update,delete,deleteorphan’,single_parent=True) # 當(dāng)關(guān)聯(lián)關(guān)系被解除時(shí),子表數(shù)據(jù)會(huì)被清空
class Article(Base): tablename = ‘t_article’ id = Column(Integer, primary_key=True,autoincrement=True) title = Column(String(32)) uid = Column(Integer,ForeignKey(“t_user.id”))
user =relationship(‘User’,backref=‘a(chǎn)rticles’,cascade=‘save-update,delete’) # 會(huì)把主表的數(shù)據(jù)刪除
user =relationship(‘User’,backref=backref(‘a(chǎn)rticles’,cascade=‘saveupdate,delete,deleteorphan’)) def create_data(): Base.metadata.drop_all() # 刪除已有的表 Base.metadata.create_all() # 創(chuàng)建表
初始化數(shù)據(jù)
user = User(name=‘SXT’) art1 = Article(title=‘Python’, uid=1) art2 = Article(title=‘MySQL’, uid=1) user.articles.append(art1) user.articles.append(art2)
保存數(shù)據(jù)
with Session() as ses: ses.add(user) ses.commit()
def delete_data(): with Session() as ses: user = ses.query(User).first() ses.delete(user) ses.commit() def delete_art(): with Session() as ses: art = ses.query(Article).first() ses.delete(art) ses.commit() def update_data(): with Session() as ses: user = ses.query(User).first() user.articles = [] ses.commit() if name == ‘main’:
create_data()
delete_data()
update_data()
delete_art()
排序
order_by方法排序:可以指定根據(jù)模型中某個(gè)屬性進(jìn)行排序,"模型 名.屬性名.desc()"代表的是降序排序。
relationship的方法中order_by屬性:在指定relationship方法的時(shí) 候,添加order_by屬性來指定排序的字段。
方式1:order_by方法指定
升序
users =ses.query(User).order_by(User.age).all()
降序
users =ses.query(User).order_by(User.age.desc()).all()
方式2:涉及兩表時(shí),定義模型時(shí),用relationship方法中的 order_by屬性指定排序方式
from random import randint from sqlalchemy import Column,Integer,String,ForeignKey from sqlalchemy.orm import relationship,backref from db_util import Base,Session class User(Base): tablename = ‘t_user’ id = Column(Integer,primary_key=True,autoincrement=True) name = Column(String(32)) age = Column(Integer) def repr(self): return f’
for i in range(10): news = News(title = f’新聞{i}',content = ‘新聞’,read_count =randint(1,1000)) user.newss.append(news) ses.commit() def query_user(): with Session() as ses: users = ses.query(User).all() for i in users[-1].newss: print(i) if name == ‘main’:
Base.metadata.drop_all()
Base.metadata.create_all()
create_user()
query_user()
注意 __mapper_args__ 參數(shù)的1.1版本已被拋棄
limit、offset、slice使用
limit:可以限制查詢的時(shí)候只查詢前幾條數(shù)據(jù)。 屬top-N查詢offset:可以限制查找數(shù)據(jù)的時(shí)候過濾掉前面多少條??芍付ㄩ_ 始查詢時(shí)的偏移量。切片:可以對(duì)Query對(duì)象使用切片操作,來獲取想要的數(shù)據(jù)。
可以使用 slice(start,stop) 方法來做切片操作。也可以使用 [start:stop] 的方式來進(jìn)行切片操作。一般在實(shí)際開發(fā)中,中括號(hào)的形式是用得比較多的。
from random import randint from sqlalchemy import Column,Integer,String from db_util import Base,Session class News(Base): tablename = ‘t_news’ id =Column(Integer,primary_key=True,autoincrement=True) title =Column(String(32),nullable=False) content =Column(String(32),nullable=False) read_count = Column(Integer) def repr(self): return f’
limit topN數(shù)據(jù)
offset 跳過n數(shù)據(jù)
分頁效果 1-3 4-6 7-9
3 0 1 (pagenum-1)*pagesize
3 3 2 (2-1)*3 = 3
3 6 3 (3-1)*3 = 6
3 9 4 (4-1)*3 = 6
with Session() as ses:
(pagenum-1)*pagesize
newss = ses.query(News).limit(3).offset(3).all() for n in newss: print(n) def query_by_slice(): with Session() as ses:
從哪個(gè)索引開始,到哪個(gè)索引結(jié)束
newss = ses.query(News).slice(3,6).all() for n in newss: print(n) def query_by_qiepian(): with Session() as ses:
從哪個(gè)索引開始,到哪個(gè)索引結(jié)束
newss = ses.query(News).all()[3:6] for n in newss: print(n) if name == ‘main’:
create_data()
query_by_limit()
query_by_offset()
query_by_page()
query_by_slice()
query_by_qiepian()
懶加載
在一對(duì)多,或者多對(duì)多關(guān)系的時(shí)候,如果想要獲取多的一方這一部 分的數(shù)據(jù)的時(shí)候,往往能通過一個(gè)屬性就可以全部獲取了。 如有一個(gè)作者,想要這個(gè)作者的所有文章,通過user.articles就可 以獲取所有的 但有時(shí)候我們不想獲取所有的數(shù)據(jù),如只想獲取這個(gè)作者今天發(fā)表 的文章,那么這時(shí)候我們可以給relationship方法添加屬性 lazy=‘dynamic’ ,以后通過 user.articles 獲取到的就不是一個(gè)列表,而是一個(gè) AppenderQuery對(duì)象了。這樣就可以對(duì)這個(gè)對(duì)象再進(jìn)行一層過濾和 排序等操作 通過 lazy=‘dynamic’ ,獲取出來的多的那一部分的數(shù)據(jù),就是一個(gè) AppenderQuery 對(duì)象了。這種對(duì)象既可以添加新數(shù)據(jù),也可以跟 Query 一 樣,可以再進(jìn)行一層過濾.
lazy可用的選項(xiàng):
1 select : (默認(rèn)) 后臺(tái)會(huì)用select語句一次性加載所有數(shù)據(jù),即訪問 到屬性的時(shí)候,就會(huì)全部加載該屬性的數(shù)據(jù)
2 joined - 數(shù)據(jù)會(huì)被JOIN語句加載,即對(duì)關(guān)聯(lián)的兩個(gè)表進(jìn)行join操 作,從而獲取到所有相關(guān)的對(duì)象 3 subquery - 數(shù)據(jù)被用subquery子查詢SQL語句加載
4 dynamic :這個(gè)也是懶加載。在訪問屬性的時(shí)候,并不在內(nèi)存中加 載數(shù)據(jù),而是返回一個(gè) AppenderQuery 對(duì)象, 需要執(zhí)行相應(yīng)方法才可 以獲取對(duì)象。適用于數(shù)據(jù)量大的時(shí)候
注意
lazy=“dynamic” 只可以用在一對(duì)多和多對(duì)對(duì)關(guān)系中,不可以用在一 對(duì)一和多對(duì)一中。 這樣也合理:如果返回結(jié)果很少的話,就沒必要延遲加載數(shù)據(jù)了。
from random import randint from sqlalchemy import Column,Integer,String,ForeignKey from sqlalchemy.orm import relationship,backref from db_util import Base,Session class User(Base): tablename = ‘t_user’ id = Column(Integer,primary_key=True,autoincrement=True) name = Column(String(32)) age = Column(Integer) def repr(self): return f’
‘lazy = select 默認(rèn)不能2次過濾’
with Session() as ses: users = ses.query(User).all() newss =users[-1].newss.filter(News.read_count >500).all() print(newss)
if name == ‘main’:
Base.metadata.drop_all()
Base.metadata.create_all()
create_data()
query_data2()
query_data3()
分組group_by和過濾分組having
group_by
根據(jù)某個(gè)字段進(jìn)行分組。如想要根據(jù)年齡進(jìn)行分組,來統(tǒng)計(jì)每個(gè)分 組分別有多少人
r =session.query(User.age,func.count(User.id)).group_by(User.age).all()
having having
是對(duì)分組查找結(jié)果作進(jìn)一步過濾。如只想要看未成年人的人 數(shù), 那么可以首先對(duì)年齡進(jìn)行分組統(tǒng)計(jì)人數(shù),然后再對(duì)分組進(jìn)行having 過濾。
r =session.query(User.age,func.count(User.id)).group_by(User.age).having(User.age < 18).all()
from random import randint from sqlalchemy import Column,Integer,String,ForeignKey,func from sqlalchemy.orm import relationship,backref from db_util import Base,Session class User(Base): tablename = ‘t_user’ id = Column(Integer,primary_key=True,autoincrement=True) name = Column(String(32)) age = Column(Integer) def repr(self): return f’
統(tǒng)計(jì) 每個(gè)年齡的人數(shù)
with Session() as ses: user =ses.query(User.age,func.count(User.id)).group_by(User.age).all() print(user) def query_by_age_gt_18(): #統(tǒng)計(jì) 每個(gè)年齡的人數(shù),要求排除未成年人 with Session() as ses: user =ses.query(User.age,func.count(User.id)).group_by(User.age).having(User.age>18).all() print(user) def query_by_age_lt_18():
統(tǒng)計(jì) 每個(gè)年齡的人數(shù),要求未成年人
with Session() as ses: user =ses.query(User.age,func.count(User.id)).group_by(User.age).having(User.age<18).all() print(user) if name ==‘main’:
create_data()
query_data()
query_by_age_gt_18()
query_by_age_lt_18()
Flask-SQLAlchemy的使用
深知大多數(shù)程序員,想要提升技能,往往是自己摸索成長,但自己不成體系的自學(xué)效果低效又漫長,而且極易碰到天花板技術(shù)停滯不前!
既有適合小白學(xué)習(xí)的零基礎(chǔ)資料,也有適合3年以上經(jīng)驗(yàn)的小伙伴深入學(xué)習(xí)提升的進(jìn)階課程,涵蓋了95%以上鴻蒙開發(fā)知識(shí)點(diǎn),真正體系化!
由于文件比較多,這里只是將部分目錄截圖出來,全套包含大廠面經(jīng)、學(xué)習(xí)筆記、源碼講義、實(shí)戰(zhàn)項(xiàng)目、大綱路線、講解視頻,并且后續(xù)會(huì)持續(xù)更新
需要這份系統(tǒng)化的資料的朋友,可以戳這里獲取
elf.id} name={self.name} age={self.age}>’ class News(Base): tablename = ‘t_news’ id =Column(Integer,primary_key=True,autoincrement=True) title =Column(String(32),nullable=False) content =Column(String(32),nullable=False) read_count = Column(Integer) uid =Column(Integer,ForeignKey(‘t_user.id’)) user =relationship(‘User’,backref=backref(‘newss’,lazy=‘dynamic’)) def repr(self): return f’
‘lazy = select 默認(rèn)不能2次過濾’
with Session() as ses: users = ses.query(User).all() newss =users[-1].newss.filter(News.read_count >500).all() print(newss)
if name == ‘main’:
Base.metadata.drop_all()
Base.metadata.create_all()
create_data()
query_data2()
query_data3()
分組group_by和過濾分組having
group_by
根據(jù)某個(gè)字段進(jìn)行分組。如想要根據(jù)年齡進(jìn)行分組,來統(tǒng)計(jì)每個(gè)分 組分別有多少人
r =session.query(User.age,func.count(User.id)).group_by(User.age).all()
having having
是對(duì)分組查找結(jié)果作進(jìn)一步過濾。如只想要看未成年人的人 數(shù), 那么可以首先對(duì)年齡進(jìn)行分組統(tǒng)計(jì)人數(shù),然后再對(duì)分組進(jìn)行having 過濾。
r =session.query(User.age,func.count(User.id)).group_by(User.age).having(User.age < 18).all()
from random import randint from sqlalchemy import Column,Integer,String,ForeignKey,func from sqlalchemy.orm import relationship,backref from db_util import Base,Session class User(Base): tablename = ‘t_user’ id = Column(Integer,primary_key=True,autoincrement=True) name = Column(String(32)) age = Column(Integer) def repr(self): return f’
統(tǒng)計(jì) 每個(gè)年齡的人數(shù)
with Session() as ses: user =ses.query(User.age,func.count(User.id)).group_by(User.age).all() print(user) def query_by_age_gt_18(): #統(tǒng)計(jì) 每個(gè)年齡的人數(shù),要求排除未成年人 with Session() as ses: user =ses.query(User.age,func.count(User.id)).group_by(User.age).having(User.age>18).all() print(user) def query_by_age_lt_18():
統(tǒng)計(jì) 每個(gè)年齡的人數(shù),要求未成年人
with Session() as ses: user =ses.query(User.age,func.count(User.id)).group_by(User.age).having(User.age<18).all() print(user) if name ==‘main’:
create_data()
query_data()
query_by_age_gt_18()
query_by_age_lt_18()
Flask-SQLAlchemy的使用
深知大多數(shù)程序員,想要提升技能,往往是自己摸索成長,但自己不成體系的自學(xué)效果低效又漫長,而且極易碰到天花板技術(shù)停滯不前!
[外鏈圖片轉(zhuǎn)存中…(img-D6RYJbso-1715809528300)] [外鏈圖片轉(zhuǎn)存中…(img-xmwlQxgG-1715809528300)]
既有適合小白學(xué)習(xí)的零基礎(chǔ)資料,也有適合3年以上經(jīng)驗(yàn)的小伙伴深入學(xué)習(xí)提升的進(jìn)階課程,涵蓋了95%以上鴻蒙開發(fā)知識(shí)點(diǎn),真正體系化!
由于文件比較多,這里只是將部分目錄截圖出來,全套包含大廠面經(jīng)、學(xué)習(xí)筆記、源碼講義、實(shí)戰(zhàn)項(xiàng)目、大綱路線、講解視頻,并且后續(xù)會(huì)持續(xù)更新
需要這份系統(tǒng)化的資料的朋友,可以戳這里獲取
柚子快報(bào)激活碼778899分享:鴻蒙 面試 學(xué)習(xí) 上傳文件
文章來源
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場。
轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。