Code

tiforadacaixa

Tutorial completo SQLALCHEMY em portugues

Tradução da documentação disponivel em: https://docs.sqlalchemy.org/en/13/orm/tutorial.html


Pra quem ja estudou django, o funcionamento do SqlAlchemy é muito similar ao dos models do django.

Desenvolvido para a linguagem de programação Python, o SQLAlchemy é um framework de mapeamento objeto-relacional SQL (ORM) de código aberto sobre a licença MIT.

Mas afinal, o que é o ORM?

Mapeamento objeto-relacional ou simplesmente ORM é uma técnica de programação que auxilia na conversão de dados entre banco de dados relacionais e linguagens de programação que são orientadas à objetos.
A utilização deste Framework faz com que o programador reduza a programação de acesso ao banco de dados, desta forma, obtendo uma produtividade significativa no desenvolvimento de suas aplicações.
Ou seja, ORM trata-se de um Framework que visa auxiliar na redução da impedância, realizando todas as conversões necessárias entre o modelo relacional e o modelo orientado a objetos de maneira automática, geralmente da seguinte forma:
  • Cada classe é interpretada com uma tabela;
  • Cada linha de uma determinada tabela, junto com seu relacionamento é tratada como instância do objeto relacionado à tabela em questão.
Sua utilização também visa retirar a necessidade do desenvolvedor de software em se preocupar com a linguagem SQL (geração dos comandos SQL) e com as conversões necessárias entre os diferentes tipos de dados, já que tudo isto fica a cargo do Framework ORM.
Conseguimos utilizar banco de dados relacionais por meio de objetos escritos em Python, não se preocupando em como este BD está funcionando (relações, campos, etc), apenas mapeando sua estrutura.

Checar a versão

Uma verificação rapida pode ser feita com os seguintes comandos no console python:
>>> import sqlalchemy
>>> sqlalchemy.__version__ 
1.3.0

Conectando

Para esse tutorial nos iremos usar apenas uma base de dados SQLite na memoria ram (temporaria)
para se conectar a essa base temporaria iremos usar o comando create_engine():
>>> from sqlalchemy import create_engine
>>> engine = create_engine('sqlite:///:memory:', echo=True)
O parametro echo  é um atalho para definir o log do SqlAlchemy, que é realizado atraves do modulo padrão do Python logging .Com isso ativado, veremos todo o SQL gerado produzido. Se você estiver trabalhando neste tutorial e desejar gerar menos resultados, defina esse parametro echo = False.
o valor de returno do comando create_engine()  é uma instancia da  Engine, e isso representa uma interface de uso da base de dados, adaptado através de um dialeto que lida com os detalhes do banco de dados e da DBAPI em uso. Nesse caso, o dialeto SQLite interpretará instruções para o módulo sqlite3 interno do Python.
A primeira vez que um método como Engine.execute() ou Engine.connect() é executado a Engine estabelece uma conexão DBAPI real com a database,  que é então usado para emitir os comandos SQL.
Ao usar o ORM, normalmente não usamos a Engine que criamos direntamente; em vez disso, é usado no backend pelo ORM, como veremos em breve .
Veja tambem:
Database Urls - incluem exemplos de uso do comando create_engine() conectando-se a vários tipos de bancos de dados (com links para mais informações).

Declarar um mapeamento


Ao usar o ORM, o processo de configuração começa descrevendo as tabelas de banco de dados com as quais lidaremos e depois definindo nossas próprias classes que serão mapeadas para essas tabelas. No SQLAlchemy moderno, essas duas tarefas geralmente são executadas juntas, usando um sistema conhecido como Declarativo, que permite criar classes que incluem diretivas para descrever a tabela de banco de dados real para a qual eles serão mapeados.Classes mapeadas usando o sistema Declarative são definidas em termos de uma classe base que mantém um catálogo de classes e tabelas relativas a essa base - isso é conhecido como classe base declarativa. Nosso aplicativo geralmente terá apenas uma instância dessa base em um módulo comumente importado. Criamos a classe base usando a função declarative_base()  da seguinte maneira:
>>> from sqlalchemy.ext.declarative import declarative_base

>>> Base = declarative_base()

Agora que temos uma "base", podemos definir qualquer número de classes mapeadas em termos dela. Começaremos com apenas uma única tabela chamada usuarios, que armazenará registros para os usuários finais usando nosso aplicativo. Uma nova classe chamada Usuario será a classe para a qual mapeamos esta tabela. Dentro da classe, definimos detalhes sobre a tabela para a qual mapearemos, principalmente o nome da tabela e os nomes e tipos de dados das colunas:

>>> from sqlalchemy import Column, Integer, String
>>> class Usuario(Base):
...     __tablename__ = 'users'
...
...     id = Column(Integer, primary_key=True)
...     nome = Column(String)
...     sobrenome = Column(String)
...     apelido = Column(String)
...
...     def __repr__(self):
...        return "<Usuario(nome='%s', sobrenome='%s', apelido='%s')>" % (
...                             self.nome, self.sobrenome, self.apelido)

Uma classe usando a forma declarativa precisa ter no minimo um atributo  __tablename__, e pelo menos uma coluna Column que faz parte de uma chave primária 1.O SQLAlchemy nunca faz nenhuma suposição por si só sobre a tabela à qual uma classe se refere, incluindo o fato de não possuir convenções internas para nomes, tipos de dados ou restrições. Mas isso não significa que o clichê seja necessário; em vez disso, incentive-o a criar suas próprias convenções automatizadas usando funções auxiliares e classes mixin, descritas detalhadamente em Mixin e Classes personalizadas.

Quando nossa classe é construída, Declarative substitui todos os objetos Column por acessadores especiais do Python conhecidos como descritores; este é um processo conhecido como instrumentação. A classe mapeada "instrumentada" nos fornecerá os meios para nos referir à nossa tabela em um contexto SQL, bem como para persistir e carregar os valores das colunas do banco de dados.

Fora o que o processo de mapeamento faz com a nossa classe, a classe permanece, na maioria das vezes, uma classe Python normal, na qual podemos definir qualquer número de atributos e métodos comuns necessários ao nosso aplicativo.
Para informações do por que uma chave primaria é necessaria veja: How do I map a table that has no primary key?.

Criar um esquema

Com nossa classe User construída através do sistema Declarative, definimos informações sobre nossa tabela, conhecidas como metadados da tabela . O objeto usado pelo SQLAlchemy para representar essas informações para uma tabela específica é chamado de objeto Table , e aqui o Declarative criou um para nós. Podemos ver esse objeto inspecionando o atributo __table__ :
 >>>  User . __table__ 
Table('users', MetaData(bind=None),
            Column('id', Integer(), table=<users>, primary_key=True, nullable=False),
            Column('name', String(), table=<users>),
            Column('fullname', String(), table=<users>),
            Column('nickname', String(), table=<users>), schema=None) 
Quando declaramos nossa classe, o Declarative usou uma metaclasse do Python para executar atividades adicionais depois que a declaração da classe foi concluída; nessa fase, ele criou um objeto Table acordo com nossas especificações e o associou à classe construindo um objeto Mapper . Esse objeto é um objeto de bastidores com o qual normalmente não precisamos lidar diretamente (embora possa fornecer muitas informações sobre nosso mapeamento quando necessário).
O objeto Table é um membro de uma coleção maior conhecida como MetaData . Ao usar Declarative, esse objeto está disponível usando o atributo .metadata da nossa classe base declarativa.
MetaData é um registro que inclui a capacidade de emitir um conjunto limitado de comandos de geração de esquema para o banco de dados. Como nosso banco de dados SQLite realmente não possui uma tabela de users presente, podemos usar o MetaData para emitir instruções CREATE TABLE no banco de dados para todas as tabelas que ainda não existem. Abaixo, chamamos o método MetaData.create_all() , transmitindo nosso Engine como fonte de conectividade do banco de dados. Veremos que comandos especiais são emitidos primeiro para verificar a presença da tabela de users e, a seguir, a instrução CREATE TABLE real:
 >>> Base . metadata . create_all ( engine )
