sqlalchemy 是一款Python語言寫的ORM框架, 該框架創建在數據庫API基礎之上。python
sqlalchemy 自己沒法操做數據庫,必須已第三方插件爲基礎,Dialect用於和數據API進行交流,根據不通的的配置調用不通的數據庫API,從而實現對數據庫的操做。mysql
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...]
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:123@127.0.0.1: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
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()
查詢的結果, 有幾種不一樣的類型, 這個須要注意, 像是:sql
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')
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()
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)
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()
兩種修改方式數據庫
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
這個參數.session
'evaluate'
默認值, 會同時修改當前 session 中的對象屬性.'fetch'
修改前, 會先經過 select
查詢條目的值.‘False’
不修改當前 session 中的對象屬性.在默認狀況下, 由於會有修改當前會話中的對象屬性, 因此若是語句中有 SQL 函數, 或者"原值引用", 那是沒法完成的操做, 天然也會報錯, 好比:oracle
from sqlalchemy import func session.query(User).update({User.name: func.trim('123 ')}) # 使用了函數 session.query(User).update({User.name: User.name + 'x'}) #使用了原值引用
# 以上兩種狀況都會報錯
這種狀況下, 就不能要求 SQLAlchemy 修改當前 session 的對象屬性了, 而是直接進行數據庫的交互, 無論當前會話值(將synchronize_session值設置爲False):app
session.query(User).update({User.name: User.name + 'x'}, synchronize_session=False)
是否修改當前會話的對象屬性, 涉及到當前會話的狀態. 若是當前會話過時, 那麼在獲取相關對象的屬性值時, SQLAlchemy 會自動做一次數據庫查詢, 以便獲取正確的值:框架
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
時已經修改了當前會話中的對象了.fetch
無論 synchronize_session
的行爲如何, commit
以後 session
都會過時, 再次獲取相關對象值時, 都會從新做一次查詢.
好好體會上邊的話。
刪除一樣有像修改同樣的 synchronize_session
參數的問題, 影響當前會話的狀態.
session.query(User).filter_by(username='abc').delete() user = session.query(User).filter_by(username='abc').first() session.delete(user)
sqlalchemy 默認狀況下的join 是內鏈接
r = session.query(Blog, User).join(User, Blog.user == User.id).all() for blog, user in r: print blog.id, blog.user, user.id
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')
添加數據
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
, 或者關聯條目也被刪除等
獲取數據
session = Session() print session.query(Blog).get(1).user_obj print session.query(User).get(1).blog_list
對於 一對多 的關係, 使用 any()
函數查詢:
user = session.query(User).filter(User.blogs.any(Blog.title == u'A')).first()
反之, 若是是 多對一 的關係, 則使用 has()
函數查詢:
blog = session.query(Blog).filter(Blog.user_obj.has(User.name == u'XX')).first()
上面的關係定義, 對應的屬性是實際查詢出的實例列表, 當條目數多的時候, 這樣可能會有問題. 好比用戶名下有成千上萬的文章, 一次全取出就太暴力了. 關係對應的屬性能夠定義成一個 Query
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")
這樣就能自由控制了
session.query(User).get(1).blog_list.all() session.query(User).get(1).blog_list.filter(Blog.title == 'abc').first()
二、關係的表現形式
關係在對象屬性中的表現, 默認是列表, 可是, 這不是惟一的形式. 根據須要, 能夠做成 dictionary , set 或者其它你須要的對象.
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')
對於上面的兩個模型:
user = session.query(User).first() print user.blogs
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, 集合
blogs = relationship('Blog', collection_class=set) #InstrumentedSet([<__main__.Blog object at 0x1a58710>, <__main__.Blog object at 0x1a587d0>])
attribute_mapped_collection , 字典, 鍵值從屬性取:
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 , 字典, 鍵值自定義:
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>}