一包框架 - 敏捷,易于整合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