SQLAlchemy ORM 參考

#Session

###建立 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 類型沒有指定長度,是由於這種修飾參數僅在建表時有用。

#Query

new

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 對象

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))

all(), one(), first()

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()

###關係 略

相關文章
相關標籤/搜索