SELECT ...
PRAGMA main . table_info ( "users" )
()
PRAGMA temp . table_info ( "users" )
()
CREATE TABLE users (
    id INTEGER NOT NULL , name VARCHAR ,
    fullname VARCHAR ,
    nickname VARCHAR ,
    PRIMARY KEY ( id )
)
()
COMMIT 
Descrições mínimas de tabela vs. descrições completas
Usuários familiarizados com a sintaxe de CREATE TABLE podem perceber que as colunas VARCHAR foram geradas sem um comprimento; no SQLite e PostgreSQL, este é um tipo de dados válido, mas em outros, não é permitido. Portanto, se você estiver executando este tutorial em um desses bancos de dados e desejar usar o SQLAlchemy para emitir CREATE TABLE, poderá ser fornecido um "comprimento" para o tipo String como abaixo:
 Column ( String ( 50 )) 
O campo de comprimento em String , bem como os campos de precisão / escala semelhantes disponíveis em Integer , Numeric , etc. não são referenciados por SQLAlchemy, exceto quando se cria tabelas.
Além disso, o Firebird e o Oracle exigem sequências para gerar novos identificadores de chave primária, e o SQLAlchemy não os gera ou assume sem ser instruído. Para isso, você usa a construção Sequence :
 from sqlalchemy import Sequence
Column ( Integer , Sequence ( 'user_id_seq' ), primary_key = True ) 
Uma Table completa e infalível gerada por meio de nosso mapeamento declarativo é, portanto:
 class User ( Base ):
    __tablename__ = 'users'
    id = Column ( Integer , Sequence ( 'user_id_seq' ), primary_key = True )
    name = Column ( String ( 50 ))
    fullname = Column ( String ( 50 ))
    nickname = Column ( String ( 50 ))

    def __repr__ ( self ):
        return "<User(name=' %s ', fullname=' %s ', nickname=' %s ')>" % (
                                self . name , self . fullname , self . nickname ) 
Incluímos essa definição de tabela mais detalhada separadamente para destacar a diferença entre uma construção mínima voltada principalmente para o uso em Python, versus uma que será usada para emitir instruções CREATE TABLE em um conjunto específico de back-end com requisitos mais rigorosos.

Criar uma instância da classe mapeada 

Com os mapeamentos concluídos, agora vamos criar e inspecionar um objeto User :
 >>>  ed_user = User ( name = 'ed' , fullname = 'Ed Jones' , nickname = 'edsnickname' )
>>>  ed_user . name
'ed'
>>>  ed_user . nickname
'edsnickname'
>>>  str ( ed_user . id )
'None' 
Mesmo que não o tenhamos especificado no construtor, o atributo id ainda produz um valor None quando o acessamos (em oposição ao comportamento usual do Python de aumentar o AttributeError para um atributo indefinido). instrumentação do SQLAlchemy normalmente produz esse valor padrão para atributos mapeados em colunas quando acessados ​​pela primeira vez. Para os atributos em que realmente atribuímos um valor, o sistema de instrumentação está rastreando essas atribuições para uso em uma eventual instrução INSERT a ser emitida no banco de dados.

Criando uma sessão

Agora estamos prontos para começar a conversar com o banco de dados. O "identificador" do ORM para o banco de dados é a Session . Quando configuramos o aplicativo pela primeira vez, no mesmo nível da instrução create_engine() , definimos uma classe Session que servirá como uma fábrica para novos objetos Session :
 >>>  from sqlalchemy.orm import sessionmaker
>>>  Session = sessionmaker ( bind = engine ) 
No caso em que seu aplicativo ainda não possui um Engine ao definir seus objetos no nível do módulo, basta configurá-lo da seguinte maneira:
 >>>  Session = sessionmaker () 
Posteriormente, quando você criar seu mecanismo com create_engine() , conecte-o à Session usando configure() :
 >>>  Session . configure ( bind = engine )  # once engine is available 
Essa classe Session customizada criará novos objetos Session vinculados ao nosso banco de dados. Outras características transacionais também podem ser definidas ao chamar o sessionmaker ; estes são descritos em um capítulo posterior. Então, sempre que precisar conversar com o banco de dados, instancie uma Session :
 >>>  session = Session () 
Session acima está associada ao nosso Engine habilitado para SQLite, mas ainda não abriu nenhuma conexão. Quando é usado pela primeira vez, ele recupera uma conexão de um conjunto de conexões mantidas pelo Engine e o mantém até que confirmamos todas as alterações e / ou fechemos o objeto da sessão.

Adicionando e atualizando objetos 

Para persistir nosso objeto User , add() à nossa Session :
 >>>  ed_user = User ( name = 'ed' , fullname = 'Ed Jones' , nickname = 'edsnickname' )
>>>  session . add ( ed_user ) 
Neste ponto, dizemos que a instância está pendente ; nenhum SQL ainda foi emitido e o objeto ainda não está representado por uma linha no banco de dados. Session emitirá o SQL para persistir Ed Jones assim que necessário, usando um processo conhecido como flush . Se consultarmos o banco de dados em busca de Ed Jones , todas as informações pendentes serão liberadas primeiro e a consulta será emitida imediatamente a seguir.
Por exemplo, abaixo, criamos um novo objeto Query que carrega instâncias de User . "Filtramos por" o atributo name de ed e indicamos que gostaríamos apenas do primeiro resultado na lista completa de linhas. Uma instância de User é retornada, equivalente à que adicionamos:
 SQL >>> our_user = session . query ( User ) . filter_by ( name = 'ed' ) . first () 
 >>> our_user
< User ( name = 'ed' , fullname = 'Ed Jones' , nickname = 'edsnickname' ) > 
De fato, a Session identificou que a linha retornada é a mesma que a já representada em seu mapa interno de objetos; portanto, recuperamos a instância idêntica à que acabamos de adicionar:
 >>>  ed_user is our_user
True 
O conceito ORM em ação aqui é conhecido como mapa de identidade e garante que todas as operações em uma linha específica em uma Session operem no mesmo conjunto de dados. Quando um objeto com uma chave primária específica estiver presente na Session , todas as consultas SQL nessa Session sempre retornarão o mesmo objeto Python para essa chave primária específica; ele também gera um erro se for feita uma tentativa de colocar um segundo objeto já persistente com a mesma chave primária na sessão.
Podemos adicionar mais objetos User ao mesmo tempo usando add_all() :
 >>> session . add_all ([
...     User ( name = 'wendy' , fullname = 'Wendy Williams' , nickname = 'windy' ),
...     User ( name = 'mary' , fullname = 'Mary Contrary' , nickname = 'mary' ),
...     User ( name = 'fred' , fullname = 'Fred Flintstone' , nickname = 'freddy' )]) 
Além disso, decidimos que o apelido de Ed não é tão bom, então vamos alterá-lo:
 >>> ed_user . nickname = 'eddie' 
Session está prestando atenção. Sabe, por exemplo, que Ed Jones foi modificado:
 >>> session . dirty
IdentitySet ([ < User ( name = 'ed' , fullname = 'Ed Jones' , nickname = 'eddie' ) > ]) 
e que três novos objetos User estão pendentes:
 >>> session . new  
IdentitySet ([ < User ( name = 'wendy' , fullname = 'Wendy Williams' , nickname = 'windy' ) > ,
< User ( name = 'mary' , fullname = 'Mary Contrary' , nickname = 'mary' ) > ,
< User ( name = 'fred' , fullname = 'Fred Flintstone' , nickname = 'freddy' ) > ]) 
Dizemos à Session que gostaríamos de emitir todas as alterações restantes no banco de dados e confirmar a transação, que já estava em andamento. Fazemos isso via commit() . Session emite a UPDATE para a mudança de apelido em "ed", bem como as instruções INSERT para os três novos objetos User que adicionamos:
 SQL >>> session . commit ()
