Flask,3-6

第三節:

一、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())

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章