第三節:
一、threading.local,Flask自帶的
1、作用:爲每個線程開闢一塊空間(空間與空間的數據之間是隔離的)進行數據存儲。例子如下:
前言:使用global的方式
from threading import Thread
import time
sudada = -1
def foo(arg):
global sudada # 設置爲全局變量
sudada = arg
time.sleep(2)
print(sudada)
for i in range(10):
t=Thread(target=foo,args=(i,)) # 線程傳參
t.start()
9
9
9
9
9
9
9
9
9
9
方式二:使用threading.local的方式
from threading import local
from threading import Thread
import time
sudada=local() # 獲取一個線程的唯一標識
def foo(arg):
sudada.value=arg
time.sleep(2)
print(sudada.value)
for i in range(10):
t=Thread(target=foo,args=(i,)) # 線程傳參
t.start() # 開啓線程
# 執行結果:
2
8
4
5
3
6
7
9
0
1
2、自定義local對象 -- 基於函數
作用:爲每個線程開闢一塊空間(空間與空間的數據之間是隔離的)進行數據存儲。例子如下:
from threading import Thread
from threading import get_ident
import time
# 定義一個空的字典,字典的最終效果爲:storage={get_ident():{"val":value}}
storage={}
# 設置字典的值
def set(k,v):
ident=get_ident()
if ident in storage:
storage[ident][k] = v
else:
storage[ident] = {k:v}
# 獲取字典的值
def get(k):
ident = get_ident()
return storage[ident][k]
# 調用函數,調用set和get函數給字典傳值或者獲取字典的值
def task(arg):
set("val",arg)
print(storage)
time.sleep(2)
v=get("val")
print(v)
# 開啓線程
for i in range(10):
t = Thread(target=task,args=(i,))
t.start()
# 返回結果
{123145497202688: {'val': 0}}
{123145497202688: {'val': 0}, 123145502457856: {'val': 1}}
{123145497202688: {'val': 0}, 123145502457856: {'val': 1}, 123145507713024: {'val': 2}}
{123145497202688: {'val': 0}, 123145502457856: {'val': 1}, 123145507713024: {'val': 2}, 123145512968192: {'val': 3}}
{123145497202688: {'val': 0}, 123145502457856: {'val': 1}, 123145507713024: {'val': 2}, 123145512968192: {'val': 3}, 123145518223360: {'val': 4}}
{123145497202688: {'val': 0}, 123145502457856: {'val': 1}, 123145507713024: {'val': 2}, 123145512968192: {'val': 3}, 123145518223360: {'val': 4}, 123145523478528: {'val': 5}}
{123145497202688: {'val': 0}, 123145502457856: {'val': 1}, 123145507713024: {'val': 2}, 123145512968192: {'val': 3}, 123145518223360: {'val': 4}, 123145523478528: {'val': 5}, 123145528733696: {'val': 6}}
{123145497202688: {'val': 0}, 123145502457856: {'val': 1}, 123145507713024: {'val': 2}, 123145512968192: {'val': 3}, 123145518223360: {'val': 4}, 123145523478528: {'val': 5}, 123145528733696: {'val': 6}, 123145533988864: {'val': 7}}
{123145497202688: {'val': 0}, 123145502457856: {'val': 1}, 123145507713024: {'val': 2}, 123145512968192: {'val': 3}, 123145518223360: {'val': 4}, 123145523478528: {'val': 5}, 123145528733696: {'val': 6}, 123145533988864: {'val': 7}, 123145539244032: {'val': 8}}
{123145497202688: {'val': 0}, 123145502457856: {'val': 1}, 123145507713024: {'val': 2}, 123145512968192: {'val': 3}, 123145518223360: {'val': 4}, 123145523478528: {'val': 5}, 123145528733696: {'val': 6}, 123145533988864: {'val': 7}, 123145539244032: {'val': 8}, 123145544499200: {'val': 9}}
0
1
2
3
4
5
6
7
8
9
3、自定義local對象 -- 基於面向對象
作用:爲每個線程(協程)開闢一塊空間(空間與空間的數據之間是隔離的)進行數據存儲。例子如下:
首先使用協程,如果協程不存在則使用線程:
try:
from greenlet import getcurrent as get_ident # getcurrent表示獲取當前協程的唯一標識
except Exception as e:
from threading import get_ident
from threading import Thread
class Local(object):
def __init__(self):
# 這裏不能直接寫self.storage={},因爲實例化對象的時候,self.storage會調用Local類裏面的__setattr__方法,
# 由於Local類裏面的__setattr__方法未定義值,存在相互引用,導致報錯
object.__setattr__(self,"storage",{})
def __setattr__(self, k, v):
ident = get_ident() # 獲取到當前線程的唯一標記
if ident in self.storage:
self.storage[ident][k] = v
else:
self.storage[ident] = {k:v}
def __getattr__(self, k):
ident = get_ident() # 獲取到當前線程的唯一標記
return self.storage[ident][k]
obj=Local()
def task(arg):
obj.val=arg # 這個調用的是類裏面的"__setattr__"
print(obj.val) # 這個調用的是類裏面的"__getattr__"
for i in range(10):
t = Thread(target=task,args=(i,))
t.start()
# 獲取到的值
0
1
2
3
4
5
6
7
8
9
二、*****(5星) Flask上下文管理
Local的作用?
用於保存:請求上下文對象,app上下文對象
並且可以做到"線程"間的數據隔離
線程:threading.local
協程:greenlet.get_current as get_ident
LocalStack的作用?
將Local中保存的數據維護成一個棧
2.1、上下文管理之本質分析
第一階段:將ctx(request,session)放到"空調"上(local對象)。詳見 2.2
第二階段:視圖函數導入:request/session。詳見 2.3
第三階段:請求處理完畢:1.獲取session,保存到cookie。2.將cts刪除。詳見 2.4
2.2、上下文管理之請求到來處理階段(第一階段)
1.當請求進來的時候,會將request和session封裝成一個RequestContext對象,
2.之後會把這個RequestContext對象,通過localstack放入到Local對象中。
3.由於最初的local對象裏面的ctx的值(session)爲空,那麼執行open_session,將cookie裏面的值拿出來重新賦值到local對象裏面的ctx值(session)。
4.最後返回的時候,執行save_session,將local對象裏面的ctx值(session)讀出來,序列化到cookie給用戶,然後執行pop把ctx給移除掉。
2.3、上下文管理之視圖調用階段(第二階段)
認識partial方法的使用:
import functools
def func(a1,a2,a3):
return a1 + a2 +a3
new_func=functools.partial(func,1,2)
# func函數原本需要傳遞3個參數才能正常運行,使用partial先傳入2個參數,那麼在使用func函數的時候只需要再次傳遞一個值,就能獲取到func函數return返回的值了。
print(new_func(3))
6
2.4、上下文管理之視圖調用階段和結束階段(第三階段)
2.5、Flask中的g是什麼?
1.是一個單次請求的"全局變量",執行完畢就會被銷燬。
import flask
from flask import Flask, render_template, redirect, request, url_for, g
app=Flask(__name__)
@app.route("/n1")
def index():
g.x=123 # 在g裏面設置一個值"x" = 123
print(g.x) # 在當前函數內獲取這個值
return "n1"
@app.route("/n2")
def order():
print(g.x) # 在order則獲取不到g對應的"x"的值
return "n2"
if __name__ == '__main__':
app.run()
# 運行後返回值
# 請求:http://127.0.0.1:5000/n2 無返回值(獲取不到g.x對應的值)
# 請求:http://127.0.0.1:5000/n1 有返回值(獲取到g.x對應的值:123)
2.通過before_request,在請求週期之前做一次g.x的賦值,那麼後續的函數內鬥可以取到g.x的值。
import flask
from flask import Flask, render_template, redirect, request, url_for, g
app=Flask(__name__)
@app.before_request
def foo():
g.x = 456
@app.route("/n1")
def index():
print(g.x) # 在當前函數內獲取g對應的"x"的值
return "n1"
@app.route("/n2")
def order():
print(g.x) # 在order獲取g對應的"x"的值
return "n2"
if __name__ == '__main__':
app.run()
第四節
三、flask-session組件(第三方組件)
3.1、session放在cookie裏面(默認使用session["k1"] = 123的方式)
from flask import Flask,session
app=Flask(__name__)
app.debug=True
app.secret_key="isdnfj"
@app.route("/login")
def login():
session["k1"] = 123
return "login"
@app.route("/index",methods=["GET","POST"])
def index():
print(session["k1"])
return "index"
if __name__ == '__main__':
app.run()
3.2、將session從cookie裏面轉移到redis裏面進行存儲
方式一:直接使用 app.session_interface=RedisSessionInterface 的方式
from flask import Flask,session
from flask_session import RedisSessionInterface
app=Flask(__name__)
app.debug=True
app.secret_key="isdnfj"
# 添加的配置參數如下,如果不加以下參數,默認把session放在cookie裏面
from redis import Redis
app.session_interface=RedisSessionInterface(
redis=Redis(host="127.0.0.1",port=6379), # redis服務器信息
key_prefix='szq_flask', # 給隨機字符串加一個前綴
)
@app.route("/login")
def login():
session["k1"] = 123
return "login"
@app.route("/index",methods=["GET","POST"])
def index():
print(session["k1"])
return "index"
if __name__ == '__main__':
app.run()
方式二:通過Session類傳入app對象,然後通過config['SESSION_TYPE']判斷使用哪種方式來存儲session
from flask import Flask, session
from redis import Redis
from flask_session import Session
app = Flask(__name__)
app.debug = True
app.secret_key = "isdnfj"
app.config["SESSION_TYPE"] = "redis" # 標記類型爲redis
app.config["SESSION_REDIS"] = Redis(host="127.0.0.1", port=6379) # 設置redis連接地址
Session(app)
@app.route("/login")
def login():
session["k1"] = 123
return "login"
@app.route("/index", methods=["GET", "POST"])
def index():
print(session["k1"])
return "index"
if __name__ == '__main__':
app.run()
3.3、flask支持將session放在多種緩存服務下存儲,如:redis,memcached,filesystem,mongodb,sqlalchemy
可以通過導入 from flask_session import Session (詳見3.2方式二),查看Session的源碼。
3.4、例子:將session放入到redis中具體實現 配置文件地址:https://www.cnblogs.com/wupeiqi/articles/7552008.html
flask-session組件使用和原理:
1、作用:
將默認保存的簽名cookie中的值,保存到redis,memcached,filesystem,mongodb,sqlalchemy裏面去。
2、應用
2.1、配置:
app.config["SESSION_TYPE"]="redis"
app.config["SESSION_REDIS"]=Redis(host="127.0.0.1",port=6379)
2.2、替換:
from flask_session import Session
Session(app)
注意:session中存儲的是字典,修改字典內部元素時,會造成數據不更新,
需要通過加入以下配置參數來修改對應的字典值:
motified = True
SESSION_REFRESH_EACH_REQUEST = True 和 session.permanent = True (redis默認)
一、manage.py
from 第四節.flask_session_study import create_app
app = create_app()
if __name__ == '__main__':
app.run()
二、settings.py
from datetime import timedelta
from redis import Redis
class Config(object):
DEBUG = True
SECRET_KEY = "asdkqwdm"
PERMANENT_SESSION_LIFETIME = timedelta(minutes=30)
# 設置session的超時時間,session.permanent默認爲True
SESSION_REFRESH_EACH_REQUEST = True
"""
設置session修改後立即生效,例原始session的值爲 {"id":123, "user":sudada}
get方式修改:session["user_info"]["id]=aaa,修改後:{"id":"aaa", "user":sudada}
set方式修改: session["user_info"] = {"id":123, "user":sss} ,修改後:{"id":123, "user":sss}
"""
SESSION_TYPE = 'redis' # 將session存入redis,步驟1
class ProductionConfig(Config):
SESSION_REDIS = Redis(host='192.168.1.1', port='6379')
class DevelopmentConfig(Config):
SESSION_REDIS = Redis(host='127.0.0.1',port='6379') # 將session存入redis,步驟2
三、__init__.py
from flask import Flask
from flask_session import Session # 將session存入redis,步驟3
from .views import account
from .views import home
def create_app():
app = Flask(__name__)
app.config.from_object("settings.DevelopmentConfig")
app.register_blueprint(account.ac)
app.register_blueprint(home.ho)
# 將session存入redis,步驟4
Session(app)
return app
四、account.py
from flask import Blueprint,redirect,request,render_template,session
from uuid import uuid4
ac=Blueprint("ac",__name__)
uid=str(uuid4())
@ac.route("/login",methods=["GET","POST"])
def login():
if request.method == "GET":
return render_template("login.html")
user = request.form.get("user")
pwd = request.form.get("pwd")
if user == "szq" and pwd == "123":
session.permanent=True # 修改session字典內的值:session["user_info"]['name']="ssss"
session["user_info"] = {"id":uid,"name":user}
return redirect("/index")
else:
return render_template("login.html",msg="用戶名密碼錯誤")
五、home.py
from flask import Blueprint,session,redirect,request,render_template
from uuid import uuid4
ho=Blueprint("ho",__name__)
uid=str(uuid4())
@ho.route("/index",methods=["GET","POST"])
def home():
user_info = session.get("user_info")
print(user_info)
# session["user_info"]={"k1":111,"k2":222}
# session的值可以被修改:需要添加 SESSION_REFRESH_EACH_REQUEST = True 即可(在配置文件裏面添加)
# 修改後請求"/test"獲取到的值爲:{"k1":111,"k2":222}
# session["user_info"]['name']="ssss"
# session的值可以被修改:需要添加 session.permanent=True 即可(在session創建上一行)
# 修改後請求"/test"獲取到的值爲:{'id': '05af7893-514b-4d94-9562-729db160505b', 'name': 'ssss'}
return "index"
@ho.route("/test",methods=["GET","POST"])
def test():
user_info = session.get("user_info")
print(user_info)
return "test"
四、基於pymysql實現用戶登錄
4.1、代碼簡單實現連接DB及執行SQL
import pymysql
class SQLHelper(object):
# 連接MySQL
@staticmethod
def open():
conn=pymysql.connect(host="127.0.0.1",port=3306,user="root",passwd="123456",db="db_test")
cursor=conn.cursor(cursor=pymysql.cursors.DictCursor) # 拿到的值爲,字典格式
return conn,cursor
# 關閉MySQL
@staticmethod
def close(conn,cursor):
conn.commit()
cursor.close()
conn.close()
# 執行fetchone
@classmethod
def fetch_one(cls,sql,args):
conn, cursor = cls.open()
cursor.execute(sql,args)
obj=cursor.fetchone()
cls.close(conn, cursor)
return obj
# 執行fetch_all
@classmethod
def fetch_all(cls,sql,args):
conn, cursor = cls.open()
cursor.execute(sql,args)
obj=cursor.fetch_all()
cls.close(conn, cursor)
return obj
4.2、DBUtils(公共組件) -- DBUtils是Python的一個用於實現數據庫連接池的模塊。安裝:pip3 install DBUtils
模式:1、每個線程創建一個鏈接,關閉(不會真正的關閉數據庫連接),線程終止時,才關閉連接。2、創建共享連接池
應用:只要寫原生SQL,就得用數據庫連接池。
4.2.1、例子1:數據庫連接池腳本單項測試
import time
import pymysql
import threading
from DBUtils.PooledDB import PooledDB, SharedDBConnection
POOL = PooledDB(
creator=pymysql, # 使用鏈接數據庫的模塊
maxconnections=6, # 連接池允許的最大連接數,0和None表示不限制連接數
mincached=2, # 初始化時,鏈接池中至少創建的空閒的鏈接,0表示不創建
maxcached=5, # 鏈接池中最多閒置的鏈接,0和None不限制
maxshared=3, # 鏈接池中最多共享的鏈接數量,0和None表示全部共享。PS: 無用,因爲pymysql和MySQLdb等模塊的 threadsafety都爲1,所有值無論設置爲多少,_maxcached永遠爲0,所以永遠是所有鏈接都共享。
blocking=True, # 連接池中如果沒有可用連接後,是否阻塞等待。True,等待;False,不等待然後報錯
maxusage=None, # 一個鏈接最多被重複使用的次數,None表示無限制
setsession=[], # 開始會話前執行的命令列表。如:["set datestyle to ...", "set time zone ..."]
ping=0, # ping MySQL服務端,檢查是否服務可用。# 如:0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always
host='127.0.0.1',
port=3306,
user='root',
password='123456',
database='orm',
charset='utf8'
)
def func():
# 檢測當前正在運行連接數的是否小於最大鏈接數,如果不小於則:等待或報raise TooManyConnections異常
# 否則
# 則優先去初始化時創建的鏈接中獲取鏈接 SteadyDBConnection。
# 然後將SteadyDBConnection對象封裝到PooledDedicatedDBConnection中並返回。
# 如果最開始創建的鏈接沒有鏈接,則去創建一個SteadyDBConnection對象,再封裝到PooledDedicatedDBConnection中並返回。
# 一旦關閉鏈接後,連接就返回到連接池讓後續線程繼續使用。
conn = POOL.connection()
# print(th, '鏈接被拿走了', conn1._con)
# print(th, '池子裏目前有', pool._idle_cache, '\r\n')
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) # 獲取字典格式的數據
cursor.execute('select * from users')
result = cursor.fetchall()
conn.close() # 把DB連接放回連接池,並不是真正的關閉掉了連接。
return result
res=func()
print(res)
# [{'course_id': 1, 'course_name': 'Network', 'course_grade': 3.0, 'course_info': 'Computer Network'}]
4.2.2、例子2:數據庫連接池結合調用數據庫方法一起使用
一、pool.py # 定義數據庫連接池文件
import pymysql
from DBUtils.PooledDB import PooledDB
from flask import current_app
# 數據庫連接池
POOL = PooledDB(
creator=pymysql, # 使用鏈接數據庫的模塊
maxconnections=6, # 連接池允許的最大連接數,0和None表示不限制連接數
mincached=2, # 初始化時,鏈接池中至少創建的空閒的鏈接,0表示不創建
maxcached=5, # 鏈接池中最多閒置的鏈接,0和None不限制
maxshared=3, # 鏈接池中最多共享的鏈接數量,0和None表示全部共享。PS: 無用,因爲pymysql和MySQLdb等模塊的 threadsafety都爲1,所有值無論設置爲多少,_maxcached永遠爲0,所以永遠是所有鏈接都共享。
blocking=True, # 連接池中如果沒有可用連接後,是否阻塞等待。True,等待;False,不等待然後報錯
maxusage=None, # 一個鏈接最多被重複使用的次數,None表示無限制
setsession=[], # 開始會話前執行的命令列表。如:["set datestyle to ...", "set time zone ..."]
ping=0, # ping MySQL服務端,檢查是否服務可用。# 如:0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always
host=current_app.config["PYMYSQL_HOST"],
port=3306,
user='root',
password='123456',
database='orm',
charset='utf8'
)
二、sql.py # 連接數據庫,執行SQL
import pymysql
# 數據庫執行方法
class SQLHelper(object):
# 連接MySQL
@staticmethod
def open():
from .pool import POOL
conn = POOL.connection()
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
return conn,cursor
# 關閉MySQL
@staticmethod
def close(conn,cursor):
conn.commit()
cursor.close()
conn.close()
# 執行fetchone
@classmethod
def fetch_one(cls,sql,args):
conn, cursor = cls.open()
cursor.execute(sql,args)
obj=cursor.fetchone()
cls.close(conn, cursor)
return obj
# 執行fetch_all
@classmethod
def fetch_all(cls,sql,args):
conn, cursor = cls.open()
cursor.execute(sql,args)
obj=cursor.fetch_all()
cls.close(conn, cursor)
return obj
三、account.py # 線上登錄頁面,用戶驗證走數據庫
from flask import Blueprint,redirect,request,render_template,session,current_app
from uuid import uuid4
from ..utils.sql import SQLHelper
ac=Blueprint("ac",__name__)
uid=str(uuid4())
@ac.route("/login",methods=["GET","POST"])
def login():
print(current_app.config["PYMYSQL_HOST"]) # 獲取配置文件setting.py內的參數配置"PYMYSQL_HOST"
if request.method == "GET":
return render_template("login.html")
user = request.form.get("user")
pwd = request.form.get("pwd")
# 連接數據庫
obj = SQLHelper.fetch_one("select id,name from users where name = %s and pwd = %s",[user,pwd])
if obj:
session.permanent=True # 修改session字典內的值:session["user_info"]['name']="ssss"
session["user_info"] = {"id":uid,"name":user}
return redirect("/index")
else:
return render_template("login.html",msg="用戶名密碼錯誤")
四、settings.py # 數據庫連接池,部分參數走配置文件
from datetime import timedelta
from redis import Redis
class Config(object):
DEBUG = True
SECRET_KEY = "asdkqwdm"
PERMANENT_SESSION_LIFETIME = timedelta(minutes=30)
# 設置session的超時時間,session.permanent默認爲True
SESSION_REFRESH_EACH_REQUEST = True
"""
設置session修改後立即生效,例原始session的值爲 {"id":123, "user":sudada}
get方式修改:session["user_info"]["id]=aaa,修改後:{"id":"aaa", "user":sudada}
set方式修改: session["user_info"] = {"id":123, "user":sss} ,修改後:{"id":123, "user":sss}
"""
SESSION_TYPE = 'redis' # 將session存入redis,步驟1
PYMYSQL_HOST = "127.0.0.1"
class ProductionConfig(Config):
SESSION_REDIS = Redis(host='192.168.1.1', port='6379')
class DevelopmentConfig(Config):
SESSION_REDIS = Redis(host='127.0.0.1',port='6379') # 將session存入redis,步驟2
五、wtforms(公共組件) -- 表單驗證+生成HTML標籤,安裝:pip3 install wtforms
簡介:WTForms是一個支持多個web框架的form組件,主要用於對用戶請求數據進行驗證。
地址:https://www.cnblogs.com/wupeiqi/articles/8202357.html 詳見查看用戶登錄和用戶註冊功能。
第五節
六、SQLAlchemy -- 基於Python實現的ORM框架 pip3 install sqlalchemy
學習地址:https://www.cnblogs.com/wupeiqi/articles/5713330.html
框架概念
db first:根據數據庫的表生成類(類代碼)
在django: python manage.py inspectdb
code first:根據類(類代碼)在DB裏面創建數據庫表
在django: python manage.py makemigrations
python manage.py migrate
6.1、SQLAchemy框架快速使用
6.1.1、創建數據庫表單,連接數據庫,創建表和刪除表
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, Index
from sqlalchemy import create_engine
Base = declarative_base()
# 創建單表
class Users(Base):
__tablename__ = 'users_test'
id = Column(Integer, primary_key=True,autoincrement=True)
name = Column(String(32))
extra = Column(String(16))
# 創建表函數
def init_db():
# 數據庫連接相關
engine = create_engine("mysql+pymysql://root:[email protected]:3306/orm?charset=utf8", max_overflow=5)
# 創建表
Base.metadata.create_all(engine)
# 刪除表函數
def drop_db():
# 數據庫連接相關
engine = create_engine("mysql+pymysql://root:[email protected]:3306/orm?charset=utf8", max_overflow=5)
# 刪除表
Base.metadata.drop_all(engine)
if __name__ == '__main__':
pass
6.2、SQLAchemy框架組件介紹 學習地址:https://www.cnblogs.com/wupeiqi/articles/5713330.html
SQLAlchemy本身無法操作數據庫,其必須以來pymsql等第三方插件,Dialect用於和數據API進行交流,根據配置文件的不同調用不同的數據庫API,從而實現對數據庫的操作:
如: 常用的 pymysql: mysql+pymysql://<username>:<password>@<host>/<dbname>[?<options>]
MySQL-Python
mysql+mysqldb://<user>:<password>@<host>[:<port>]/<dbname>
pymysql
mysql+pymysql://<username>:<password>@<host>/<dbname>[?<options>]
MySQL-Connector
mysql+mysqlconnector://<user>:<password>@<host>[:<port>]/<dbname>
cx_Oracle
oracle+cx_oracle://user:pass@host:port/dbname[?key=value&key=value...]
6.3、SQLAchemy執行原生SQL,使用sqlalchemy的數據庫連接池
import threading
from sqlalchemy import create_engine
engine = create_engine(
"mysql+pymysql://root:[email protected]:3306/orm?charset=utf8",
max_overflow=0, # 超過連接池大小外最多創建的連接
pool_size=5, # 連接池大小
pool_timeout=30, # 池中沒有線程最多等待的時間,否則報錯
pool_recycle=-1 # 多久之後對線程池中的線程進行一次連接的回收(重置)
)
def task(arg):
conn = engine.raw_connection()
cursor = conn.cursor()
cursor.execute(
"select * from users"
)
result = cursor.fetchall()
print(result)
cursor.close()
conn.close()
for i in range(20):
t = threading.Thread(target=task, args=(i,))
t.start()
6.3、SQLAchemy創建表結構 學習地址:https://www.cnblogs.com/wupeiqi/articles/8259356.html
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, Index, DateTime, create_engine
import datetime
engine = create_engine("mysql+pymysql://root:[email protected]:3306/test?charset=utf8", max_overflow=5)
Base = declarative_base()
# 創建單表
class Users(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String(32))
extra = Column(String(16))
# 課程表
class Classes(Base):
__tablename__ = 'classess'
id = Column(Integer, primary_key=True,autoincrement=True) # primary_key:是否主鍵,autoincrement:是否自增
name = Column(String(32),nullable=False,unique=True) # unique: 是否唯一索引,nullable:是否爲空,index:普通索引
# 學生表(一對多)
class Student(Base):
__tablename__ = 'student'
id = Column(Integer, primary_key=True,autoincrement=True) # primary_key:是否主鍵,autoincrement:是否自增
username = Column(String(32),nullable=False,unique=True) # unique: 是夠唯一索引,nullable:是否爲空
password = Column(String(32),nullable=False)
create_date = Column(DateTime,default=datetime.datetime.now) # 生成一個默認時間"當前時間"
class_id = Column(Integer,ForeignKey("classess.id")) # 使用ForeignKey關聯另外一個表字段(class表名,id對應字段)
# 愛好表
class Hobby(Base):
__tablename__ = 'hobby'
id = Column(Integer, primary_key=True)
caption = Column(String(50),default="籃球")
# 數據表,多對多
class Student2Hobby(Base):
__tablename__ = 'student2hobby'
id = Column(Integer, primary_key=True,autoincrement=True)
student_id = Column(Integer,ForeignKey("student.id"))
hobby_id = Column(Integer,ForeignKey("hobby.id"))
# 聯合索引
__table_args__ = (
# 唯一聯合索引, unique=True
# "student_id","hobby_id"表示字段,name = "uix_student_id_hobby_id"表示聯合索引名稱
UniqueConstraint("student_id","hobby_id", name = "uix_student_id_hobby_id"),
# 普通的聯合索引, index = True
# Index("ix_id_name","name","extra"),
)
# 創建表函數
def init_db():
Base.metadata.create_all(engine)
# 刪除表函數
def drop_db():
Base.metadata.drop_all(engine)
6.4、SQLAchemy實現增刪改查 學習地址:https://www.cnblogs.com/wupeiqi/articles/8259356.html
一、數據庫文件.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, Index, DateTime, create_engine
from sqlalchemy.orm import relationship
import datetime
engine = create_engine("mysql+pymysql://root:[email protected]:3306/test?charset=utf8", max_overflow=5)
Base = declarative_base()
# 創建單表
class Users(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String(32))
extra = Column(String(16))
# 課程表
class Classes(Base):
__tablename__ = 'classess'
id = Column(Integer, primary_key=True,autoincrement=True) # primary_key:是否主鍵,autoincrement:是否自增
name = Column(String(32),nullable=False,unique=True) # unique: 是否唯一索引,nullable:是否爲空,index:普通索引
# 學生表(一對多)
class Student(Base):
__tablename__ = 'student'
id = Column(Integer, primary_key=True,autoincrement=True) # primary_key:是否主鍵,autoincrement:是否自增
username = Column(String(32),nullable=False,unique=True) # unique: 是夠唯一索引,nullable:是否爲空
password = Column(String(32),nullable=False)
create_date = Column(DateTime,default=datetime.datetime.now) # 生成一個默認時間"當前時間"
class_id = Column(Integer,ForeignKey("classess.id")) # 使用ForeignKey關聯另外一個表字段(class表名,id對應字段)
# 這一行不會在數據庫內添加任何字段,這是用來做主動關聯查詢用到的,通過ForeignKey實現連表。可以添加多個relationship字段。
# "Classes" 表示要關聯的表。
# backref="stus" 表示反向。
cls = relationship("Classes",backref="stus")
# 愛好表
class Hobby(Base):
__tablename__ = 'hobby'
id = Column(Integer, primary_key=True)
caption = Column(String(50),default="籃球")
# 數據表,多對多
class Student2Hobby(Base):
__tablename__ = 'student2hobby'
id = Column(Integer, primary_key=True,autoincrement=True)
student_id = Column(Integer,ForeignKey("student.id"))
hobby_id = Column(Integer,ForeignKey("hobby.id"))
# 聯合索引
__table_args__ = (
# 唯一聯合索引, unique=True
# "student_id","hobby_id"表示字段,name = "uix_student_id_hobby_id"表示聯合索引名稱
UniqueConstraint("student_id","hobby_id", name = "uix_student_id_hobby_id"),
# 普通的聯合索引, index = True
# Index("ix_id_name","name","extra"),
)
# 創建表函數
def init_db():
Base.metadata.create_all(engine)
# 刪除表函數
def drop_db():
Base.metadata.drop_all(engine)
二、增刪改查文件.py
import models
from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine,text
engine = create_engine("mysql+pymysql://root:[email protected]:3306/test?charset=utf8", max_overflow=5)
Session = sessionmaker(bind=engine)
session = Session()
# 一、添加數據
# 1.1、一次添加單條表數據
# obj = models.Classes(name="BBBBB")
# session.add(obj)
# session.commit()
# 1.2、一次添加多條表數據
# obj= [
# models.Classes(name="GGGGG"),
# models.Classes(name="HHHHH"),
# ]
# session.add_all(obj)
# session.commit()
# 二、查詢數據
# 2.1、查詢一個表內所有的數據,all()表示所有
# res = session.query(models.Classes).all()
# for n in res:
# print(n.id,n.name)
# 2.2、查詢的多種用法
# 2.2.1
# res = session.query(models.Classes.id,models.Classes.name).all()
# print(res)
# 默認取到的值res是元組(實際上是一個對象):[(1, 'AAAAA'), (2, 'BBBBB099'), (8, 'CCCCC099')]
# for n in res:
# print(n.id,n.name)
# 循環取到的值,可以通過所有或者字段。
# 1 AAAAA
# 2 BBBBB099
# 8 CCCCC099
# 2.2.2、起別名"lable",models.Classes.name.label("xx") 表示把"name"這個字段起個別名叫"xx"
# res = session.query(models.Classes.id,models.Classes.name.label("xx")).all()
# for n in res:
# print(n.id,n.xx)
# 2.2.3、filter的使用方式
# r3 = session.query(models.Classes).filter(models.Classes.name == "AAAAA").all()
# for n in r3:
# print(n.name) # AAAAA
# 2.2.4、filter_by的使用方式
# r4 = session.query(models.Classes).filter_by(name='BBBBB').all() # 直接指定字段
# for n in r4:
# print(n.name) # AAAAA
# r4 = session.query(models.Classes).filter_by(name='BBBBB').first() # 查詢第一個匹配字段
# print(r4.name)
# 2.2.5、構造查詢條件(構造SQL語句)
# r7 = session.query(models.Classes).from_statement(text("SELECT * FROM classess where name=:name")).params(name='AAAAA').all()
# print(r7) # [<models.Classes object at 0x1062508d0>]
# r7 = session.query(models.Classes).from_statement(text("SELECT * FROM classess where name=:name")).params(name='AAAAA')
# print(r7) # 不加".all()"拿到的就是執行的SQL語句 SELECT * FROM classess where name=%(name)s
# 2.2.6、子查詢
# 2.2.7、關聯子查詢
# 三、刪除數據
# 3、刪除表內的數據,query(models.Classes)表示哪張表,filter(models.Classes.id > 10)表示過濾條件,delete()表示刪除
# session.query(models.Classes).filter(models.Classes.id > 10).delete()
# session.commit()
# 四、修改數據
# 4、修改數據,先查找數據,然後對數據做修改。{models.Classes.name: models.Classes.name + "099"}把 "A" 修改爲 "A099" 。
# 4.1、synchronize_session=False 表示在內部做計算(字符串的拼接)
# 4.2、synchronize_session="evaluate" 表示在內部做計算(數值上相加)
# session.query(models.Classes).filter(models.Classes.id > 1).update({models.Classes.name: models.Classes.name + "099"},synchronize_session=False)
# session.commit()
# 五、聯繫測試
# 1、插入數據
# obj = models.Student(username="sudada",password="123456",class_id=2)
# session.add(obj)
# session.commit()
# 2、查找一個用戶名
# res = session.query(models.Student).filter(models.Student.username == "sudada").first()
# print(res.username)
# 3、找到所有學生,打印學生信息(包含班級名稱),方式一
# res = session.query(models.Student).all()
# for n in res:
# cls_obj = session.query(models.Classes).filter(models.Classes.id == n.class_id).first()
# print(n.id,n.username,cls_obj.name)
# 方式二、使用join關聯表,查詢學生(Student)id,username以及課程(Classes)name
# res = session.query(models.Student.id,models.Student.username,models.Classes.name).join(models.Classes,isouter=True).all()
# print(res)
# 方式三、在數據表對應的代碼裏,加上relationships配置:cls = relationship("Classes",backref="stus")
# res = session.query(models.Student).all()
# for n in res:
# print(n.id,n.username,n.cls.name)
# 4、查詢 BBBBB099 課程所有的學生
# res = session.query(models.Classes).filter(models.Classes.name == "BBBBB099").first() # 拿到課程
# student_list = res.stus # 通過課程表裏的relationship("Classes",backref="stus")拿到關聯表Student
# for n in student_list:
# print(n.id,n.username)
6.5、彙總常用的一些查詢方法:
# 給username起一個別名"xx"
session.query(models.Student.username.label("xx"))
# 多個查詢條件,默認就是and
session.query(models.Student).filter(models.Student.username == "szq",models.Student.id == 2)
# 使用and,查詢多個條件
session.query(models.Student).filter(and_(models.Student.username == "szq",models.Student.id == 1))
# 使用or,查詢多個條件
session.query(models.Student).filter(or_(models.Student.username == "szq",models.Student.id == 2))
# 使用and+or,查詢多個條件
session.query(models.Student).filter(or_(models.Student.username == "szq",and_(models.Student.username == "sudada",models.Student.id >1),models.Student.id == 2))
# 跨表查詢時有ForeignKey
session.query("表").join("表",isouter=True)
# 跨表查詢時無ForeignKey,需要加"A.id == B.id"
session.query("表").join("表",A.id == B.id ,isouter=True)
# 分頁功能:
session.query("表")[1:34]
session.query("表").order_by(表.字段.desc())
# 分組功能
session.query("表").group_by(表.字段.desc())