commit() libera as alterações restantes no banco de dados e confirma a transação. Os recursos de conexão mencionados pela sessão agora são retornados ao pool de conexões. As operações subsequentes com esta sessão ocorrerão em uma nova transação, que novamente readquirirá recursos de conexão quando necessário.
Se olharmos para o atributo id de Ed, que anteriormente era None , ele agora tem um valor:
 SQL >>> ed_user . id 
 1 
Após a Session inserir novas linhas no banco de dados, todos os identificadores recém-gerados e padrões gerados pelo banco de dados ficam disponíveis na instância, imediatamente ou por meio do carregamento no primeiro acesso. Nesse caso, a linha inteira foi recarregada no acesso porque uma nova transação foi iniciada após a emissão do commit() . Por padrão, o SQLAlchemy atualiza os dados de uma transação anterior na primeira vez em que é acessada em uma nova transação, para que o estado mais recente esteja disponível. O nível de recarregamento é configurável, conforme descrito em Usando a Sessão .
Estados do Objeto de Sessão
À medida que nosso objeto User passou de fora da Session , para dentro da Session sem uma chave primária, para realmente ser inserido, ele se moveu entre três dos quatro "estados de objeto" disponíveis - transitório , pendente e persistente . Estar ciente desses estados e do que eles significam é sempre uma boa idéia - leia Introdução Rápida aos Estados Objetos para obter uma visão geral rápida.

Revertendo 

Como a Session funciona em uma transação, também podemos reverter as alterações feitas. Vamos fazer duas alterações que reverteremos; O nome de usuário de ed_user é definido como Edwardo :
 >>> ed_user . name = 'Edwardo' 
e adicionaremos outro usuário incorreto, fake_user :
 >>> fake_user = User ( name = 'fakeuser' , fullname = 'Invalid' , nickname = '12345' )
>>> session . add ( fake_user ) 
Consultando a sessão, podemos ver que elas estão liberadas na transação atual:
 SQL >>> session . query ( User ) . filter ( User . name . in_ ([ 'Edwardo' , 'fakeuser' ])) . all ()
 [ < User ( name = 'Edwardo' , fullname = 'Ed Jones' , nickname = 'eddie' ) > , < User ( name = 'fakeuser' , fullname = 'Invalid' , nickname = '12345' ) > ] 
ed_user , podemos ver que o nome de ed_user voltou a ser ed , e fake_user foi expulso da sessão:
 SQL >>> session . rollback ()

SQL >>> ed_user . name
 u 'ed'
>>> fake_user in session
False 
emitir um SELECT ilustra as alterações feitas no banco de dados:
 SQL >>> session . query ( User ) . filter ( User . name . in_ ([ 'ed' , 'fakeuser' ])) . all ()
 [ < User ( name = 'ed' , fullname = 'Ed Jones' , nickname = 'eddie' ) > ] 

Consulta 

Um objeto Query é criado usando o método query() na Session . Essa função recebe um número variável de argumentos, que pode ser qualquer combinação de classes e descritores instrumentados por classe. Abaixo, indicamos uma Query que carrega instâncias de User . Quando avaliada em um contexto iterativo, a lista de objetos User presentes é retornada:
 SQL >>> for instance in session . query ( User ) . order_by ( User . id ):
...     print ( instance . name , instance . fullname )
 ed Ed Jones
wendy Wendy Williams
mary Mary Contrary
fred Fred Flintstone 
Query também aceita descritores instrumentados por ORM como argumentos. Sempre que várias entidades de classe ou entidades baseadas em coluna são expressas como argumentos para a função query() , o resultado do retorno é expresso como tuplas:
 SQL >>> for name , fullname in session . query ( User . name , User . fullname ):
...     print ( name , fullname )
 ed Ed Jones
wendy Wendy Williams
mary Mary Contrary
fred Fred Flintstone 
As tuplas retornadas pelo Query são denominadas tuplas, fornecidas pela classe KeyedTuple , e podem ser tratadas como um objeto Python comum. Os nomes são os mesmos que o nome do atributo para um atributo e o nome da classe para uma classe:
 SQL >>> for row in session . query ( User , User . name ) . all ():
...    print ( row . User , row . name )
 < User ( name = 'ed' , fullname = 'Ed Jones' , nickname = 'eddie' ) > ed
< User ( name = 'wendy' , fullname = 'Wendy Williams' , nickname = 'windy' ) > wendy
< User ( name = 'mary' , fullname = 'Mary Contrary' , nickname = 'mary' ) > mary
< User ( name = 'fred' , fullname = 'Fred Flintstone' , nickname = 'freddy' ) > fred 
Você pode controlar os nomes das expressões de colunas individuais usando a construção label() , disponível em qualquer objeto derivado de ColumnElement , bem como em qualquer atributo de classe mapeado para um (como User.name ):
 SQL >>> for row in session . query ( User . name . label ( 'name_label' )) . all ():
...    print ( row . name_label )
 ed
wendy
mary
fred 
O nome dado a uma entidade completa, como User , assumindo que várias entidades estejam presentes na chamada para query() , pode ser controlado usando o aliased() :
 >>> from sqlalchemy.orm import aliased
>>> user_alias = aliased ( User , name = 'user_alias' )

SQL >>> for row in session . query ( user_alias , user_alias . name ) . all ():
...    print ( row . user_alias )
 < User ( name = 'ed' , fullname = 'Ed Jones' , nickname = 'eddie' ) >
< User ( name = 'wendy' , fullname = 'Wendy Williams' , nickname = 'windy' ) >
< User ( name = 'mary' , fullname = 'Mary Contrary' , nickname = 'mary' ) >
< User ( name = 'fred' , fullname = 'Fred Flintstone' , nickname = 'freddy' ) > 
As operações básicas com o Query incluem a emissão de LIMIT e OFFSET, mais convenientemente usando fatias de array Python e normalmente em conjunto com ORDER BY:
 SQL >>> for u in session . query ( User ) . order_by ( User . id )[ 1 : 3 ]:
...    print ( u )
 < User ( name = 'wendy' , fullname = 'Wendy Williams' , nickname = 'windy' ) >
< User ( name = 'mary' , fullname = 'Mary Contrary' , nickname = 'mary' ) > 
e resultados de filtragem, que são realizados com filter_by() , que usa argumentos de palavra-chave:
 SQL >>> for name , in session . query ( User . name ) . \
...             filter_by ( fullname = 'Ed Jones' ):
...    print ( name )
 ed 
… Ou filter() , que usa construções de linguagem de expressão SQL mais flexíveis. Isso permite que você use operadores Python regulares com os atributos de nível de classe na sua classe mapeada:
 SQL >>> for name , in session . query ( User . name ) . \
...             filter ( User . fullname == 'Ed Jones' ):
...    print ( name )
 ed 
O objeto Query é totalmente generativo , o que significa que a maioria das chamadas de método retorna um novo objeto Query no qual critérios adicionais podem ser adicionados. Por exemplo, para consultar usuários nomeados "ed" com um nome completo de "Ed Jones", você pode chamar filter() duas vezes, que une os critérios usando AND :
 SQL >>> for user in session . query ( User ) . \
...          filter ( User . name == 'ed' ) . \
...          filter ( User . fullname == 'Ed Jones' ):
...    print ( user )
 < User ( name = 'ed' , fullname = 'Ed Jones' , nickname = 'eddie' ) > 

Operadores de filtro comuns 

Aqui está um resumo de alguns dos operadores mais comuns usados ​​em filter() :
  • equals :
     query . filter ( User . name == 'ed' ) 
  • not equals :
     query . filter ( User . name != 'ed' ) 
  • LIKE :
     query . filter ( User . name . like ( ' %e d%' )) 
Nota
ColumnOperators.like() renderiza o operador LIKE, que não faz ColumnOperators.like() maiúsculas e minúsculas em alguns backends e ColumnOperators.like() maiúsculas de minúsculas em outros. Para comparações garantidas sem ColumnOperators.ilike() entre maiúsculas e minúsculas, use ColumnOperators.ilike() .
  • ILIKE (LIKE que não ILIKE maiúsculas de minúsculas):
     query . filter ( User . name . ilike ( ' %e d%' )) 
Nota
a maioria dos back-end não oferece suporte direto ao ILIKE. Para esses, o operador ColumnOperators.ilike() renderiza uma expressão combinando LIKE com a função SQL LOWER aplicada a cada operando.
  • IN :
     query . filter ( User . name . in_ ([ 'ed' , 'wendy' , 'jack' ]))
    
    # works with query objects too:
    query . filter ( User . name . in_ (
        session . query ( User . name ) . filter ( User . name . like ( ' %e d%' ))
    ))
    
    # use tuple_() for composite (multi-column) queries
    from sqlalchemy import tuple_
    query . filter (
        tuple_ ( User . name , User . nickname ) . \
        in_ ([( 'ed' , 'edsnickname' ), ( 'wendy' , 'windy' )])
    ) 
  • NOT IN :
     query . filter ( ~ User . name . in_ ([ 'ed' , 'wendy' , 'jack' ])) 
  • IS NULL :
     query . filter ( User . name == None )
    
    # alternatively, if pep8/linters are a concern
    query . filter ( User . name . is_ ( None )) 
  • IS NOT NULL :
     query . filter ( User . name != None )
    
    # alternatively, if pep8/linters are a concern
    query . filter ( User . name . isnot ( None )) 
  • AND :
     # use and_()
    from sqlalchemy import and_
    query . filter ( and_ ( User . name == 'ed' , User . fullname == 'Ed Jones' ))
    
    # or send multiple expressions to .filter()
    query . filter ( User . name == 'ed' , User . fullname == 'Ed Jones' )
    
    # or chain multiple filter()/filter_by() calls
    query . filter ( User . name == 'ed' ) . filter ( User . fullname == 'Ed Jones' ) 
Nota
Certifique-se de usar and_() e não o Python and operador!
  • OR :
     from sqlalchemy import or_
    query . filter ( or_ ( User . name == 'ed' , User . name == 'wendy' )) 
Nota
Certifique-se de usar or_() e não o Python or operador!
  • MATCH :
     query . filter ( User . name . match ( 'wendy' )) 
Nota
match() usa uma função MATCH ou CONTAINS específica do banco de dados; seu comportamento varia de acordo com o back-end e não está disponível em alguns back-end, como o SQLite.

Retornando listas e escalares 

Vários métodos no Query emitem imediatamente o SQL e retornam um valor que contém os resultados carregados do banco de dados. Aqui está um breve tour:
  • all() retorna uma lista:
     >>> query = session . query ( User ) . filter ( User . name . like ( ' %e d' )) . order_by ( User . id )
    SQL >>> query . all ()
     [ < User ( name = 'ed' , fullname = 'Ed Jones' , nickname = 'eddie' ) > ,
          < User ( name = 'fred' , fullname = 'Fred Flintstone' , nickname = 'freddy' ) > ] 
    Aviso
    Quando o objeto Query retorna listas de objetos mapeados em ORM, como o objeto User acima, as entradas são deduplicadas com base na chave primária, pois os resultados são interpretados no conjunto de resultados SQL. Ou seja, se a consulta SQL retornar uma linha com id=7 duas vezes, você só obteria um único objeto User(id=7) na lista de resultados. Isso não se aplica ao caso em que colunas individuais são consultadas.
  • first() aplica um limite de um e retorna o primeiro resultado como um escalar:
     SQL >>> query . first ()
     < User ( name = 'ed' , fullname = 'Ed Jones' , nickname = 'eddie' ) > 
  • one() busca totalmente todas as linhas e, se não houver exatamente uma identidade de objeto ou linha composta presente no resultado, gera um erro. Com várias linhas encontradas:
     >>> user = query . one ()
    Traceback ( most recent call last ):
    ...
    MultipleResultsFound : Multiple rows were found for one () 
    Sem linhas encontradas:
     >>> user = query . filter ( User . id == 99 ) . one ()
    Traceback ( most recent call last ):
    ...
    NoResultFound : No row was found for one () 
    O método one() é ótimo para sistemas que esperam lidar com “nenhum item encontrado” versus “vários itens encontrados” de maneira diferente; como um serviço da web RESTful, que pode gerar um "404 não encontrado" quando nenhum resultado for encontrado, mas gerar um erro de aplicativo quando vários resultados forem encontrados.
  • one_or_none() é como one() , exceto que, se nenhum resultado for encontrado, não gera um erro; apenas retorna None . Como one() , no entanto, ele gera um erro se vários resultados forem encontrados.
  • scalar() chama o método one() e, com sucesso, retorna a primeira coluna da linha:
     >>> query = session . query ( User . id ) . filter ( User . name == 'ed' ) . \
    ...    order_by ( User . id )
    SQL >>> query . scalar ()
     1 

Usando SQL textual 

Seqüências de caracteres literais podem ser usadas de maneira flexível com o Query , especificando seu uso com a construção text() , que é aceita pela maioria dos métodos aplicáveis. Por exemplo, filter() e order_by() :
 >>> from sqlalchemy import text
SQL >>> for user in session . query ( User ) . \
...             filter ( text ( "id<224" )) . \
...             order_by ( text ( "id" )) . all ():
...     print ( user . name )
 ed
wendy
mary
fred 
Os parâmetros de ligação podem ser especificados com SQL baseado em cadeia, usando dois pontos. Para especificar os valores, use o método params() :
 SQL >>> session . query ( User ) . filter ( text ( "id<:value and name=:name" )) . \
...     params ( value = 224 , name = 'fred' ) . order_by ( User . id ) . one ()
 < User ( name = 'fred' , fullname = 'Fred Flintstone' , nickname = 'freddy' ) > 
Para usar uma instrução inteiramente baseada em string, uma construção text() representando uma instrução completa pode ser passada para from_statement() . Sem especificadores adicionais, as colunas na string SQL são correspondidas às colunas do modelo com base no nome, como abaixo, onde usamos apenas um asterisco para representar o carregamento de todas as colunas:
 SQL >>> session . query ( User ) . from_statement (
...                     text ( "SELECT * FROM users where name=:name" )) . \
...                     params ( name = 'ed' ) . all ()
 [ < User ( name = 'ed' , fullname = 'Ed Jones' , nickname = 'eddie' ) > ] 
As colunas correspondentes no nome funcionam em casos simples, mas podem se tornar difíceis de lidar ao lidar com instruções complexas que contêm nomes de colunas duplicados ou ao usar construções ORM anonimizadas que não correspondem facilmente a nomes específicos. Além disso, há um comportamento de digitação presente em nossas colunas mapeadas que podemos achar necessário ao lidar com linhas de resultados. Para esses casos, a construção text() permite vincular seu SQL textual às expressões de coluna mapeadas no Core ou no ORM; podemos conseguir isso passando expressões de coluna como argumentos posicionais para o método TextClause.columns() :
 >>> stmt = text ( "SELECT name, id, fullname, nickname "
...             "FROM users where name=:name" )
>>> stmt = stmt . columns ( User . name , User . id , User . fullname , User . nickname )
SQL >>> session . query ( User ) . from_statement ( stmt ) . params ( name = 'ed' ) . all ()
 [ < User ( name = 'ed' , fullname = 'Ed Jones' , nickname = 'eddie' ) > ] 
Novo na versão 1.1: O método TextClause.columns() agora aceita expressões de coluna que serão correspondidas posicionalmente a um conjunto de resultados SQL em texto sem formatação, eliminando a necessidade de os nomes das colunas corresponderem ou até serem únicos na instrução SQL.
Ao selecionar a partir de uma construção text() , a Query ainda pode especificar quais colunas e entidades devem ser retornadas; em vez de query(User) , também podemos solicitar as colunas individualmente, como em qualquer outro caso:
 >>> stmt = text ( "SELECT name, id FROM users where name=:name" )
>>> stmt = stmt . columns ( User . name , User . id )
SQL >>> session . query ( User . id , User . name ) . \
...          from_statement ( stmt ) . params ( name = 'ed' ) . all ()
 [( 1 , u 'ed' )] 
Veja também
Usando SQL textual - A construção text() explicada da perspectiva de consultas somente do Core.

Contando 

Query inclui um método de conveniência para contar chamado count() :
 SQL >>> session . query ( User ) . filter ( User . name . like ( ' %e d' )) . count ()
 2 
O método count() é usado para determinar quantas linhas a instrução SQL retornaria. Observando o SQL gerado acima, o SQLAlchemy sempre coloca o que estamos consultando em uma subconsulta e, em seguida, conta as linhas a partir disso. Em alguns casos, isso pode ser reduzido a uma SELECT count(*) FROM table mais simples, no entanto, as versões modernas do SQLAlchemy não tentam adivinhar quando isso é apropriado, pois o SQL exato pode ser emitido usando meios mais explícitos.
Para situações em que a “coisa a ser contada” precisa ser indicada especificamente, podemos especificar a função “contar” diretamente usando a expressão func.count() , disponível no construto func . Abaixo, usamos para retornar a contagem de cada nome de usuário distinto:
 >>> from sqlalchemy import func
SQL >>> session . query ( func . count ( User . name ), User . name ) . group_by ( User . name ) . all ()
 [( 1 , u 'ed' ), ( 1 , u 'fred' ), ( 1 , u 'mary' ), ( 1 , u 'wendy' )] 
Para obter nossa SELECT count(*) FROM table simples, podemos aplicá-la como:
 SQL >>> session . query ( func . count ( '*' )) . select_from ( User ) . scalar ()
 4 
O uso de select_from() pode ser removido se expressarmos a contagem em termos da chave primária do User diretamente:
 SQL >>> session . query ( func . count ( User . id )) . scalar ()
 4 

Construindo um relacionamento 

Vamos considerar como uma segunda tabela, relacionada ao User , pode ser mapeada e consultada. Os usuários do nosso sistema podem armazenar qualquer número de endereços de email associados ao seu nome de usuário. Isso implica uma associação básica para muitos dos users para uma nova tabela que armazena endereços de email, que chamaremos de addresses . Usando declarativo, definimos esta tabela junto com sua classe mapeada, Address :
 >>>  from sqlalchemy import ForeignKey
>>>  from sqlalchemy.orm import relationship

>>>  class Address ( Base ):
...     __tablename__ = 'addresses'
...     id = Column ( Integer , primary_key = True )
...     email_address = Column ( String , nullable = False )
...     user_id = Column ( Integer , ForeignKey ( 'users.id' ))
...
...     user = relationship ( "User" , back_populates = "addresses" )
...
...     def __repr__ ( self ):
...         return "<Address(email_address=' %s ')>" % self . email_address

>>>  User . addresses = relationship (
...     "Address" , order_by = Address . id , back_populates = "user" ) 
A classe acima introduz a construção ForeignKey , que é uma diretiva aplicada à Column que indica que os valores nessa coluna devem ser restritos a valores presentes na coluna remota nomeada. Esse é um recurso central dos bancos de dados relacionais e é a "cola" que transforma uma coleção de tabelas desconectada de outro modo para ter ricos relacionamentos sobrepostos. ForeignKey acima expressa que os valores na coluna users.id devem ser restritos a esses valores na coluna users.id , ou seja, sua chave primária.
Uma segunda diretiva, conhecida como relationship() , informa ao ORM que a própria classe Address deve ser vinculada à classe User , usando o atributo Address.user . relationship() usa os relacionamentos de chave estrangeira entre as duas tabelas para determinar a natureza desse vínculo, determinando que Address.user será muitos para um . Uma diretiva de relationship() adicional é colocada na classe mapeada pelo User sob o atributo User.addresses . Nas duas diretivas relationship() , o parâmetro relationship.back_populates é designado para se referir aos nomes de atributos complementares; ao fazer isso, cada relationship() pode tomar uma decisão inteligente sobre o mesmo relacionamento, expresso em sentido inverso; por um lado, Address.user refere-se a uma instância de User e, por outro lado, User.addresses refere-se a uma lista de instâncias de Address .
Nota
O parâmetro relationship.back_populates é uma versão mais recente de um recurso SQLAlchemy muito comum chamado relationship.backref . O parâmetro relationship.backref não foi a lugar nenhum e sempre permanecerá disponível! relationship.back_populates é a mesma coisa, exceto um pouco mais detalhado e mais fácil de manipular. Para uma visão geral de todo o tópico, consulte a seção Vinculando Relacionamentos à Backref .
O verso de um relacionamento muitos-para-um é sempre um para muitos . Um catálogo completo de configurações de relationship() disponível está em Padrões Básicos de Relacionamento .
Os dois relacionamentos complementares Address.user e User.addresses são referidos como um relacionamento bidirecional e são um recurso essencial do SQLAlchemy ORM. A seção Vinculando Relacionamentos ao Backref discute o recurso "backref" em detalhes.
Os argumentos para o relationship() que dizem respeito à classe remota podem ser especificados usando cadeias, assumindo que o sistema Declarativo esteja em uso. Após a conclusão de todos os mapeamentos, essas cadeias são avaliadas como expressões Python para produzir o argumento real, no caso acima, a classe User . Os nomes permitidos durante esta avaliação incluem, entre outras coisas, os nomes de todas as classes criadas em termos da base declarada.
Consulte a documentação para o relationship() para obter mais detalhes sobre o estilo do argumento.
Você sabia ?
  • uma restrição FOREIGN KEY na maioria dos bancos de dados relacionais (embora não todos) só pode vincular a uma coluna de chave primária ou a uma coluna que possui uma restrição UNIQUE.
  • uma restrição FOREIGN KEY que se refere a uma chave primária de várias colunas, e ela própria possui várias colunas, é conhecida como "chave estrangeira composta". Também pode fazer referência a um subconjunto dessas colunas.
  • As colunas FOREIGN KEY podem se atualizar automaticamente, em resposta a uma alteração na coluna ou linha referenciada. Isso é conhecido como ação referencial CASCADE e é uma função integrada do banco de dados relacional.
  • CHAVE ESTRANGEIRA pode se referir a sua própria tabela. Isso é chamado de chave estrangeira "auto-referencial".
  • Leia mais sobre chaves estrangeiras em Foreign Key - Wikipedia .
Precisamos criar a tabela de addresses no banco de dados, para que possamos emitir outro CREATE de nossos metadados, que pulará as tabelas que já foram criadas:
 SQL >>> Base . metadata . create_all ( engine )

Trabalhando com objetos relacionados

Consultando com junta 

Agora que temos duas tabelas, podemos mostrar mais alguns recursos Query, especificamente como criar consultas que lidam com as duas tabelas ao mesmo tempo. página da Wikipedia no SQL JOIN oferece uma boa introdução para juntar técnicas, várias das quais ilustraremos aqui.
Para construir uma junção implícita simples entre UserAddress, podemos usar Query.filter()para igualar suas colunas relacionadas. Abaixo, carregamos as entidades Usere de Addressuma só vez usando este método:
 SQL >>> for u , a in session . query ( User , Address ) . \
...                     filter ( User . id == Address . user_id ) . \
...                     filter ( Address . email_address == 'jack@google.com' ) . \
...                     all ():
...     print ( u )
...     print ( a )
 < User ( name = 'jack' , fullname = 'Jack Bean' , nickname = 'gjffdd' ) >
< Address ( email_address = 'jack@google.com' ) > 
A sintaxe real do SQL JOIN, por outro lado, é alcançada mais facilmente usando o Query.join()método:
 SQL >>> session . query ( User ) . join ( Address ) . \
...         filter ( Address . email_address == 'jack@google.com' ) . \
...         all ()
 [ < User ( name = 'jack' , fullname = 'Jack Bean' , nickname = 'gjffdd' ) > ] 
Query.join()sabe como se unir UserAddressporque existe apenas uma chave estrangeira entre eles. Se não houver chaves estrangeiras, ou várias, Query.join()funcionará melhor quando um dos seguintes formulários for usado:
 query . join ( Address , User . id == Address . user_id )    # explicit condition
query . join ( User . addresses )                       # specify relationship from left to right
query . join ( Address , User . addresses )              # same, with explicit target
query . join ( 'addresses' )                          # same, using a string 
Como seria de esperar, a mesma idéia é usada para junções "externas", usando a outerjoin()função:
 query . outerjoin ( User . addresses )   # LEFT OUTER JOIN 
A documentação de referência para join()contém informações detalhadas e exemplos dos estilos de chamada aceitos por este método; join()é um método importante no centro de uso de qualquer aplicativo fluente em SQL.
O que Queryselecionar se houver várias entidades?
Query.join()método normalmente ingressará no item mais à esquerda na lista de entidades, quando a cláusula ON for omitida ou se a cláusula ON for uma expressão SQL simples. Para controlar a primeira entidade na lista de JOINs, use o Query.select_from()método:
 query = session . query ( User , Address ) . select_from ( Address ) . join ( User ) 

Usando aliases 

Ao consultar várias tabelas, se a mesma tabela precisar ser referenciada mais de uma vez, o SQL normalmente exige que a tabela seja aliasada com outro nome, para que possa ser distinguida de outras ocorrências dessa tabela. Querysuporta isso explicitamente usando a aliasedconstrução. Abaixo, ingressamos na Addressentidade duas vezes, para localizar um usuário que tenha dois endereços de email distintos ao mesmo tempo:
 >>> from sqlalchemy.orm import aliased
>>> adalias1 = aliased ( Address )
>>> adalias2 = aliased ( Address )
SQL >>> for username , email1 , email2 in \
...     session . query ( User . name , adalias1 . email_address , adalias2 . email_address ) . \
...     join ( adalias1 , User . addresses ) . \
...     join ( adalias2 , User . addresses ) . \
...     filter ( adalias1 . email_address == 'jack@google.com' ) . \
...     filter ( adalias2 . email_address == 'j25@yahoo.com' ):
...     print ( username , email1 , email2 )
 jack jack @google.com j25 @yahoo.com 

Usando Subqueries 

Queryé adequado para gerar instruções que podem ser usadas como subconsultas. Suponha que desejássemos carregar Userobjetos juntamente com uma contagem de quantos Addressregistros cada usuário possui. A melhor maneira de gerar SQL dessa maneira é obter a contagem de endereços agrupados por IDs de usuário e JOIN para o pai. Nesse caso, usamos um LEFT OUTTER JOIN para recuperar as linhas dos usuários que não possuem endereços, por exemplo:
 SELECT users .* , adr_count . address_count FROM users LEFT OUTER JOIN
    ( SELECT user_id , count ( * ) AS address_count
        FROM addresses GROUP BY user_id ) AS adr_count
    ON users . id = adr_count . user_id 
Usando o Query, construímos uma declaração como essa de dentro para fora. statementacessador retorna uma expressão SQL que representa a instrução gerada por um determinado Query- esta é uma instância de uma select()construção, descrita no Tutorial da Linguagem de Expressão SQL :
 >>>  from sqlalchemy.sql import func
>>>  stmt = session . query ( Address . user_id , func . count ( '*' ) . \
...         label ( 'address_count' )) . \
...         group_by ( Address . user_id ) . subquery () 
funcpalavra-chave gera funções SQL, e o subquery()método Queryproduz uma construção de expressão SQL representando uma instrução SELECT incorporada em um alias (na verdade, é uma abreviação de query.statement.alias()).
Depois de termos nossa declaração, ela se comporta como uma Tableconstrução, como a que criamos usersno início deste tutorial. As colunas na instrução são acessíveis através de um atributo chamado c:
 SQL >>> for u , count in session . query ( User , stmt . c . address_count ) . \
...     outerjoin ( stmt , User . id == stmt . c . user_id ) . order_by ( User . id ):
...     print ( u , count )
 < User ( name = 'ed' , fullname = 'Ed Jones' , nickname = 'eddie' ) > None
< User ( name = 'wendy' , fullname = 'Wendy Williams' , nickname = 'windy' ) > None
< User ( name = 'mary' , fullname = 'Mary Contrary' , nickname = 'mary' ) > None
< User ( name = 'fred' , fullname = 'Fred Flintstone' , nickname = 'freddy' ) > None
< User ( name = 'jack' , fullname = 'Jack Bean' , nickname = 'gjffdd' ) > 2 

Seleção de Entidades de Subqueries 

Acima, apenas selecionamos um resultado que incluía uma coluna de uma subconsulta. E se quiséssemos que nossa subconsulta fosse mapeada para uma entidade? Para isso, usamos aliased()para associar um "alias" de uma classe mapeada a uma subconsulta:
 SQL >>> stmt = session . query ( Address ) . \
...                 filter ( Address . email_address != 'j25@yahoo.com' ) . \
...                 subquery ()
>>> adalias = aliased ( Address , stmt )
>>> for user , address in session . query ( User , adalias ) . \
...         join ( adalias , User . addresses ):
...     print ( user )
...     print ( address )
 < User ( name = 'jack' , fullname = 'Jack Bean' , nickname = 'gjffdd' ) >
< Address ( email_address = 'jack@google.com' ) > 

Usando EXISTS 

A palavra-chave EXISTS no SQL é um operador booleano que retorna True se a expressão especificada contiver alguma linha. Pode ser usado em muitos cenários no lugar de junções e também é útil para localizar linhas que não possuem uma linha correspondente em uma tabela relacionada.
Existe uma construção EXISTS explícita, que se parece com isso:
 >>> from sqlalchemy.sql import exists
>>> stmt = exists () . where ( Address . user_id == User . id )
SQL >>> for name , in session . query ( User . name ) . filter ( stmt ):
...     print ( name )
 jack 
Querydispõe de vários operadores que fazem uso de EXISTE automaticamente. Acima, a declaração pode ser expressa ao longo do User.addressesrelacionamento usando any():
 SQL >>> for name , in session . query ( User . name ) . \
...         filter ( User . addresses . any ()):
...     print ( name )
 jack 
any() também aceita o critério, para limitar as linhas correspondentes:
 SQL >>> for name , in session . query ( User . name ) . \
...     filter ( User . addresses . any ( Address . email_address . like ( ' %g oogle%' ))):
...     print ( name )
 jack 
has()é o mesmo operador any()das relações muitos-para-um (observe também o ~operador aqui, que significa "NÃO"):
 SQL >>> session . query ( Address ) . \
...         filter ( ~ Address . user . has ( User . name == 'jack' )) . all ()
 [] 

Operadores de relacionamento comuns 

Aqui estão todos os operadores que constroem relacionamentos - cada um deles está vinculado à documentação da API, que inclui detalhes completos sobre o uso e o comportamento:
  • __eq__() (comparação "igual a muitos" para um):
     query . filter ( Address . user == someuser ) 
  • __ne__() (comparação "muitos para um" não é igual "):
     query . filter ( Address . user != someuser ) 
  • É NULL (comparação muitos-para-um, também usa __eq__()):
     query . filter ( Address . user == None ) 
  • contains() (usado para coleções um para muitos):
     query . filter ( User . addresses . contains ( someaddress )) 
  • any() (usado para coleções):
     query . filter ( User . addresses . any ( Address . email_address == 'bar' ))
    
    # also takes keyword arguments:
    query . filter ( User . addresses . any ( email_address = 'bar' )) 
  • has() (usado para referências escalares):
     query . filter ( Address . user . has ( name = 'ed' )) 
  • Query.with_parent() (usado para qualquer relacionamento):
     session . query ( Address ) . with_parent ( someuser , 'addresses' ) 

Carregamento ansioso 

Lembre-se de que ilustramos uma operação de carregamento lento , quando acessamos a User.addressescoleção de a Usere SQL foi emitida. Se você deseja reduzir o número de consultas (drasticamente, em muitos casos), podemos aplicar uma carga ágil à operação de consulta. O SQLAlchemy oferece três tipos de carregamento rápido, dois dos quais são automáticos e um terceiro que envolve critérios personalizados. Todos os três são geralmente chamados por meio de funções conhecidas como opções de consulta, que fornecem instruções adicionais Querysobre como gostaríamos que vários atributos fossem carregados, através do Query.options()método

Selecionar carga 

Neste caso, gostaríamos de indicar que User.addressesdeve carregar ansiosamente. Uma boa opção para carregar um conjunto de objetos e suas coleções relacionadas é a orm.selectinload()opção, que emite uma segunda instrução SELECT que carrega completamente as coleções associadas aos resultados recém-carregados. O nome "selectin" se origina do fato de a instrução SELECT usar uma cláusula IN para localizar linhas relacionadas para vários objetos de uma só vez:
 >>> from sqlalchemy.orm import selectinload
SQL >>> jack = session . query ( User ) . \
...                 options ( selectinload ( User . addresses )) . \
...                 filter_by ( name = 'jack' ) . one ()
 >>> jack
< User ( name = 'jack' , fullname = 'Jack Bean' , nickname = 'gjffdd' ) >

>>> jack . addresses
[ < Address ( email_address = 'jack@google.com' ) > , < Address ( email_address = 'j25@yahoo.com' ) > ] 

Carga 

A outra função de carregamento rápido automático é mais conhecida e é chamada orm.joinedload()Esse estilo de carregamento emite um JOIN, por padrão, um LEFT OUTER JOIN, para que o objeto principal e o objeto ou coleção relacionado sejam carregados em uma etapa. Ilustramos o carregamento da mesma addressescoleção dessa maneira - observe que, embora a User.addressescoleção ativada jackesteja realmente preenchida no momento, a consulta emitirá a junção extra, independentemente:
 >>> from sqlalchemy.orm import joinedload

SQL >>> jack = session . query ( User ) . \
...                        options ( joinedload ( User . addresses )) . \
...                        filter_by ( name = 'jack' ) . one ()
 >>> jack
< User ( name = 'jack' , fullname = 'Jack Bean' , nickname = 'gjffdd' ) >

>>> jack . addresses
[ < Address ( email_address = 'jack@google.com' ) > , < Address ( email_address = 'j25@yahoo.com' ) > ] 
Observe que, embora o OUTER JOIN tenha resultado em duas linhas, ainda temos apenas uma instância de Uservolta. Isso ocorre porque Queryaplica uma estratégia de "exclusividade", baseada na identidade do objeto, às entidades retornadas. Isso é especificamente para que o carregamento antecipado combinado possa ser aplicado sem afetar os resultados da consulta.
Enquanto joinedload()existe há muito tempo, selectinload()é uma forma mais nova de carregamento ansioso. selectinload()tende a ser mais apropriado para carregar coleções relacionadas, enquanto joinedload()tende a ser mais adequado para relacionamentos muitos-para-um, devido ao fato de que apenas uma linha é carregada para o lead e o objeto relacionado. subqueryload()Também existe outra forma de carregamento, que pode ser usada no lugar do selectinload()uso de chaves primárias compostas em determinados back-end.
joinedload() não é um substituto para join()
A junção criada por joinedload()é anonimamente aliasada, de modo que não afeta os resultados da consulta . Uma chamada Query.order_by()ou não pode fazer referência a essas tabelas com alias - as chamadas junções de "espaço do usuário" são construídas usando A lógica para isso é que é aplicada apenas para afetar como objetos ou coleções relacionados são carregados como um detalhe de otimização - eles podem ser adicionados ou removidos sem afetar os resultados reais. Consulte a seção O Zen do carregamento ansioso para obter uma descrição detalhada de como isso é usado.Query.filter()Query.join()joinedload()

Junção explícita + Eagerload 

Um terceiro estilo de carregamento rápido é quando estamos construindo um JOIN explicitamente para localizar as linhas primárias e gostaria de aplicar adicionalmente a tabela extra a um objeto ou coleção relacionada no objeto primário. Esse recurso é fornecido por meio da orm.contains_eager()função e normalmente é útil para pré-carregar o objeto muitos para um em uma consulta que precisa filtrar no mesmo objeto. A seguir, ilustramos o carregamento de uma Addresslinha e o Userobjeto relacionado , filtrando o User"jack" nomeado e usando orm.contains_eager()para aplicar as colunas "user" ao Address.useratributo:
 >>> from sqlalchemy.orm import contains_eager
SQL >>> jacks_addresses = session . query ( Address ) . \
...                             join ( Address . user ) . \
...                             filter ( User . name == 'jack' ) . \
...                             options ( contains_eager ( Address . user )) . \
...                             all ()
 >>> jacks_addresses
[ < Address ( email_address = 'jack@google.com' ) > , < Address ( email_address = 'j25@yahoo.com' ) > ]

>>> jacks_addresses [ 0 ] . user
< User ( name = 'jack' , fullname = 'Jack Bean' , nickname = 'gjffdd' ) > 
Para obter mais informações sobre carregamento rápido, incluindo como configurar várias formas de carregamento por padrão, consulte a seção Técnicas de carregamento de relacionamento .

Excluindo 

Vamos tentar excluir jacke ver como isso acontece. Marcaremos o objeto como excluído na sessão e emitiremos uma countconsulta para ver se não há linhas restantes:
 >>> session . delete ( jack )
SQL >>> session . query ( User ) . filter_by ( name = 'jack' ) . count ()
 0 
Por enquanto, tudo bem.E os Addressobjetos de Jack ?
 SQL >>> session . query ( Address ) . filter (
...     Address . email_address . in_ ([ 'jack@google.com' , 'j25@yahoo.com' ])
...  ) . count ()
 2 
Ainda estão lá! Analisando o SQL de liberação, podemos ver que a user_idcoluna de cada endereço foi definida como NULL, mas as linhas não foram excluídas. SQLAlchemy não assume que exclui cascata, você precisa dizer para fazê-lo.

Configurando o Cascade delete / delete-órfão 

Vamos configurar as opções em cascata no User.addressesrelacionamento para alterar o comportamento. Embora o SQLAlchemy permita adicionar novos atributos e relacionamentos aos mapeamentos a qualquer momento, nesse caso, o relacionamento existente precisa ser removido, portanto, precisamos desmontar os mapeamentos completamente e começar novamente - fecharemos o Session:
 >>>  session . close ()
ROLLBACK 
e use um novo declarative_base():
 >>>  Base = declarative_base () 
Em seguida, declararemos a Userclasse, adicionando o addressesrelacionamento, incluindo a configuração em cascata (deixaremos o construtor de fora também):
 >>>  class User ( Base ):
...     __tablename__ = 'users'
...
...     id = Column ( Integer , primary_key = True )
...     name = Column ( String )
...     fullname = Column ( String )
...     nickname = Column ( String )
...
...     addresses = relationship ( "Address" , back_populates = 'user' ,
...                     cascade = "all, delete, delete-orphan" )
...
...     def __repr__ ( self ):
...        return "<User(name=' %s ', fullname=' %s ', nickname=' %s ')>" % (
...                                self . name , self . fullname , self . nickname ) 
Em seguida, recriamos Address, observando que, nesse caso, já criamos o Address.userrelacionamento por meio da Userclasse:
 >>>  class Address ( Base ):
...     __tablename__ = 'addresses'
...     id = Column ( Integer , primary_key = True )
...     email_address = Column ( String , nullable = False )
...     user_id = Column ( Integer , ForeignKey ( 'users.id' ))
...     user = relationship ( "User" , back_populates = "addresses" )
...
...     def __repr__ ( self ):
...         return "<Address(email_address=' %s ')>" % self . email_address 
Agora, quando carregamos o usuário jack(abaixo de usar get(), que carrega pela chave primária), a remoção de um endereço da addressescoleção correspondente resultará na Addressexclusão:
 # load Jack by primary key
SQL >>> jack = session . query ( User ) . get ( 5 )

# remove one Address (lazy load fires off)
SQL >>> del jack . addresses [ 1 ]

# only one address remains
SQL >>> session . query ( Address ) . filter (
...     Address . email_address . in_ ([ 'jack@google.com' , 'j25@yahoo.com' ])
... ) . count ()
 1 
Excluir Jack excluirá Jack e o restante Addressassociado ao usuário:
 >>> session . delete ( jack )

SQL >>> session . query ( User ) . filter_by ( name = 'jack' ) . count ()
 0

SQL >>> session . query ( Address ) . filter (
...    Address . email_address . in_ ([ 'jack@google.com' , 'j25@yahoo.com' ])
... ) . count ()
 0 
Mais sobre Cascades
Mais detalhes sobre a configuração de cascatas estão em Cascades . A funcionalidade em cascata também pode se integrar facilmente à funcionalidade do banco de dados relacional. Consulte Usando exclusões passivas para obter detalhes.ON DELETE CASCADE

Construindo um relacionamento de muitos para muitos 

Estamos entrando na rodada de bônus aqui, mas vamos mostrar um relacionamento de muitos para muitos. Também vamos nos infiltrar em alguns outros recursos, apenas para fazer um tour. Tornaremos nosso aplicativo um aplicativo de blog, onde os usuários podem escrever BlogPostitens com Keyworditens associados a eles.
Para um muitos para muitos, precisamos criar uma Tableconstrução não mapeada para servir como tabela de associação. Parece com o seguinte:
 >>>  from sqlalchemy import Table , Text
>>>  # association table
>>>  post_keywords = Table ( 'post_keywords' , Base . metadata ,
...     Column ( 'post_id' , ForeignKey ( 'posts.id' ), primary_key = True ),
...     Column ( 'keyword_id' , ForeignKey ( 'keywords.id' ), primary_key = True )
...  ) 
Acima, podemos ver que declarar Tablediretamente é um pouco diferente de declarar uma classe mapeada. Tableé uma função construtora, portanto, cada Columnargumento individual é separado por vírgula. Columnobjeto também recebe seu nome explicitamente, em vez de ser retirado de um nome de atributo atribuído.
Em seguida, definimos BlogPostKeyword, usando relationship()construções complementares , cada um referente à post_keywordstabela como uma tabela de associação:
 >>>  class BlogPost ( Base ):
...     __tablename__ = 'posts'
...
...     id = Column ( Integer , primary_key = True )
...     user_id = Column ( Integer , ForeignKey ( 'users.id' ))
...     headline = Column ( String ( 255 ), nullable = False )
...     body = Column ( Text )
...
...     # many to many BlogPost<->Keyword
...     keywords = relationship ( 'Keyword' ,
...                             secondary = post_keywords ,
...                             back_populates = 'posts' )
...
...     def __init__ ( self , headline , body , author ):
...         self . author = author
...         self . headline = headline
...         self . body = body
...
...     def __repr__ ( self ):
...         return "BlogPost( %r ,  %r ,  %r )" % ( self . headline , self . body , self . author )


>>>  class Keyword ( Base ):
...     __tablename__ = 'keywords'
...
...     id = Column ( Integer , primary_key = True )
...     keyword = Column ( String ( 50 ), nullable = False , unique = True )
...     posts = relationship ( 'BlogPost' ,
...                          secondary = post_keywords ,
...                          back_populates = 'keywords' )
...
...     def __init__ ( self , keyword ):
...         self . keyword = keyword 
Nota
As declarações de classe acima ilustram __init__()métodos explícitos Lembre-se, ao usar Declarative, é opcional!
Acima, o relacionamento muitos-para-muitos é BlogPost.keywords. O recurso definidor de um relacionamento muitos para muitos é o secondaryargumento de palavra chave que faz referência a um Tableobjeto que representa a tabela de associação. Esta tabela contém apenas colunas que referenciam os dois lados do relacionamento; se tiver quaisquer outras colunas, como a sua própria chave primária ou chaves estrangeiras para outras tabelas, SQLAlchemy requer um padrão de uso diferente chamado de “objeto associação”, descrito na Associação Objeto .
Também gostaríamos que a nossa BlogPostturma tivesse um authorcampo. Adicionaremos isso como outro relacionamento bidirecional, exceto que um problema que temos é que um único usuário pode ter muitas postagens no blog. Quando acessamos User.posts, gostaríamos de poder filtrar ainda mais os resultados para não carregar a coleção inteira. Para isso, usamos uma configuração aceita pelo relationship()chamado lazy='dynamic', que configura uma estratégia de carregador alternativo no atributo:
 >>> BlogPost . author = relationship ( User , back_populates = "posts" )
>>> User . posts = relationship ( BlogPost , back_populates = "author" , lazy = "dynamic" ) 
Crie novas tabelas:
 SQL >>> Base . metadata . create_all ( engine )
O uso não é muito diferente do que estamos fazendo. Vamos dar a Wendy algumas postagens no blog:
 SQL >>> wendy = session . query ( User ) . \
...                 filter_by ( name = 'wendy' ) . \
...                 one ()
 >>> post = BlogPost ( "Wendy's Blog Post" , "This is a test" , wendy )
>>> session . add ( post ) 
Estamos armazenando palavras-chave exclusivamente no banco de dados, mas sabemos que ainda não temos nenhuma, para que possamos criá-las:
 >>> post . keywords . append ( Keyword ( 'wendy' ))
>>> post . keywords . append ( Keyword ( 'firstpost' )) 
Agora, podemos procurar todas as postagens do blog com a palavra-chave 'firstpost'. Usaremos o anyoperador para localizar "postagens de blog em que qualquer uma de suas palavras-chave tenha a cadeia de palavras-chave 'firstpost'":
 SQL >>> session . query ( BlogPost ) . \
...             filter ( BlogPost . keywords . any ( keyword = 'firstpost' )) . \
...             all ()
 [ BlogPost ( "Wendy's Blog Post" , 'This is a test' , < User ( name = 'wendy' , fullname = 'Wendy Williams' , nickname = 'windy' ) > )] 
Se quisermos procurar postagens pertencentes ao usuário wendy, podemos dizer à consulta para restringir esse Userobjeto como pai:
 SQL >>> session . query ( BlogPost ) . \
...             filter ( BlogPost . author == wendy ) . \
...             filter ( BlogPost . keywords . any ( keyword = 'firstpost' )) . \
...             all ()
 [ BlogPost ( "Wendy's Blog Post" , 'This is a test' , < User ( name = 'wendy' , fullname = 'Wendy Williams' , nickname = 'windy' ) > )] 
Ou podemos usar o próprio postsrelacionamento de Wendy , que é um relacionamento "dinâmico", para consultar diretamente a partir daí:
 SQL >>> wendy . posts . \
...         filter ( BlogPost . keywords . any ( keyword = 'firstpost' )) . \
...         all ()
 [ BlogPost ( "Wendy's Blog Post" , 'This is a test' , < User ( name = 'wendy' , fullname = 'Wendy Williams' , nickname = 'windy' ) > )] 

Referência adicional 

Referência de consulta: API de consulta
Referência do Mapper: Configuração do Mapper
Referência de relacionamento: configuração de relacionamento
Referência da sessão: usando a sessão

Postar um comentário

0 Comentários