flask-sqlalchemy應用和創建session的方法


簡介

Flask-SQLAlchemy 使用起來非常有趣,對於基本應用十分容易使用,並且對於大型項目易於擴展。

常見情況下對於只有一個 Flask 應用,所有您需要做的事情就是創建 Flask 應用,選擇加載配置接着創建 SQLAlchemy 對象時候把 Flask 應用傳遞給它作為參數。

基本操作:

pip3 install flask-sqlalchemy
# __init__.py:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy

#第一步:在__init__中創建db對象,包含了SQLALchemy的所有操作。
db = SQLAlchemy()

def create_app():

app = Flask(__name__)
app.debug = True
app.config.from_object('settings.DevelopmentConfig')

from .views import account
app.register_blueprint(account.ac)
  
# 第二步: 在__init___中將app傳入到db中。
db.init_app(app)
return app

# settings.py:第三步:將連接字符串的定義寫在配置文件中
class BaseConfig(object):
# SESSION_TYPE = 'redis' # session類型為redis
# SESSION_KEY_PREFIX = 'session:' # 保存到session中的值的前綴
# SESSION_PERMANENT = True # 如果設置為False,則關閉瀏覽器session就失效。
# SESSION_USE_SIGNER = False # 是否對發送到瀏覽器上 session:cookie值進行加密
SQLALCHEMY_DATABASE_URI = "mysql+pymysql://root@127.0.0.1:3306/userinfo3?charset=utf8"
SQLALCHEMY_POOL_SIZE = 5
SQLALCHEMY_POOL_TIMEOUT = 30
SQLALCHEMY_POOL_RECYCLE = -1

# 追蹤對象的修改並且發送信號
SQLALCHEMY_TRACK_MODIFICATIONS = False


class ProductionConfig(BaseConfig):
pass


class DevelopmentConfig(BaseConfig):
pass

class TestingConfig(BaseConfig):
pass

models:
from sqlalchemy import Column, Integer, String, UniqueConstraint, Index, DATETIME, ForeignKey
from flask2 import db

# 第四步:定義數據庫表
class Users(db.Model):
__tablename__ = 'users'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(32), unique=True, nullable=False)

class School(db.Model):
__tablename__ = 'school'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(32), unique=True, nullable=False)

# 第五步: 創建離線腳本: drop_create_table.py
"""
Web運行時,flask程序運行起來,用戶通過瀏覽器訪問
離線腳本,自定義的一個py文件+使用flask中定義好的功能
"""

from flask2 import db, create_app,models


app = create_app()
with app.app_context():
# db.drop_all()
db.create_all()
# data = db.session.query(models.Users).all()
# print(data)

# 第六步: 在視圖函數中使用SQLAlchemy:

from .. import models
from flask import Blueprint

from flask2 import db

ac = Blueprint('ac',__name__)


@ac.route('/login',methods=['GET', 'POST'])
def login():

ret = db.session.query(models.Users).all()
print(ret[0].name)
db.session.remove()

return 'login'

Session 的生命周期

首先我們需要知道一個 sqlalchemy session 的生命周期是怎樣的。我們的 web 應用會同時服務多個用戶,因此不同的請求要有不同的 session,不然就會翻車。

session 會在一次請求進來的時候創建出來

session = Session()
在整個請求處理過程中被使用

session.add(some_obj)
session.commit()
在請求處理完畢后被關閉銷毀。

session.close()

當然上面的代碼只是簡略的情形,通常還需要包括 try-except 來處理 session 需要 rollback 之類的情況。

scoped_session 與 registry 模式

scoped_session就是用在 web 應用這種處理請求的場景下,協助進行 session 維護的。

from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session
engine = create_engine("mysql+pymysql://root@127.0.0.1:3306/userinfo3?charset=utf8",pool_size=2, max_overflow=0)
session_factory = sessionmaker(bind=engine)
session = scoped_session(session_factory )

通常我們在初始化 web 應用時通過scoped_session函數對原始的Session工廠進行處理,返回出一個ScopedSession工廠,在每個請求來的時候就可以通過這個工廠獲得一個 scoped_session 對象。

這實際上實現了一種 registry 模式,它有個中心化的 registry 來保存已經創建的 session,並在你調用ScopedSession工廠的時候,在 registry 里面找找是不是之前已經為你創建過 session 了,如果有,就直接把這個 session 返回給你,如果沒有,就創建一個新的 session,並注冊到 registry 中以便你下次來要的時候給你。

Thread-Local Storage

上面說到 scoped_session 類似單例模式,我們看似創建了新的 session,實際上拿到的可能是之前創建出來的 session。但我們 web 應用通常要同時處理多個請求,我的請求有沒有可能不小心拿到別人創建的 session 對象呢?

盡管 Python 有着神奇的 GIL,沒法真正的並行地跑線程,但至少還是有線程的概念的,對於不同的請求進來的時候我們通常會在不同的線程中進行處理。Python 里有個概念叫 thread local storage(TLS),即線程本地存儲,它可以作為全局變量一樣使用,但這個數據只在這個線程中有效,與其他的線程是隔離的。

import threading
mydata = threading.local()
mydata.x = 1

這不正好和剛才 registry 模式的思想差不多嘛。和我們所希望的一樣,scoped_session 也確實使用了 tls 作為 session 的存儲方式,一個線程只能拿到自己創建出來的 session,保證了不同線程不會亂入別人的 session。

使用 tls 還有另外一個好處,由於 session 是跟着線程走的,就算你沒有調用remove()親手干掉 session,也會由於線程結束,session 也跟着被一起回收掉,不至於泄漏。(但仍建議在必要的時候對資源進行顯式的回收)

還有一個隱蔽的問題,如果我們用了 gevent 來處理並發而不是用多線程,會翻車嗎?答案是不會。gevent 在monkey.patch_all()的時候,已經悄悄把這個 threading 相關的東西悄悄替換成自己的一套了,thread-local 的東西已經變成了 greenlet-local,不同協程間仍是隔離的,一般不會有問題。

一些思路

如果你是純Flask應用,不涉及任何視圖函數之外的數據庫邏輯,請直接使用flask-sqlalchemy。它封裝的session默認是scoped_session。

如果還有很多外部代碼,一定保證你的session是可控的。 簡單來說就是:

  • 用就開,用完就關
  • 盡量避免多個函數依賴之間同時使用session
  • 使用scoped_session來保證線程安全

 

關於sqlalchemy的session參照了https://blog.csdn.net/lucyxu107/article/details/82699996,感謝博主


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM