# SQLAlchemy备忘录

By [Untitled](https://paragraph.com/@0x9493695d028e374e8e462810b7f1836c3cdfb26e) · 2021-11-17

---

前言
--

`SQLALchemy`是`Python`社区中著名的`ORM`库，由于本职工作是前端，`Python`相关技术栈不常用到，经常性的会造成遗忘，所以记下这篇文章，已备忘速查

以下代码演示环境为:

*   数据库: `MySQL5.7`
    
*   操作系统: `Windows10`
    
*   开发工具: `PyCharm2019.3.2`
    
*   `Python3.8`
    

安装
--

    # 安装sqlalchemy
    pip install sqlalchemy
    # 安装对应的数据库驱动
    pip install mysql-connector-python
    

引入对应的模块
-------

    from sqlalchemy.orm import sessionmaker, relationship
    from sqlalchemy import Column, String, Integer, Text, ForeignKey, create_engine
    from sqlalchemy.ext.declarative import declarative_base
    

连接数据库并创建session
---------------

    # 创建连接
    engine = create_engine('mysql+mysqlconnector://<username>:<password>@<host>:<port>/<db>')
    # 创建会话类
    Session = sessionmaker(bind=engine)
    # 创建会话
    session = Session()
    

创建表的class定义
-----------

    Base = declarative_base()
    class Vod(Base):
        # 表名
        __tablename__ = 'vod'
        
        id = Column(Integer, primary_key=True, autoincrement=True)
        
        title = Column(String(200))
        
        cover = Column(Text)
        
        intro = Column(Text)
    class Actor(Base):
        # 表名
        __tablename__ = 'actor'
        
        id = Column(Integer, primary_key=True, autoincrement=True)
        
        name = Column(String(40))
        
        avatar = Column(Text)
    

创建表(如果表已经存在，不会重新创建覆盖)
---------------------

    Base.metadata.create_all(engine)
    

删除表
---

    Base.metadata.drop_all(engine)
    

基本CRUD
------

    def get_vod(vid):
        return session.query(Vod).filter_by(id=vid).first()
    def insert_vod(vod):
        # add方法仅仅是新建，如果实例的主键值已经存在于表中，则会报错，不会去更新
        session.add(vod)
        session.commit()
        return vod
    def delete_vod(vod):
        # 以一个实例对象为参数，通过主键字段来判断表中是否存在这条记录
        session.delete(vod)
        session.commit()
    def update_vod(vod):
        # merge方法会通过主键值来判断当前实例是否存在于表中，如果存在，则更新; 否则新建
        session.merge(vod)
        session.commit()
        # 或者先通过查询获取到需要更新的对象，然后更新这个对象并提交
        my_vod = session.query(Vod).filter_by(id=vod.id).first()
        if my_vod:
            my_vod.name = vod.name
            session.commit()
    

创建多对多关系
-------

创建多对多关系，需要通过中间表实现

    class Vod2Actor(Base):
        # 表名
        __tablename__ = 'vod2actor'
        
        id = Column(Integer, primary_key=True, autoincrement=True)
        
        vod_id = Column(Integer, ForeignKey('vod.id'))
        
        actor_id = Column(Integer, ForeignKey('actor.id'))
    

使用`ForeignKey`创建外键, 外键所在的表为子表，所以Vod2Actor为子表，Vod和Actor为父表ForeignKey接受一个字符串作为参数，参数为'<\_\*tablename\_\*>.' 创建relationship `relationship`是`sqlalchemy.orm`提供的对关系之间的一种便利调用方式。 创建`relationship`不是必须的。假设现在已知一个`Vod.title`的值，想要知道这个`Vod.actors`的值，如果不通过`relationship`，则需要这样操作 def get\_actors\_by\_vod\_title(title): vod = session.query(Vod).filter\_by(title=title).first() if vod: vod2Actors = session.query(Vod2Actor).filter\_by(vod\_id=vod.id).all() actors = \[session.query(Actor).filter\_by(id=item.actor\_id).first() for item in vod2Actors\] return actors return None 如果在`Vod`中加上`relationship`, 则操作就会变得非常简单 class Vod(Base): # ...... # 第一个参数为类名，第二个secondary为中间表的表名 actors = relationship('Actor', secondary='vod2actor') def get\_actors\_by\_vod\_title\_with\_relationship(title): vod = session.query(Vod).filter\_by(title=title).first() if vod: return vod.actors return None `relationship`还有一个`backref`参数，它提供了反向操作的能力, 当设置后， `actor`也可以直接查询与其对应的`vods`的值 class Vod(Base): # ...... # backref为一个字符串，设置后，actor便会多一个以这个字符串为名的属性 # 除了backref，还有一个back\_populates, 两者功能一样，backref是back\_populates的高级版，使用更简单方便 # backref只需单向声明，而back\_populates则需要双向声明 # 以vod和actor为例,backref只需要在Vod或Actor其中一个的relationship中声明，而back\_populates则需要在Vod和Actor的relationship中都声明 actors = relationship('Actor', secondary='vod2actor', backref='vods') def get\_vods\_by\_actor\_name\_with\_backref(name): actor = session.query(Actor).filter\_by(name=name).first() if actor: return actor.vods return None 创建一对多关系 创建一对多关系，只需要在子表中设置外键, 外键的值为父表的主键值， 不需要额外建立中间表；多对多关系本质上就是通过两张表与中间表建立一对多关系实现的 class Vod(Base): # ... downloads = relationship('Download', backref="vod"); class Download(Base): # 表名 \_\_tablename\_\_ = 'download' id = Column(Integer, primary\_key=True, autoincrement=True) url = Column(Text) # 迅雷/磁力/百度云 type = Column(Text(20)) vod\_id = Column(Integer, ForeignKey('vod.id')) 创建一对一关系 创建一对一关系，和创建一对多关系方法一致，只是在通过`relationship`进行快捷操作时，需要设置`uselist=False`, 使`play_bilibili`不是一个列表 class Vod(Base): # ... # 默认uselist=True, 通过play\_bilibili获取到的值是一个列表 play\_bilibili = relationship('Play', uselist=False, backref='vod'); Demo源码 [https://github.com/demo-box/sqlalchemy-demo.git](https://github.com/demo-box/sqlalchemy-demo.git)

---

*Originally published on [Untitled](https://paragraph.com/@0x9493695d028e374e8e462810b7f1836c3cdfb26e/sqlalchemy)*
