前言
SQLALchemy
是Python
社区中著名的ORM
库,由于本职工作是前端,Python
相关技术栈不常用到,经常性的会造成遗忘,所以记下这篇文章,已备忘速查
以下代码演示环境为:
- 数据库:
MySQL5.7
- 操作系统:
Windows10
- 开发工具:
PyCharm2019.3.2
Python3.8
安装
1 2 3 4 5
| pip install sqlalchemy
pip install mysql-connector-python
|
引入对应的模块
1 2 3
| from sqlalchemy.orm import sessionmaker, relationship from sqlalchemy import Column, String, Integer, Text, ForeignKey, create_engine from sqlalchemy.ext.declarative import declarative_base
|
连接数据库并创建session
1 2 3 4 5 6
| engine = create_engine('mysql+mysqlconnector://<username>:<password>@<host>:<port>/<db>')
Session = sessionmaker(bind=engine)
session = Session()
|
创建表的class定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| 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)
|
创建表(如果表已经存在,不会重新创建覆盖)
1
| Base.metadata.create_all(engine)
|
删除表
1
| Base.metadata.drop_all(engine)
|
基本CRUD
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| def get_vod(vid): return session.query(Vod).filter_by(id=vid).first()
def insert_vod(vod): session.add(vod) session.commit() return vod
def delete_vod(vod): session.delete(vod) session.commit()
def update_vod(vod): 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()
|
创建多对多关系
创建多对多关系,需要通过中间表实现
1 2 3 4 5 6 7 8 9
| 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
,则需要这样操作
1 2 3 4 5 6 7
| 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
, 则操作就会变得非常简单
1 2 3 4 5 6 7 8 9 10 11
| class Vod(Base): 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
的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 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
|
创建一对多关系
创建一对多关系,只需要在子表中设置外键, 外键的值为父表的主键值, 不需要额外建立中间表;多对多关系本质上就是通过两张表与中间表建立一对多关系实现的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 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
不是一个列表
1 2 3 4
| class Vod(Base): play_bilibili = relationship('Play', uselist=False, backref='vod');
|
Demo源码
https://github.com/demo-box/sqlalchemy-demo.git