SQLAlchemy 是Python 编程语言下的一款开源软件。提供了SQL工具包及对象关系映射(ORM)工具。
ORM, 全称Object Relational Mapping, 中文叫做对象关系映射,通过ORM,我们可以像操作类一样使用数据库的数据
ORM把表映射成类,把行作为实例,把字段作为属性,ORM在执行对象操作的时候会把对相应的操作转换为数据库原生
语句的方式来完成数据库开发工作
SQLAlchemy 至少需要3部分代码,
- 定义表
- 定义数据库连接
- 进行增、删、改、查等逻辑操作
'Hello World'入门
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
from sqlalchemy import Column, Integer, String
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String)
fullname = Column(String)
password = Column(String)
def __repr__(self):
return "<User(name='%s', fullname='%s', password='%s')>" % (
self.name, self.fullname, self.password)
注意
以上的例子来自SQLAlchemy 官网,数据库用的是SQLite,没有长度限制的字符串是有效的数据类型,但在MySql中则
不然,此时可以使用String的带参数形式,如String(10)来实现指定长度。
关键点解释
- 首先导入sqlalchemy.ext.declarative,declarative_base,并定义它的实例。所有表必须继承自Base。之所以这样,可以看原版英文
When using the ORM, the configurational process starts by describing the database tables we’ll be dealing with, and then by defining our own classes which will be mapped to those tables. In modern SQLAlchemy, these two tasks are usually performed together, using a system known as Declarative, which allows us to create classes that include directives to describe the actual database table they will be mapped to.
之后利用Base 定义了一个User类
- 使用tablename属性定义了表在数据库中实际的名称users
- 引入sqlalchemy包中的Column、Intege、String类型,定义User表的数据列。
定义数据库连接
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
db_connect_string='sqlite://'
engine = create_engine(db_connect_string)
SessionType = scoped_session(sessionmaker(bind=engine,expire_on_commit=False))
def GetSession():
return SessionType()
from contextlib import contextmanager
@contextmanager
def session_scope():
session = GetSession()
try:
yield session
session.commit()
except:
session.rollback()
raise
finally:
session.close()
代码解析
引入数据库和会话引擎:sqlalchemy.create_engine,sqlalchemy.orm.scoped_session,sqlalchemy.orm.sessionmaker,其中,create_engine传入了
一个数据库的URI,sqlite://表示使用了一个SQLite的内存型数据库。URI的格式如下:
dialect+driver://username:password@host:port/database。dialect是数据库的实现,比如MySql,PostgreSQL,SQLite。driver是Python对应的驱动,
如果不指定,会选择默认的驱动。比如MySQL的默认驱动是MySQldb: engine=create_engine('mysql+mysqldb://scott:tiger@localhost/foo'),默认驱动可以省略。定义数据库连接需要用到的字符串。
用create_engine建立数据库引擎。
使用上下文管理器定义的scoped_session,建立会话。
为了使之后的数据库操作的代码能够自动进行事务处理,定义了上下文函数session_scope()。在Python中定义上下文函数的方法
是为其加入contextlib包中的contextmanager装饰器。在上下文函数中执行如下逻辑:在函数开始时建立数据库会话,此时会自动建立一个数据库事务;
当发生异常是回滚事务;当退出时关闭连接。在关闭连接时会自动进行事务处理*
SQLAlchemy 同样可以执行原生SQL对象
from sqlalchemy import create_engine
def exec_raw_sql(eng, sql):
with eng.connect() as con:
return list(con.execute(sql))
- exec_raw_sql 是自定义的函数,需要传入SQLAlchemy 数据库连接实例化的引擎,具体为
eng = create_engine(db_uri),con.execute返回的对象需要用list转化一下,因为返回的是数据库查询对象。
查询条件设置
SQLAlchemy 查询条件被称为过滤器
- 等值过滤器
session.query(Account).filter(Account.user_name == "Jackey")
session.query(Account).filter(Account.salary == 2000)
- 不等过滤器(!=,<,>,<=,>=)
session.query(Account).filter(Account.user_name != "Jackey")
session.query(Account).filter(Account.salary >= 2000)
- 模糊查询--like 仅支持字符串
session.query(Account).filter(Account.user_name.like('%i%'))
- 包括过滤器(in_)
session.query(Account).filter(Account.user_name.in_(['xiaohong','xiaoming','xiaoli']))
- 判断是否为空(is NULL, is not NULL)
空值NULL是数据库字段中比较特殊的值。在SQLAlchemy中支持对字段是否为空进行判断。判读时可以用等值,不等值过滤器筛选,亦可用is,isnot进行筛选。
session.query(Account).filter(Account.salary == None)
session.query(Account).filter(Account.salary.is_(None))
session.query(Account).filter(Account.salary != None)
session.query(Account).filter(Account.isnot(None))
- 非逻辑(~)
session.query(Account).filter(~Account.id.in_([1,3,5]))
- 与逻辑(and_)
session.query(Account).filter(Account.title=='Engineer',Account.salary=3000)
session.query(Account).filter(and_(Account.title=="engineer",Account.salary=3000))
- 或逻辑(or_)
session.query(Account).filter(or_(Account.title=="engineer",Account.salary=3000))
关系操作
SQLAlchemy 支持数据库模型的一对一,一对多,多对多关系。支持通过将各个表通过主键,外键联系起来。
以下是一个简单的数据表 E-R图映射,其中,class表与student表是一对多关系,class表与teacher表之间是多对多关系
将E-R图转化为SQLAlchemy模型定义的代码文件如下
from sqlalchemy import Table, Column, Integer, ForeignKey, String
from sqlalchemy.orm import relationship, backref
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Class(Base):
__tablename__ = 'class'
class_id = Column(Integer, primary_key=True)
name= Column(String(50))
level = Column(Integer)
address = Column(String(50))
class_teachers = relationship("ClassTeacher", backref="class")
students = relationship("Student", backref="class")
class Student(Base):
__tablename__ = 'student'
student_id = Column(Integer, primary_key=True)
name= Column(String(50))
age = Column(Integer)
gender= Column(String(10))
address= Column(String(50))
class_id = Column(Integer, ForeignKey('class.class_id'))
class Teacher(Base):
__tablename__ = 'teacher'
teacher_id = Column(Integer, primary_key=True)
name= Column(String(50))
gender= Column(String(10))
telephone= Column(String(50))
address= Column(String(50))
class_teachers = relationship("ClassTeacher", backref="teacher")
class ClassTeacher(Base):
__tablename__ = 'class_teacher'
teacher_id = Column(Integer, ForeignKey('teacher.teacher_id'), primary_key=True)
class_id = Column(Integer,ForeignKey('class.class_id'),primary_key=True)
代码说明
外键设置:在列的定义中,为Column传入ForeignKey进行外键设置
class_id = Column(Integer, ForeignKey('class.class_id'))
关系设置:通过relationship关键字在父模型中建立对子表的引用,例如Class模型中的关系设置如下
students = relationship("Student", backref="class")
注意students 即student表中所有拥有相同class_id的记录的集合,SQLAlchemy中Class表的实例可直接通过students属性访问到。
class_=session.query(Class).filter(Class.name==u"三年二班").first()
for student in class_.students:
print u"学生姓名:%s, 年龄:%d" %(student.name, student.age)
另外,backref参数如果设置了,则表示同时设置了父表(一对多关系中的一)对子表的引用。
即student表的实例可以通过class属性访问与其关联的class记录。
多对多关系的使用,通过ClassTeacher表实现。其中分别设置模型Class和Teacher的外键,并且在父模型中设置相应的relationship实现。
多对多关系也可以想象成一个关联表,分别对两个父表实现了多对一的关系。班级与老师之间为多对多关系,如下代码可以打印特定班级所有老师的信息
class_=session.query(Class).filter(Class.name==u"三年二班").first()
for class_teacher in class_.class_teachers:
teacher = class_teacher.teacher
print u"老师姓名:%s, 电话:%s"%(teacher.name, teacher.telephone)
级联
级联是在一对多关系中父表对字表进行联动操作的数据库术语。因为父表与字表通过外键关联,所以对父表或字表的增删改查都会对另一张表也产生相应的
影响。
级联定义
SQLAlchemy 中的级联通过父表中的relationship属性定义cascade参数来实现,代码如下:
from sqlalchemy import Table, Column, Integer, ForeignKey, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship, backref
Base = declarative_base()
class Class(Base):
__tablename__ = 'class'
class_id = Column(Integer, primary_key=True)
name= Column(String(50))
level = Column(Integer)
address = Column(String(50))
students = relationship("Student", backref="class_", cascade="all")
class Student(Base):
__tablename__ = 'student'
student_id = Column(Integer, primary_key=True)
name= Column(String(50))
age = Column(Integer)
gender= Column(String(10))
address= Column(String(50))
class_id = Column(Integer, ForeignKey('class.class_id'))
上述代码定义了class表和student表。一对多的关系由父表中的relationship属性students进行定义。relationship中的cascade属性
定义了要在该关系上实现的级联方法为"all", 其他的cascade属性有save_update,merge,expunge,delete,delete-orphan,refresh-expire,all
其中save_update,delete,delete_orphan与父表的数据变化有关,其他与session操作有关。多个cascade属性可以通过逗号分隔并同时赋值给
cascade。如
students = relationship("Student",backref="class_",cascade="save-update,merge,expunge")