一包框架 - 敏捷,易於整合Sqlalchemy Orm。
受django orm和雄辯的ORM的啟發
易於與您現有項目(如Fastapi)集成:
from sqlalchemy_mixins import BaseMixin
class User ( Base , BaseMixin ):
pass 使用PIP
pip install SqlalchemyMixin
這是我們混合物可以做什麼的快速演示。
bob = User . create ( name = 'Bob' )
post1 = Post . create ( body = 'Post 1' , user = bob , rating = 3 )
post2 = Post . create ( body = 'long-long-long-long-long body' , rating = 2 ,
user = User . create ( name = 'Bill' ),
comments = [ Comment . create ( body = 'cool!' , user = bob )])
# filter using operators like 'in' and 'contains' and relations like 'user'
# will output this beauty: <Post #1 body:'Post1' user:'Bill'>
print ( Post . where ( rating__in = [ 2 , 3 , 4 ], user___name__like = '%Bi%' ). all ())
# joinedload post and user
print ( Comment . with_joined ( 'user' , 'post' , 'post.comments' ). first ())
# subqueryload posts and their comments
print ( User . with_subquery ( 'posts' , 'posts.comments' ). first ())
# sort by rating DESC, user name ASC
print ( Post . sort ( '-rating' , 'user___name' ). all ())
# created_at, updated_at timestamps added automatically
print ( "Created Bob at " , bob . created_at )
# serialize to dict, with relationships import sqlalchemy as sa
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy_mixins import BaseMixin
app = Flask ( __name__ )
app . config [ 'SQLALCHEMY_DATABASE_URI' ] = 'sqlite://'
db = SQLAlchemy ( app )
######### Models #########
class BaseModel ( db . Model , BaseMixin ):
__abstract__ = True
pass
class User ( BaseModel ):
name = sa . Column ( sa . String )
######## Initialize ########
BaseModel . set_session ( db . session )
######## Create test entity ########
db . create_all ()
user = User . create ( name = 'bob' )
print ( user )該庫依賴於SQLalchemy的autocommit標誌。初始化會話IE時,需要將其設置為true:
session = scoped_session ( sessionmaker ( bind = engine , autocommit = True ))
BaseModel . set_session ( session )或與Flask-SQLAlchemy
db = SQLAlchemy ( app , session_options = { 'autocommit' : True })主要功能是
由ActiveRecordMixin提供
Sqlalchemy的數據映射器模式很酷,但是主動記錄模式最簡單,更乾燥。
好吧,我們在數據映射器之上實現了它!我們需要的只是在引導我們的應用程序時僅將會話注入ORM類:
BaseModel . set_session ( session )
# now we have access to BaseOrmModel.session property我們都喜歡Sqlalchemy,但是在那裡做crud有點棘手。
例如,創建對象需要3行代碼:
bob = User ( name = 'Bobby' , age = 1 )
session . add ( bob )
session . flush ()好吧,有了從模型中訪問會話,我們可以寫
bob = User . create ( name = 'Bobby' , age = 1 )這就是在Django Orm和Peewee中完成的方式
還提供了更新和刪除方法
bob . update ( name = 'Bob' , age = 21 )
bob . delete ()而且,就像在Django和雄辯中一樣,我們可以通過ID快速檢索對象
User . get ( 1 ) # instead of session.query(User).get(1)如果這種ID不存在,則失敗
User . get_or_abort ( 123987 ) # will raise sqlalchemy_mixins.ModelNotFoundError就像在燒瓶中,peewee和django orm一樣,您可以快速查詢一些課程
User . query # instead of session.query(User)另外,我們可以首先檢索或所有對象:
User . first () # instead of session.query(User).first()
User . all () # instead of session.query(User).all() 由EagerLoadMixin提供
如果您使用Sqlalchemy的急切加載,您可能會發現它不是很方便,尤其是當我們想要(例如,加載用戶),他的所有帖子和評論時,他在同一查詢中對他的每個帖子進行評論。
好吧,現在您可以輕鬆地設置要渴望負載的ORM關係
User . with_ ({
'posts' : {
'comments' : {
'user' : JOINED
}
}
}). all ()或者我們可以編寫類屬性而不是字符串:
User . with_ ({
User . posts : {
Post . comments : {
Comment . user : JOINED
}
}
}). all ()有時我們想在單獨的查詢中加載關係,即進行子查詢。例如,我們像這樣加載頁面上的帖子,對於每篇文章,我們都希望擁有用戶和所有註釋(以及評論作者)。
為了加快查詢的速度,我們將註釋加載在單獨的查詢中,但是,在此單獨的查詢中,加入用戶
from sqlalchemy_mixins import JOINED , SUBQUERY
Post . with_ ({
'user' : JOINED , # joinedload user
'comments' : ( SUBQUERY , { # load comments in separate query
'user' : JOINED # but, in this separate query, join user
})
}). all ()在這裡,將在第一個查詢上加載帖子,並在第二個查詢中對用戶發表評論。有關解釋關係加載技術的解釋,請參見SQLalchemy文檔。
對於簡單的情況,當您只想加入Load或subqueryload幾個關係時,我們為您提供了更容易的語法:
Comment . with_joined ( 'user' , 'post' , 'post.comments' ). first ()
User . with_subquery ( 'posts' , 'posts.comments' ). all ()請注意,由於此
post.comments功能
由SmartQueryMixin提供
我們實現了類似Django的現場查找和自動關係連接。
這意味著您可以通過字符串中定義的屬性動態過濾和分類!
因此,在使用Post.user與User模型的關係定義了Post模型後,您可以寫作
Post . where ( rating__gt = 2 , user___name__like = '%Bi%' ). all () # post rating > 2 and post user name like ...
Post . sort ( '-rating' , 'user___name' ). all () # sort by rating DESC, user name ASC ( ___分割關係和屬性, __拆分屬性和操作員)
如果需要更大的靈活性,則可以使用低級
filter_exprmethodsession.query(Post).filter(*Post.filter_expr(rating__gt=2, body='text')),請參見示例。它就像sqlalchemy中的
filter_by一樣,但也允許魔術運算符等rating__gt。注意:
filter_expr方法非常低級別,並且不會像魔術Django一樣連接。為此使用smart_query。
過濾/排序中使用的所有關係都應明確設置
在我們的示例中,即使
User.posts也定義了Post.user關係,也應在Post類中定義。所以,你不能輸入
class User ( BaseModel ): # ... user = sa . orm . relationship ( 'User' , backref = 'posts' )並跳過定義
Post.user關係。您無論如何都必須定義它:class Post ( BaseModel ): # ... user = sa . orm . relationship ( 'User' ) # define it anyway
為了乾燥您的代碼和不覆蓋業務邏輯,您可以使用SQLalchemy的混合屬性和Hybrid_Methods。在我們的過濾/排序中使用它們很簡單(請參見示例和測試)。
好吧,正如SmartQueryMixin確實可以自動進行過濾/排序一樣,有一種感覺可以告訴Sqlalchemy,我們已經加入了這種關係。
因此,如果使用用於過濾/排序,則將自動設置為加入負載。
所以,如果我們寫
comments = Comment . where ( post___public = True , post___user___name__like = 'Bi%' ). all ()如果我們將訪問二手關係,則不會執行其他查詢
comments [ 0 ]. post
comments [ 0 ]. post . user由SmartQueryMixin提供
在現實世界中,我們希望一次過濾,分類和渴望立即加載一些關係。好吧,如果我們使用相同的User.posts關係在過濾和排序中,則不應將其連接兩次。
這就是為什麼我們以一種最聰明的方法將過濾器,分類和急切的負載組合在一起:
Comment . smart_query (
filters = {
'post___public' : True ,
'user__isnull' : False
},
sort_attrs = [ 'user___name' , '-created_at' ],
schema = {
'post' : {
'user' : JOINED
}
}). all ()作為開發人員,我們需要方便地調試事情。當我們在替補中播放時,我們可以看到這個
>>> session.query(Post).all()
[<myapp.models.Post object at 0x04287A50>, <myapp.models.Post object at 0x04287A90>]
好吧,使用我們的Mixin,我們可以使用POST ID具有更可讀的輸出:
>>> session.query(Post).all()
[<Post #11>, <Post #12>]
更重要的是,在Post模型中,我們可以定義我們想看到的其他內容(ID):
class User ( BaseModel ):
__repr_attrs__ = [ 'name' ]
# ...
class Post ( BaseModel ):
__repr_attrs__ = [ 'user' , 'body' ] # body is just column, user is relationship
# ...現在我們有
>>> session.query(Post).all()
[<Post #11 user:<User #1 'Bill'> body:'post 11'>,
<Post #12 user:<User #2 'Bob'> body:'post 12'>]
您可以自定義最大__repr__長度:
class Post(BaseModel):
# ...
__repr_max_length__ = 25
# ...
>>> long_post
<Post #2 body:'Post 2 long-long body' user:<User #1 'Bob'>>
由DateMixin提供
您可以查看創建和更新的時間戳。
bob = User ( name = "Bob" )
session . add ( bob )
session . flush ()
print ( "Created Bob: " , bob . created_at )
# Created Bob: 2019-03-04 03:53:53.606765
print ( "Pre-update Bob: " , bob . updated_at )
# Pre-update Bob: 2019-03-04 03:53:53.606769
time . sleep ( 2 )
bob . name = "Robert"
session . commit ()
print ( "Updated Bob: " , bob . updated_at )
# Updated Bob: 2019-03-04 03:53:58.613044