Sqlalchemy ormには、フレームワークに依存し、統合しやすいパック。
Django OrmとEloquent 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フラグに依存しています。セッションを初期化するときは、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やEloquentのように、IDでオブジェクトをすばやく取得できます
User . get ( 1 ) # instead of session.query(User).get(1)そのようなIDが存在しない場合は失敗します
User . get_or_abort ( 123987 ) # will raise sqlalchemy_mixins.ModelNotFoundErrorFlask-Sqlalchemy、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 ()関係を個別にロードしたい場合があります。つまり、subqueryloadを行います。たとえば、このようなページに投稿をロードし、各投稿にユーザーとすべてのコメント(およびコメント著者)が必要です。
クエリをスピードアップするには、コメントを個別のクエリにロードしますが、この個別のクエリでは、ユーザーに参加します
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 ()ここでは、投稿は最初のクエリにロードされ、ユーザーとのコメント - 2番目のクエリにはコメントがあります。関係の負荷技術を説明するためのsqlalchemy docsを参照してください。
簡単な場合、いくつかの関係に参加またはサブクエリロードしたい場合、簡単な構文があります。
Comment . with_joined ( 'user' , 'post' , 'post.comments' ). first ()
User . with_subquery ( 'posts' , 'posts.comments' ). all ()このsqlalchemy機能のために、
post.commentsのようなDOTとの関係を分割できることに注意してください
SmartQueryMixinによって提供されます
Djangoのようなフィールドルックアップと自動関係結合を実装します。
つまり、文字列で定義された属性によって動的にフィルタリングおよびソートできることを意味します!
したがって、 UserモデルとのPost.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_exprメソッドsession.query(Post).filter(*Post.filter_expr(rating__gt=2, body='text'))使用できます。例を参照してください。Sqlalchemyの
filter_byのようなものですが、rating__gtなどの魔法のオペレーターも許可します。注:
filter_exprメソッドは非常に低レベルであり、魔法のジュンゴのような結合を行いません。そのためにsmart_queryを使用します。
フィルタリング/ソートで使用されるすべての関係は、単に逆ではなく、明示的に設定する必要があります
この例では、
Post.userUser.postsされていても、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関係をフィルタリングとソートで使用する場合、 2回結合しないでください。
そのため、最も賢い方法でフィルター、ソート、熱心な負荷を組み合わせました。
Comment . smart_query (
filters = {
'post___public' : True ,
'user__isnull' : False
},
sort_attrs = [ 'user___name' , '-created_at' ],
schema = {
'post' : {
'user' : JOINED
}
}). all ()開発者として、私たちは利便性をもって物事をデバッグする必要があります。 REPLでプレイするとき、これを見ることができます
>>> session.query(Post).all()
[<myapp.models.Post object at 0x04287A50>, <myapp.models.Post object at 0x04287A90>]
まあ、私たちのMixinを使用して、ポスト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'>]
また、Max __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