sqlalchemy 是一款Python语言写的ORM框架, 该框架建立在数据库API基础之上。
sqlalchemy 本身无法操作数据库,必须已第三方插件为基础,Dialect用于和数据API进行交流,根据不通的的配置调用不通的数据库API,从而实现对数据库的操作。
1 2 3 4 5 6 7 8 9 10 11 |
MySQL - Python mysql + mysqldb: / / <user>:<password>@<host>[:<port>] / <dbname> pymysql mysql + pymysql: / / <username>:<password>@<host> / <dbname>[?<options>] MySQL - Connector mysql + mysqlconnector: / / <user>:<password>@<host>[:<port>] / <dbname> cx_Oracle oracle + cx_oracle: / / user: pass @host:port / dbname[?key = value&key = value...] |
ORM
一、创建表
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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, Index from sqlalchemy.orm import sessionmaker, relationship from sqlalchemy import create_engine engine = create_engine( "mysql+pymysql://root:[email protected]:3306/mybase" , max_overflow = 5 ) BaseModel = declarative_base() class Blog(BaseModel): __tablename__ = 'blog' id = Column(CHAR( 32 ), primary_key = True ) title = Column(String( 64 ), server_default = '', nullable = False ) text = Column(String, server_default = '', nullable = False ) user = Column(CHAR( 32 ), index = True , server_default = '', nullable = False ) create = Column(BIGINT, index = True , server_default = '0' , nullable = False ) class User(BaseModel): __tablename__ = 'user' id = Column(CHAR( 32 ), primary_key = True ) name = Column(String( 32 ), server_default = '', nullable = False ) username = Column(String( 32 ), index = True , server_default = '', nullable = False ) password = Column(String( 64 ), server_default = '', nullable = False ) def init_db(): BaseModel.metadata.create_all(Engine) # 创建数据库及表 def drop_db(): BaseModel.metadata.drop_all(Engine) # 删除数据库及表 if __name__ = = '__main__' : #init_db() drop_db() #BaseModel.metadata.tables['user'].create(Engine, checkfirst=True) #BaseModel.metadata.tables['user'].drop(Engine, checkfirst=False) pass |
二、向数据库添加数据
1 2 3 4 5 6 7 8 |
session = Session() session.add(User( id = uuid.uuid4(). hex )) session.add(Blog( id = uuid.uuid4(). hex )) session.add_all([ User( id = uuid.uuid4(). hex ), Blog( id = uuid.uuid4(). hex ) ]) session.commit() |
三、查询
查询的结果, 有几种不同的类型, 这个需要注意, 像是:
- instance
- instance of list
- keyed tuple of list
- value of list
1、普通查询
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
session.query(User).filter_by(username = 'abc' ). all () session.query(User). filter (User.username = = 'abc' ). all () session.query(Blog). filter (Blog.create > = 0 ). all () session.query(Blog). filter (Blog.create > = 0 ).first() session.query(Blog). filter (Blog.create > = 0 | Blog.title = = 'A' ).first() session.query(Blog). filter (Blog.create > = 0 & Blog.title = = 'A' ).first() session.query(Blog). filter (Blog.create > = 0 ).offset( 1 ).limit( 1 ).scalar() session.query(User). filter (User.username = = 'abc' ).scalar() session.query(User. id ). filter (User.username = = 'abc' ).scalar() session.query(Blog. id ). filter (Blog.create > = 0 ). all () session.query(Blog. id ). filter (Blog.create > = 0 ). all ()[ 0 ]. id dict (session.query(Blog. id , Blog.title). filter (Blog.create > = 0 ). all ()) session.query(Blog. id , Blog.title). filter (Blog.create > = 0 ).first().title session.query(User. id ).order_by( 'id desc' ). all () session.query(User. id ).order_by( 'id' ).first() session.query(User. id ).order_by(User. id ).first() session.query(User. id ).order_by( - User. id ).first() session.query( 'id' , 'username' ).select_from(User). all () session.query(User).get( '16e19a64d5874c308421e1a835b01c69' ) |
2、多表查询
1 2 3 4 5 |
session.query(Blog, User). filter (Blog.user = = User. id ).first().User.username session.query(Blog, User. id , User.username). filter (Blog.user = = User. id ).first(). id session.query(Blog. id , User. id , User.username). filter (Blog.user = = User. id ).first().keys() |
3、条件查询
1 2 3 4 5 6 7 8 9 |
from sqlalchemy import or_, not_ session.query(User). filter (or_(User. id = = '', User. id = = '16e19a64d5874c308421e1a835b01c69' )). all () session.query(User). filter (not_(User. id = = '16e19a64d5874c308421e1a835b01c69' )). all () session.query(User). filter (User. id .in_([ '16e19a64d5874c308421e1a835b01c69' ])). all () session.query(User). filter (User. id .like( '16e19a%' )). all () session.query(User). filter (User. id .startswith( '16e19a' )). all () dir (User. id ) |
4、函数
1 2 3 4 5 6 7 |
from sqlalchemy import func session.query(func.count( '1' )).select_from(User).scalar() session.query(func.count( '1' ), func. max (User.username)).select_from(User).first() session.query(func.count( '1' )).select_from(User).scalar() session.query(func.md5(User.username)).select_from(User). all () session.query(func.current_timestamp()).scalar() session.query(User).count() |
四、修改
两种修改方式
1 2 3 4 5 6 |
session.query(User). filter (User.username = = 'abc' ).update({ 'name' : '123' }) session.commit() user = session.query(User).filter_by(username = 'abc' ).scalar() user.name = '111' session.commit() |
如果涉及对属性原值的引用, 则要考虑 synchronize_session
这个参数.
'evaluate'
默认值, 会同时修改当前 session 中的对象属性.
'fetch'
修改前, 会先通过 select
查询条目的值.
‘False’
不修改当前 session 中的对象属性.
在默认情况下, 因为会有修改当前会话中的对象属性, 所以如果语句中有 SQL 函数, 或者"原值引用", 那是无法完成的操作, 自然也会报错, 比如:
1 2 3 |
from sqlalchemy import func session.query(User).update({User.name: func.trim( '123 ' )}) # 使用了函数 session.query(User).update({User.name: User.name + 'x' }) #使用了原值引用<br><br># 以上两种情况都会报错 |
这种情况下, 就不能要求 SQLAlchemy 修改当前 session 的对象属性了, 而是直接进行数据库的交互, 不管当前会话值(将synchronize_session值设置为False):
1 |
session.query(User).update({User.name: User.name + 'x' }, synchronize_session = False ) |
是否修改当前会话的对象属性, 涉及到当前会话的状态. 如果当前会话过期, 那么在获取相关对象的属性值时, SQLAlchemy 会自动作一次数据库查询, 以便获取正确的值:
1 2 3 4 5 6 |
user = session.query(User).filter_by(username = 'abc' ).scalar() print user.name session.query(User).update({User.name: 'new' }, synchronize_session = fetch) print user.name session.commit() print user.name |
执行了 update
之后, 虽然相关对象的实际的属性值已变更, 但是当前会话中的对象属性值并没有改变. 直到 session.commit()
之后, 当前会话变成"过期"状态, 再次获取 user.name
时, SQLAlchemy 通过 user
的 id
属性, 重新去数据库查询了新值.
synchronize_session
设置成 'fetch'
不会有这样的问题, 因为在做 update
时已经修改了当前会话中的对象了.
不管 synchronize_session
的行为如何, commit
之后 session
都会过期, 再次获取相关对象值时, 都会重新作一次查询.
好好体会上边的话。
五、删除
删除同样有像修改一样的 synchronize_session
参数的问题, 影响当前会话的状态.
1 2 3 4 |
session.query(User).filter_by(username = 'abc' ).delete() user = session.query(User).filter_by(username = 'abc' ).first() session.delete(user) |
六、join 操作
sqlalchemy 默认情况下的join 是内连接
1 2 3 |
r = session.query(Blog, User).join(User, Blog.user = = User. id ). all () for blog, user in r: print blog. id , blog.user, user. id |
四、外键和关系
1、外键的定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
from sqlalchemy import Column, ForeignKey from sqlalchemy.types import String, Integer, CHAR, BIGINT class Blog(BaseModel): __tablename__ = 'blog' id = Column(BIGINT, primary_key = True , autoincrement = True ) title = Column(String( 64 ), server_default = '', nullable = False ) text = Column(String, server_default = '', nullable = False ) user = Column(BIGINT, ForeignKey( 'user.id' ), index = True , nullable = False ) create = Column(BIGINT, index = True , server_default = '0' , nullable = False ) user_obj = relationship( 'User' ) class User(BaseModel): __tablename__ = 'user' id = Column(BIGINT, primary_key = True , autoincrement = True ) name = Column(String( 32 ), server_default = '', nullable = False ) username = Column(String( 32 ), index = True , server_default = '', nullable = True ) password = Column(String( 64 ), server_default = ' ', nullable=False) blog_list = relationship(' Blog ', order_by=' Blog.create') |
添加数据
1 2 3 4 5 6 7 |
session = Session() user = User(name = 'first' , username = u '新的' ) session.add(user) session.flush() blog = Blog(title = u '第一个' , user = user. id ) session.add(blog) session.commit() |
session.flush()
是进行数据库交互, 但是事务并没有提交. 进行数据库交互之后, user.id
才有值.
定义了外键, 对查询来说, 并没有影响. 外键只是单纯的一条约束而已. 当然, 可以在外键上定义一些关联的事件操作, 比如当外键条目被删除时, 字段置成 null
, 或者关联条目也被删除等
获取数据
1 2 3 |
session = Session() print session.query(Blog).get( 1 ).user_obj print session.query(User).get( 1 ).blog_list |
对于 一对多 的关系, 使用 any()
函数查询:
1 |
user = session.query(User). filter (User.blogs. any (Blog.title = = u 'A' )).first() |
反之, 如果是 多对一 的关系, 则使用 has()
函数查询:
1 |
blog = session.query(Blog). filter (Blog.user_obj.has(User.name = = u 'XX' )).first() |
上面的关系定义, 对应的属性是实际查询出的实例列表, 当条目数多的时候, 这样可能会有问题. 比如用户名下有成千上万的文章, 一次全取出就太暴力了. 关系对应的属性可以定义成一个 Query
1 2 3 4 5 6 7 |
class User(BaseModel): __tablename__ = 'user' id = Column(BIGINT, primary_key = True , autoincrement = True ) name = Column(String( 32 ), server_default = '', nullable = False ) blog_list = relationship( 'Blog' , order_by = 'Blog.create' , lazy = "dynamic" ) |
这样就能自由控制了
1 2 |
session.query(User).get( 1 ).blog_list. all () session.query(User).get( 1 ).blog_list. filter (Blog.title = = 'abc' ).first() |
2、关系的表现形式
关系在对象属性中的表现, 默认是列表, 但是, 这不是唯一的形式. 根据需要, 可以作成 dictionary , set 或者其它你需要的对象.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
class Blog(BaseModel): __tablename__ = 'blog' id = Column(Integer, autoincrement = True , primary_key = True ) title = Column( Unicode ( 32 ), server_default = '') user = Column(Integer, ForeignKey( 'user.id' ), index = True ) user_obj = relationship( 'User' ) class User(BaseModel): __tablename__ = 'user' id = Column(Integer, autoincrement = True , primary_key = True ) name = Column( Unicode ( 32 ), server_default = '') blogs = relationship( 'Blog' ) |
对于上面的两个模型:
1 2 |
user = session.query(User).first() print user.blogs |
1 2 3 4 5 6 |
user = User(name = u 'XX' ) session.add_all([Blog(title = u 'A' , user_obj = user), Blog(title = u 'B' , user_obj = user)]) session.commit() user = session.query(User).first() print user.blogs |
现在 user.blogs
是一个列表. 我们可以在 relationship()
调用时通过 collection_class
参数指定一个类, 来重新定义关系的表现形式:
set, 集合
1 2 3 |
blogs = relationship( 'Blog' , collection_class = set ) #InstrumentedSet([<__main__.Blog object at 0x1a58710>, <__main__.Blog object at 0x1a587d0>]) |
attribute_mapped_collection , 字典, 键值从属性取:
1 2 3 4 5 |
from sqlalchemy.orm.collections import attribute_mapped_collection blogs = relationship( 'Blog' , collection_class = attribute_mapped_collection( 'title' )) #{u'A': <__main__.Blog object at 0x20ed810>, u'B': <__main__.Blog object at 0x20ed8d0>} |
mapped_collection , 字典, 键值自定义:
1 2 3 4 5 |
from sqlalchemy.orm.collections import mapped_collection blogs = relationship( 'Blog' , collection_class = mapped_collection( lambda blog: blog.title.lower())) #{u'a': <__main__.Blog object at 0x1de4890>, u'b': <__main__.Blog object at 0x1de4950>} |
出处:https://www.cnblogs.com/9527chu/p/5713191.html