问题描述:web应用在线上运行,使用SQLAlchemy,没有用户在进行增删改查,甚至没有用户登陆,但在后台无法对相应MySQL表进行重命名或删除,显示锁定,但可以增删改查。
本以为是锁或连接池的问题,其实跟锁或连接池没关系,实际上是对SQLAlchemy连接使用不当造成的,主要是使用完没有进行session.close(),本来使用with上下文管理器,参考SQlALchemy session详解,
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String
Base = declarative_base()
class Users(Base):
__tablename__ = 'users'
user_id = Column(Integer, primary_key=True, comment='自增ID')
user_name = Column(String, comment='用户名')
from sqlalchemy.orm import sessionmaker
DbSession = sessionmaker(bind=engine) # 创建session
from contextlib import contextmanager
@contextmanager
def session_maker(session=DbSession): # 封装上下文方法
try:
yield session()
session.commit()
except:
session.rollback()
raise
finally:
session.close()
# 调用:
def my_user():
with session_maker() as db_session:
user = db_session.query(Users).filter_by(name='Dale').one_or_none()
本质上约等于try... finally... 按照上边的调用其实没问题,但我把with session_maker(session=session) as db: self.db = db 放到了类的__init__里了:
class MyClass():
def __init__(self):
with session_maker(DbSession) as db:
self.db = db
def myfunc():
self.db.query(Users).filter_by(name='Dale').one_or_none()
然后在方法中使用时直接用self.db,这样在方法使用完时是没有进行session.close()的,而打开数据库表本质上是打开文件的操作,session.close本质上是关闭打开的文件,解除系统资源占用。如此导致可以增删改查相关表,但无法重命名和删除之类的操作。
但在类中,更pythonic的做法是使用__del__,自动销毁对象:
class MyClass():
def __init__(self):
with session_maker(DbSession) as db:
self.db = db # 使用其中的自动commit()
# def __init__(self): # 正常
# self.db = DbSession()
def __del__(self):
self.db.close() # 必要
def myfunc():
self.db.query(Users).filter_by(name='Dale').one_or_none()
这样就可以重命名表操作了。
参考资料: