Um pacote de estrutura-estrutura, fácil de integrar para o SQLalChemy ORM.
Fortemente inspirado por Django Orm e eloquente ORM
Fácil integração ao seu projeto existente como o FASTAPI:
from sqlalchemy_mixins import BaseMixin
class User ( Base , BaseMixin ):
pass Use pip
pip install SqlalchemyMixin
Aqui está uma demonstração rápida do que nossos mixins podem fazer.
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 ) Esta biblioteca conta com o sinalizador autocommit da SQLalChemy. Ele precisa ser definido como verdadeiro ao inicializar a sessão, ou seja:
session = scoped_session ( sessionmaker ( bind = engine , autocommit = True ))
BaseModel . set_session ( session ) ou com Flask-SQLAlchemy
db = SQLAlchemy ( app , session_options = { 'autocommit' : True })Os principais recursos são
Fornecido por ActiveRecordMixin
O padrão de mapeador de dados da Sqlalchemy é legal, mas o padrão de registro ativo é mais fácil e seco.
Bem, nós o implementamos no topo do Data Mapper! Tudo o que precisamos é apenas injetar sessão na classe ORM enquanto bootstrapping nosso aplicativo:
BaseModel . set_session ( session )
# now we have access to BaseOrmModel.session propertyTodos nós amamos Sqlalchemy, mas fazer Crud é um pouco complicado lá.
Por exemplo, criar um objeto precisa de 3 linhas de código:
bob = User ( name = 'Bobby' , age = 1 )
session . add ( bob )
session . flush ()Bem, tendo acesso à sessão do modelo, podemos apenas escrever
bob = User . create ( name = 'Bobby' , age = 1 )É assim que é feito em Django Orm e Peewee
Os métodos de atualização e exclusão também são fornecidos
bob . update ( name = 'Bob' , age = 21 )
bob . delete ()E, como em django e eloquente, podemos recuperar rapidamente o objeto por id
User . get ( 1 ) # instead of session.query(User).get(1)e falhar se esse id não existir
User . get_or_abort ( 123987 ) # will raise sqlalchemy_mixins.ModelNotFoundErrorComo em Flask-Sqlalchemy, Peewee e Django Orm, você pode consultar rapidamente alguma aula
User . query # instead of session.query(User)Além disso, podemos recuperar rapidamente os objetos primeiro ou em todos os objetos:
User . first () # instead of session.query(User).first()
User . all () # instead of session.query(User).all() Fornecido por EagerLoadMixin
Se você usar o carregamento ansioso da Sqlalchemy, poderá achar que não é muito conveniente, especialmente quando queremos, digamos, carregar o usuário, todas as suas postagens e comentários para cada postagem dele na mesma consulta.
Bem, agora você pode definir facilmente as relações ORM que deseja carregar
User . with_ ({
'posts' : {
'comments' : {
'user' : JOINED
}
}
}). all ()Ou podemos escrever propriedades de classe em vez de cordas:
User . with_ ({
User . posts : {
Post . comments : {
Comment . user : JOINED
}
}
}). all ()Às vezes, queremos carregar relações em consulta separada, ou seja, subqueryload. Por exemplo, carregamos postagens em página como esta e, para cada postagem, queremos ter usuário e todos os comentários (e comentar autores).
Para acelerar a consulta, carregamos comentários em consulta separada, mas, nessa consulta separada, junte -se ao usuário
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 ()Aqui, as postagens serão carregadas na primeira consulta e comentários com usuários - no segundo. Consulte os documentos da SQLalChemy para explicar as técnicas de carregamento de relacionamento.
Para casos simples, quando você deseja apenas se juntar à carga ou subqueryload algumas relações, temos uma sintaxe mais fácil para você:
Comment . with_joined ( 'user' , 'post' , 'post.comments' ). first ()
User . with_subquery ( 'posts' , 'posts.comments' ). all ()Observe que você pode dividir as relações com o DOT como
post.commentsdevido a esse recurso SQLalChemy
Fornecido por SmartQueryMixin
Implementamos pesquisas de campo semelhantes a django e conexões automáticas.
Isso significa que você pode filtrar e classificar dinamicamente por atributos definidos em strings!
Então, depois de definir o modelo Post com o relacionamento Post.user com o modelo User , você pode escrever
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 ( ___ divide a relação e atributo, __ Splits Attribute and Operator)
Se você precisar de mais flexibilidade, pode usar o método
filter_exprde baixo nível.Querysession.query(Post).filter(*Post.filter_expr(rating__gt=2, body='text')), consulte o exemplo.É como
filter_byno sqlalchemy, mas também permite operadores mágicos comorating__gt.NOTA: O método
filter_expré muito baixo e não faz junções mágicas do tipo django. Usesmart_querypara isso.
Todas as relações usadas na filtragem/classificação devem ser explicitamente definidas , não apenas sendo um backref
Em nosso exemplo, o relacionamento
Post.userdeve ser definido naPost-classe, mesmo queUser.poststambém sejam definidos.Então, você não pode digitar
class User ( BaseModel ): # ... user = sa . orm . relationship ( 'User' , backref = 'posts' )e pular o relacionamento
Post.user. Você deve defini -lo de qualquer maneira:class Post ( BaseModel ): # ... user = sa . orm . relationship ( 'User' ) # define it anyway
Para secar o seu código e incapsular a lógica de negócios, você pode usar os atributos híbridos da SQLalChemy e hybrid_methods. Usá -los em nossa filtragem/classificação é simples (consulte Exemplos e testes).
Bem, como SmartQueryMixin faz auto-joins para filtrar/classificar, há um senso de dizer à SQLalChemy que já nos juntamos a essa relação.
Para que as relações sejam automaticamente definidas para serem unidas se forem usadas para filtragem/classificação.
Então, se escrevermos
comments = Comment . where ( post___public = True , post___user___name__like = 'Bi%' ). all ()então nenhuma consulta adicional será executada se acessarmos as relações usadas
comments [ 0 ]. post
comments [ 0 ]. post . user Fornecido por SmartQueryMixin
No mundo real, queremos filtrar, classificar e também carregar algumas relações de uma só vez. Bem, se usarmos o mesmo, digamos, User.posts Relation in Filtering and Strating, ela não deve ser unida duas vezes .
É por isso que combinamos filtros, classificamos e ansiosos em um método mais inteligente:
Comment . smart_query (
filters = {
'post___public' : True ,
'user__isnull' : False
},
sort_attrs = [ 'user___name' , '-created_at' ],
schema = {
'post' : {
'user' : JOINED
}
}). all ()Como desenvolvedores, precisamos depurar as coisas com conveniência. Quando jogamos em Repl, podemos ver isso
>>> session.query(Post).all()
[<myapp.models.Post object at 0x04287A50>, <myapp.models.Post object at 0x04287A90>]
Bem, usando o mixin, podemos ter uma saída mais legível com IDs postais:
>>> session.query(Post).all()
[<Post #11>, <Post #12>]
Ainda mais, no modelo Post , podemos definir o que mais (exceto ID) queremos ver:
class User ( BaseModel ):
__repr_attrs__ = [ 'name' ]
# ...
class Post ( BaseModel ):
__repr_attrs__ = [ 'user' , 'body' ] # body is just column, user is relationship
# ...Agora temos
>>> session.query(Post).all()
[<Post #11 user:<User #1 'Bill'> body:'post 11'>,
<Post #12 user:<User #2 'Bob'> body:'post 12'>]
E você pode personalizar o comprimento máximo __repr__ :
class Post(BaseModel):
# ...
__repr_max_length__ = 25
# ...
>>> long_post
<Post #2 body:'Post 2 long-long body' user:<User #1 'Bob'>>
Fornecido por DateMixin
Você pode visualizar os registros de data e hora criados e atualizados.
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