ORM: Object Relational Mapper.html
目前Python有不少ORM工具能够将数据库映像为Python的Objects对象。
其中比较知名的有Django的ORM,SQLAlchemy, PostgreSQL等。SQLAlchemy
有更多的人维护,功能也比较齐全。因此通常是咱们的首选项。python
对于SQLAlchemy
的使用者来讲,只要你一开始链接上数据库,不论是Sqlite,MySQL仍是什么,后面的处理方式彻底同样。这种便利性也是它受欢迎的缘由。mysql
抛弃了传统的本身编织SQL语句、制做模型、链接数据库方式,SQLAlchemy
直接把这些东西全包在黑盒里面,让咱们彻底不须要去管。连SQL-Injection注入这种东西也被它帮忙防范了。这样一来,能够说在链接数据库方面,帮咱们节省了最少一半以上的代码。sql
甚至连数据查询,SQLAlchemy也代替了SQL语句,而使用了专门的相似MongoDB的Object.query.filter_by(name='Jason').all()
这种方法。数据库
安装:segmentfault
# 安装sqlalchemy $ pip install sqlalchemy
安装Drivers:session
# Sqlite # 不须要,Python自带 # MySQL $ pip install pymysql # Postgresql $ pip install psycopg2
SQLAlchemy自身不带数据库driver,须要咱们本身安装,并在链接时候指定。
而这些driver,实际上就是咱们曾经手动链接数据库所用的包。而SQLAlchemy只是代替咱们使用这些一样的包。app
建立一个sqlite的ORM引擎:工具
from sqlalchemy import create_engine # 链接格式为:sqlite://<Hostname>/<path> engine = create_engine('sqlite:///foo.db', echo=True)
建立一个MySQL的ORM引擎:测试
from sqlalchemy import create_engine # 链接格式为:dialect+driver://username:password@host:port/database engine = create_engine('mysql+pymysql://root:password123@localhost/db_test_01', echo=True)
数据库的位置(三斜杠为相对路径,四斜杠为绝对路径):
# 使用绝对路径的数据库文件(////),如/tmp/mydatabase.db engine = create_engine('sqlite:////tmp/mydatabase.db') # 使用当前「执行位置」数据库文件(///或///./) engine = create_engine('sqlite:///mydatabase.db') # 使用当前「执行位置」父级目录(///../)的数据库文件 engine = create_engine('sqlite:///../mydatabase.db') # 使用当前「脚本位置」的数据库文件 import os cwd = os.path.split(os.path.realpath(__file__))[0] engine = create_engine('sqlite:///{}/mydatabase.db'.format(cwd))
注意:不一样于SQL语句,SQLAlchemy中的表名是 彻底区分大小写的!
建立一个Schema表(指单纯表,不包含ORM对象):
from sqlalchemy import create_engine, MetaData from sqlalchemy import Table, Column from sqlalchemy import Integer, String, ForeignKey engine = create_engine('mysql+pymysql://root:password123@localhost/db_test_01', echo=True) metadata = MetaData(engine) # 建立一个表 user_table = Table( 'tb_user', metadata, Column('id', Integer, primary_key=True), Column('name', String(50)), Column('fullname', String(100)) ) # 让改动生效 metadata.create_all()
建立一个ORM对象(包括表):
# 导入表格建立引擎 from sqlalchemy import create_engine # 导入列格式 from sqlalchemy import Column, Integer, String, ForeignKey # 导入建立ORM模型相关 from sqlalchemy.ext.declarative import declarative_base Base = declarative_base() class User(Base): __tablename__ = 'tb_Person' id = Column('id', Integer, primary_key=True) username = Column('username', String, unique=True) engine = create_engine('sqlite:///test.sqlite', echo=True) Base.metadata.create_all(bind=engine)
用普通表Table和ORM对象建立的表有什么不一样?
他们在数据库中建立的,是彻底相同的表!惟一区别是,Table建立的不包含ORM对象,也就是不提供让你直接操做Python对象的功能。
这么作的好处是,有不少只是关联做用的表,没有必要生成ORM对象。
# engine = ... # Base = ... # 逐个ORM对象删除对应的表,如User类 User.__table__.drop(engine) # 删除所有表 Base.metadata.drop_all(engine)
设计或调试过程当中,咱们常常要频繁改动表格,因此有必要在建立表格前把测试数据库中的表都清除掉,再建立新的定义。
将数据添加到数据库:
# ... # 导入session相关(用于添加数据) from sqlalchemy.orm import sessionmaker, relationship user = User() user.id = 1 user.username = 'Jason' Session = sessionmaker(bind=engine) session = Session() session.add(user) session.commit() session.close()
注意:这里的session
和网站上的session概念有点不同。这里是用来commit提交数据库变更的工具。
批量添加数据(向add_all()传入列表):
session.add_all( [user1, user2, user3] )
添加每条数据的时候自动flush():
session = sessionmaker(bind=engine, autoflush=True)
autoflush
是在每次session.add()
自动执行session.flush()
,即在插入数据库以前就在内存中生成全部对象的动态数据(如主键ID等)。通常默认是选false,由于会影响效率。最好是须要的时候,才手动执行session.flush()
具体原因,看下一节“数据生效”。
SQLAlchemy中的create_all()
和session.commit()
都是直接让python文件中定义的对象在数据库中生效的语句。在此以前,不管怎么定义,数据都是在内存中,而没有在数据库中的。
注意区分:
create_all
只是让建立表格结构生效,无关insert的数据条目session.commit()
只是让添加的数据生效,而不负责任何表格结构。这两个的顺序,固然是先建立表格,再插入数据。
只是,若是咱们知道了这个原理,在编码中才能比较运用自由。好比,连create_engine()
建立引擎,咱们均可以在后面定义,而不必非得写在文件头,即全部的ORM定义以前。create_engine
只要定义在全部ORM类和Schema表以后便可。
此后,咱们再开始进行数据插入工做,也就利用到了session。
session过程当中呢,咱们也会遇到互相引用主键外键ID的状况。可是注意,这时候由于尚未使用最终的session.commit()
真正提交数据到数据库中,这些ID是没有值的。
解决办法就是利用内置的方法session.flush()
,将session中已添加的全部对象填充好数据,可是这时候尚未提交到数据库,只是咱们内部能够正常访问各类ID了。
更新:
# Get a row of data me = session.query(User).filter_by(username='Jason').first() # Method 1: me.age += 1 session.commit() # Method 2: session.query().filter( User.username == 'Jason' ).update( {"age": (User.age +1)} ) session.commit() # Method 3: setattr(user, 'age', user.age+1) session.commit()
#sqlalchemy can't get primary key
, #sqlalchemy 如何得到主键的值
这个问题花了我不少时间探索查询,不得其解,才明白原来是很显然的事。
虽然在没有用session或engine插入数据以前,咱们能够直接浏览从ORM建立的对象中的属性值。
可是这个时候不管如何都获取不到primar_key
主键列的值。
由于这时候主键尚未插入数据库,做为动态的值
,在数据库没生效以前也就为None。
为何须要获取value of primary_key
?考虑以下这些场景:
foreign key
外键须要引用主表的id那么该怎么获取主键ID呢?
再参考Stackoverflow:sqlalchemy flush() and get inserted id?
再参考:sqlalchemy获取插入的id
再参考:Sqlalchemy;将主键设置为预先存在的数据库表(不使用sqlite)
若是要想在插入数据以前就获取主键等动态列
的值,那么有这几种方法:
session.add(..)
,再session.flush()
,而后就能够获取ID,最后再session.commit()
推荐作法以下:
即每次新建立对象后,马上session.add(..),而后马上session.flush(),所有都添加好的文末,再session.commit().
注意:query是经过session进行的,也就是必须在session.commit()
以后才能进行查询,不然会报错。
这里将的query查询,指的都是
在插入到数据库生效以后
。理解这个很重要,由于在对象未插入到数据库以前,不少主键、外键等内容都是不存在的,也就没法查询到。
参考:pythonsheets - Object Relational basic query
查询数据:
session.commit() # ... users = session.query(User).all() # 返回的是多个User类的对象:>>> [ <User 1>, <User 2>, .... ] for u in users: print(u.id, u.username)
经常使用查询方法:
# 获取某ORM中数据 .query(ORM类名) >>> session.query( User ).all() # All rows of data >>> session.query( User ).first() # First row of data as an object # 查询结果排序 .order_by(类名.列名) >>> session.query(User).order_by( User.birth ).all() # 筛选结果 .filter( True/False 表达式 ) >>> session.query(User).filter( User.name != 'Jason' ).all() >>> session.query(User).filter( User.name.like('%ed%') ).all() # Fuzzy search >>> session.query(User).filter( User.id in [1, 2, 3] ).all() # IN >>> session.query(User).filter( ~ User.id in [4, 5, 6] ).all() # NOT IN >>> session.query(User).filter( User.school == 'MIT', User.age < 24 ).first() # AND >>> session.query(User).filter( _or(User.school == 'MIT', User.age < 24) ).first() # OR