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

核心要点解析

  1. 初始化流程
       • 使用create_engine创建数据库引擎
       • 通过declarative_base()创建模型基类
       • 使用sessionmaker创建会话工厂

  2. 模型定义特点
       • 显式定义__tablename__
       • 使用relationship建立双向关联
       • 多对多关系需要单独定义关联表

  3. 会话管理
       • 每个操作需显式创建和关闭session
       • 使用try-finally保证会话关闭

  4. 操作对比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()

核心要点解析

  1. 自关联关键配置
       python    # 子关系配置    children = relationship(        'AdministrativeDivision',        foreign_keys=[parent_id],  # 明确指定外键        remote_side=[id],         # 指定远程端的比较字段        back_populates='parent'   # 双向关系反向引用    )        # 父关系配置    parent = relationship(        'AdministrativeDivision',        remote_side=[id],         # 与子关系相同字段        back_populates='children'    )    

  2. 典型查询模式
       • 向下查询:直接访问children属性
         python      province.children  # 获取所有下级行政区      
       • 向上追溯:通过parent属性链式查询
         python      district.parent.parent  # 获取区→市→省的层级      

  3. 数据操作要点
       • 建立层级关系时直接操作对象属性
       • 无需手动设置外键值
       • 自动维护双向关系

  4. 实际应用扩展
       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独立使用

常见问题解决方案

  1. 循环导入问题
       • 在模型定义文件最后导入session
       • 使用字符串形式指定关系类名
       python    children = relationship('AdministrativeDivision', ...)    

  2. 性能优化
       python    # 立即加载所有子级    session.query(AdministrativeDivision).options(        joinedload(AdministrativeDivision.children)    ).filter(...)    

  3. 层级深度控制
       python    # 查询指定层级深度    def query_by_level(level):        return session.query(AdministrativeDivision).filter(            AdministrativeDivision.level == level        )    

以上实现完整演示了原生SQLAlchemy的使用方法和自关联关系的典型应用场景,可根据具体需求进行扩展和优化。

posted @ 2025-03-26 17:50  千陌666  阅读(12)  评论(0)    收藏  举报