SQLAlchemy的两个问题
问题1:原生Python中使用SQLAlchemy操作数据库
完整实现代码
from sqlalchemy import create_engine, Column, Integer, String, ForeignKey
from sqlalchemy.orm import declarative_base, relationship, sessionmaker
# 初始化数据库连接
DATABASE_URI = "mysql://root:123@localhost:3306/testdb?charset=utf8mb4"
engine = create_engine(DATABASE_URI, echo=True)
Base = declarative_base()
Session = sessionmaker(bind=engine)
# 定义多对多关联表
student_course = Base.classes.student_course = Base.metadata.tables['student_course'] \
if 'student_course' in Base.metadata.tables else Base.metadata.Table(
'student_course',
Base.metadata,
Column('id', Integer, primary_key=True),
Column('student_id', Integer, ForeignKey('students.id')),
Column('course_id', Integer, ForeignKey('courses.id')),
Column('score', Float)
)
# 学生模型
class Student(Base):
__tablename__ = 'students'
id = Column(Integer, primary_key=True)
name = Column(String(50))
courses = relationship('Course', secondary=student_course, back_populates='students')
# 课程模型
class Course(Base):
__tablename__ = 'courses'
id = Column(Integer, primary_key=True)
name = Column(String(50))
students = relationship('Student', secondary=student_course, back_populates='courses')
# 创建表
Base.metadata.create_all(engine)
# 增删查改操作示例
def basic_operations():
session = Session()
try:
# 新增数据
math = Course(name='高等数学')
session.add(math)
session.commit()
# 查询数据
course = session.query(Course).filter_by(name='高等数学').first()
print(f"查询结果:{course.name}")
# 更新数据
course.name = '线性代数'
session.commit()
# 删除数据
session.delete(course)
session.commit()
finally:
session.close()
# 关联查询示例
def relationship_query():
session = Session()
try:
# 创建关联数据
physics = Course(name='大学物理')
student = Student(name='张三', courses=[physics])
session.add(student)
session.commit()
# 查询学生课程
student = session.query(Student).first()
print(f"学生课程:{[c.name for c in student.courses]}")
# 反向查询选课学生
course = session.query(Course).first()
print(f"选课学生:{[s.name for s in course.students]}")
finally:
session.close()
核心要点解析
-
初始化流程:
• 使用create_engine
创建数据库引擎
• 通过declarative_base()
创建模型基类
• 使用sessionmaker
创建会话工厂 -
模型定义特点:
• 显式定义__tablename__
• 使用relationship
建立双向关联
• 多对多关系需要单独定义关联表 -
会话管理:
• 每个操作需显式创建和关闭session
• 使用try-finally保证会话关闭 -
操作对比Flask-SQLAlchemy:
diff - Flask: db.session自动管理 + 原生: 手动创建session = Session() - Flask: 模型继承db.Model + 原生: 继承declarative_base()
问题2:行政区划自关联查询实现
完整实现代码
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship
class AdministrativeDivision(Base):
__tablename__ = 'administrative_divisions'
id = Column(Integer, primary_key=True)
name = Column(String(50), nullable=False)
parent_id = Column(Integer, ForeignKey('administrative_divisions.id'))
# 自关联关系配置
children = relationship(
'AdministrativeDivision',
back_populates='parent',
remote_side=[id],
foreign_keys=[parent_id]
)
parent = relationship(
'AdministrativeDivision',
back_populates='children',
remote_side=[id]
)
def self_relationship_demo():
session = Session()
try:
# 创建省级行政区
zhejiang = AdministrativeDivision(name='浙江省')
session.add(zhejiang)
# 创建市级行政区
hangzhou = AdministrativeDivision(name='杭州市', parent=zhejiang)
ningbo = AdministrativeDivision(name='宁波市', parent=zhejiang)
# 创建区级行政区
xihu = AdministrativeDivision(name='西湖区', parent=hangzhou)
session.commit()
# 查询浙江省下属城市
print(f"浙江省辖市:{[c.name for c in zhejiang.children]}")
# 查询西湖区的上级路径
current = xihu
while current.parent:
print(f"{current.name} ← {current.parent.name}")
current = current.parent
finally:
session.close()
核心要点解析
-
自关联关键配置:
python # 子关系配置 children = relationship( 'AdministrativeDivision', foreign_keys=[parent_id], # 明确指定外键 remote_side=[id], # 指定远程端的比较字段 back_populates='parent' # 双向关系反向引用 ) # 父关系配置 parent = relationship( 'AdministrativeDivision', remote_side=[id], # 与子关系相同字段 back_populates='children' )
-
典型查询模式:
• 向下查询:直接访问children
属性
python province.children # 获取所有下级行政区
• 向上追溯:通过parent
属性链式查询
python district.parent.parent # 获取区→市→省的层级
-
数据操作要点:
• 建立层级关系时直接操作对象属性
• 无需手动设置外键值
• 自动维护双向关系 -
实际应用扩展:
python # 递归查询所有下级 def get_all_children(division): result = [] for child in division.children: result.append(child) result.extend(get_all_children(child)) return result # 层级缩进显示 def print_tree(division, level=0): print(' ' * level + division.name) for child in division.children: print_tree(child, level+1)
对比表格:Flask-SQLAlchemy vs 原生SQLAlchemy
功能点 | Flask-SQLAlchemy | 原生SQLAlchemy |
---|---|---|
模型基类 | db.Model | declarative_base() |
会话管理 | 自动上下文管理 | 手动创建/关闭session |
查询接口 | Model.query | session.query(Model) |
关系定义 | backref | back_populates |
多对多关联 | secondary参数 | 需显式定义关联表 |
配置管理 | 通过Flask配置 | 直接设置engine参数 |
迁移工具 | Flask-Migrate | Alembic独立使用 |
常见问题解决方案
-
循环导入问题:
• 在模型定义文件最后导入session
• 使用字符串形式指定关系类名
python children = relationship('AdministrativeDivision', ...)
-
性能优化:
python # 立即加载所有子级 session.query(AdministrativeDivision).options( joinedload(AdministrativeDivision.children) ).filter(...)
-
层级深度控制:
python # 查询指定层级深度 def query_by_level(level): return session.query(AdministrativeDivision).filter( AdministrativeDivision.level == level )
以上实现完整演示了原生SQLAlchemy的使用方法和自关联关系的典型应用场景,可根据具体需求进行扩展和优化。