###建立 from sqlalchemy import create_engine from sqlalchemy.orm import sessionmakerhtml
engine = create_engine('sqlite:///:memory:', echo=False) Session = sessionmaker(bind=engine) session = Session()
echo
負責設定是否輸出生成的 SQL 語句。mysql
create_engine
返回的是一個 Engine 對象,它負責對接 DBAPI。Session
對象會更多被直接使用。sql
session 某種意義上相似於一個緩存,他有 commit, rollback, close
等方法。也所以 session 實例的生命週期維護應該位於使用它們的函數以外,與具體的應用邏輯區分開,以免數據污染和保證 rollback/close 的調用。如:-- 參考數據庫
from contextlib import contextmanager @contextmanager def session_scope(): """Provide a transactional scope around a series of operations.""" session = Session() try: yield session session.commit() except: session.rollback() raise finally: session.close() def run_my_program(): with session_scope() as session: ThingOne().go(session) ThingTwo().go(session)
###同步 值得注意的是,在同一個 session 內,不一樣 Instance 是共享一份數據緩存的,即:緩存
>>> s = Session() >>> foo = s.query(Item).get(9) >>> bar = s.query(Item).get(9) >>> foo.amount 1 >>> foo.amount += 1 >>> bar.amount 2
既然 session 起的是一個緩存的做用,就意味着他的數據與數據庫之間並不能保持多高的一致性。session
當咱們須要刷新數據時,可使用 refesh()
方法:分佈式
>>> foo.amount 1 >>> s.refresh(foo) >>> s.commit() >>> foo.amount 0
注意記得提交。commit()
方法是向數據庫提交請求的最後一步確認,在它被調用以前,數據都不會獲得更新。包括全部被 flush()
的請求,雖然他們已經被傳入數據庫了。ide
在 commit()
以前,這些請求都還能夠經過 rollback()
取消掉。函數
###自增(減)原子操做 對某些數據一致性要求較高的字段,在不考慮分佈式存儲的前提下,一種有效的手段是使用嵌入的 SQL 子句來賦值。code
mysql> update item set amount=amount-1 where id=9;
這能夠有效避免由於多 session 同時操做同一對象時,使用字面量賦值可能引起的一致性風險。
這種操做的 orm 版語法是:
foo.amount = foo.__table__.c.amount - 1
這裏引用了 foo.__table__
屬性,它是 orm 映射中的 Table
對象,上面的語句在提交時就會生成一個嵌入的 SQL 子句來進行賦值。
#MAP
from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String Base = declarative_base() class User(Base): __tablename__ = 'user' id = Column(Integer, primary_key=True) name = Column(String)
從 Python 類到 sql 表,須要一套映射邏輯,sqlalchemy 提供了一套默認的解決方案,就是 declarative_base
。
這裏單獨的一行 Base=declarative_base()
表示了你能夠有不少種方式自定義基類,好比繼承 Base
,或者給 declarative_base(**kwargs)
傳參。-- 參考
至於 User 類,因爲 sqlalchemy 默認不會去作任何推導,你須要顯示指定 __tablename__
參數和至少一個主鍵。
Integer
類型沒有指定長度,是由於這種修飾參數僅在建表時有用。
tim = User(name='tim') session.add(tim) session.flush()
sqlalchemy 對待請求的處理方式是惰性的,他只在必要的時候才訪問數據庫。必要的一種定義是 query:
session.query(User).filter_by(name='tim')
當即同步到數據庫的方式是使用 .flush()
方法。
另一個很是有趣的特性是,sqlalchemy 可以在同一個 session 中分別出表記錄的惟一性。即:
>>> tim is session.query(User).filter_by(name='tim') >>> True
對於同一條數據庫記錄,不論是你新建的,仍是屢次從庫中取出的,他們的 Python id 都是同樣的。這種區分就是前面提到的 映射聲明必須提供主鍵 所帶來的一個好處。
query()
的參數能夠不少變,能夠是一個 Model 類,或他的某些屬性的組合:
foo = session.query(User, User.id).filter(User.name=='tim').first()
這種 query 方式返回的元組對象都是關鍵字命名的,並且支持屬性式訪問,如:
>>> foo.User.id == foo.id >>> True
若是想起別名,可使用 .label()
方法:
>>> foo = session.query(User.id.label('user_id')).filter_by(name='tim') >>> foo.user_id >>> 1
對於 LIMIT 和 OFFSET 參數,能夠直接對 Query 對象使用 Python 切片操做。
session.query(User)[20:10]
最後,Query 對象是可迭代的。儘管上面的例子爲了偷懶沒有使用過迭代方法,但它是支持的!
###filter filter()
較於 filter_by()
是一種更通用的方法,支持全部的 sql 表達式。filter() 返回的仍然是一個 Query 對象,因此你能夠連續調用 filter()
方法,或者圖省事把複合查詢語句放進同一個 filter() 裏
session.query(User).filter(id>1, name=='tim') session.query(User).filter(id>1).filter(name=='tim')
一些經常使用的表達式語句:
User.name == 'tim'
User.name != 'tim'
User.name.like('tim')
User.name.in_(['tim', 'White'])
~User.name.in_(['tim', 'white'])
User.name == None
or User.name.is_(None)
User.name != None
or User.name.isnot(None)
from sqlalchemy import or_ /// filter(or_(expr1, expr2))
User.name.match('tim')
###order_by session.query(User).order_by(User.id.desc())
or
from sqlalchemy import desc, asc session.query(User).order_by(asc(User.id))
Query 對象雖然是可迭代的,但對於多數肯定需求的情況,咱們可能更但願直接把須要的數據取出來。
all()
和 first()
顧名思義,特別的是 one()
。
它較於 first() 多了一條約束,即Query 對象的結果集必須只包含一個對象,不然報錯,即:
session.query(User).filter(name=='tim').one()
等價於:
foo = session.quert(User).filter(name='tim') assert len(foo.all()) == 1 return foo.first()
###SQL 在如 filter()
或 order_by()
這樣的方法中,條件表達式還能夠是原始的 sql 語句,只須要用 text()
把語句包起來:
from sqlalchemy import text users = session.query(User).filter(text('id<10')).order_by(text('id'))
另外一種更完全的 sql 執行方式是使用 from_statement()
方法:
session.query(User).from_statement(text('SELECT * FROM user where id<10 and status=1')).all()
此時還能夠直接 Query 列名:
>>> session.query('id', 'name', 'status').from_statement('SELECT * from user where class=3').all() >>> [(1, 'jack', 0), (2, 'rose', 1)]
###counting 一種簡單的計數是對 Query 對象使用 count()
方法,用於返回 Query 對象的行數。
另外一種較複雜的是 func.count()
,它支持細分計數:
from sqlalchemy import func session.query(func.count(User.class), User.class).filter_by(User.class='a') session.query(func.count(User.class), User.class).group_by(User.class).all()
###關係 略