Voltar ao catálogo
Season 37 7 Episódios 26 min 2026

SQLAlchemy

v2.0 — Edição de 2026. Um curso em áudio abrangente sobre SQLAlchemy, cobrindo tanto o Core como o ORM, concebido para a versão 2.0 lançada em 2026. Aprenda a mapear o seu domínio, estruturar a sua aplicação, gerir transações com a Session e executar consultas de forma eficaz.

Bases de Dados ORM Python Core
SQLAlchemy
A Reproduzir
Click play to start
0:00
0:00
1
A Base: O que é o SQLAlchemy e o ORM?
Bem-vindo ao SQLAlchemy. Apresentamos a arquitetura principal, explicando a diferença entre o Core centrado no esquema e o ORM centrado no domínio. Irá aprender o jargão fundamental e por que precisa de um ORM.
3m 57s
2
O Engine: A sua porta de entrada para a base de dados
Todas as aplicações SQLAlchemy começam com o Engine. Aprenda como estabelecer conectividade, o que é o connection pooling e como os dialetos e DBAPIs fazem a ponte para a sua base de dados.
3m 49s
3
Mapear o Domínio: Declarative Base e Modelos
Traduza as suas classes Python para tabelas de base de dados automaticamente. Abordamos a DeclarativeBase, os tipos Mapped e como a mapped_column constrói os metadados da sua base de dados.
4m 06s
4
Layout do Projeto: Estruturar a sua Aplicação
A organização do código é importante. Aprenda as melhores práticas para estruturar um repositório de projeto SQLAlchemy para que o seu engine, modelos e sessions se mantenham limpos e fáceis de manter.
3m 35s
5
A Session: Dominar o Unit of Work
Descubra o padrão Unit of Work através da Session do ORM. Aprenda como adicionar objetos, quando ocorrem os flushes e como fazer o commit de transações na perfeição.
3m 43s
6
Consultar Dados: O Construto Select Moderno
Obtenha os seus dados exatamente como precisa. Exploramos o construto unificado select() do SQLAlchemy 2.0, a filtragem com where() e a execução de consultas com a session.
3m 09s
7
Ligar os Pontos: Relacionamentos e JOINs
Ligue as suas tabelas de forma perfeita. Aprenda a configurar relacionamentos, a utilizar o back_populates e a gerir automaticamente JOINs de SQL entre modelos relacionados.
4m 07s

Episódios

1

A Base: O que é o SQLAlchemy e o ORM?

3m 57s

Bem-vindo ao SQLAlchemy. Apresentamos a arquitetura principal, explicando a diferença entre o Core centrado no esquema e o ORM centrado no domínio. Irá aprender o jargão fundamental e por que precisa de um ORM.

Download
Olá, daqui fala o Alex da DEV STORIES DOT EU. SQLAlchemy, episódio 1 de 7. Já alguma vez apagaste uma tabela acidentalmente ou deste cabo de uma query por causa de um typo numa string de raw SQL? Queries com concatenação de strings são frágeis, difíceis de manter e representam um risco de segurança. Este episódio aborda a solução: A Base: O que é o SQLAlchemy e o ORM? O problema central que os developers enfrentam ao escrever aplicações baseadas em bases de dados é que o Python e as bases de dados relacionais pensam de maneira diferente. O Python usa objetos. Os objetos têm estado, comportamento e relações complexas. As bases de dados relacionais usam tabelas, colunas e linhas. Dependem de primary e foreign keys. Colocar um objeto de Python numa tabela de base de dados exige traduzir o seu estado para um SQL statement. Trazê-lo de volta exige fazer o parsing das linhas e repopular um novo objeto. A esta fricção chama-se object-relational impedance mismatch. Escrever strings de raw SQL para lidar com esta tradução é aborrecido e propenso a erros. O SQLAlchemy é um toolkit de SQL para Python criado para resolver exatamente este problema. Ao contrário de algumas ferramentas que tentam esconder a base de dados atrás de uma parede de abstração, o SQLAlchemy abraça o SQL. Oferece um workflow robusto e Pythonic que gera SQL otimizado, mantendo-te no controlo. A arquitetura está dividida em duas partes distintas: o Core e o ORM. O SQLAlchemy Core é a base. É orientado a comandos e orientado a schemas. Isto significa que trabalhas diretamente com tabelas, colunas e linhas da base de dados, mas fazes isso usando objetos de Python em vez de raw text strings. Se quiseres selecionar dados, chamas um método select. Se quiseres filtrar, fazes chain de um método where. Isto remove o risco de erros de sintaxe da concatenação de strings e protege contra SQL injection. O Core é essencialmente uma SQL expression language. O ORM, ou Object Relational Mapper, assenta em cima do Core. Enquanto o Core é orientado a schemas, o ORM é orientado a estado e a domínio. Aqui está a ideia principal. Com o ORM, paras de pensar em tabelas e linhas, e começas a pensar no domain model da tua aplicação. Defines uma class padrão de Python, por exemplo, uma class user. Configuras o ORM para mapear esta class para uma tabela de users na base de dados, e mapeias os atributos da class para as colunas da tabela. Quando fazes uma query à base de dados usando o ORM, não recebes de volta raw rows de dados. Recebes instâncias totalmente preenchidas da tua class user. O ORM lida com a tradução de forma transparente. Mais importante ainda, faz o tracking do estado desses objetos. Se alterares o nome de um user em Python, o ORM repara que o objeto foi modificado. Quando dizes à session da base de dados para guardar as tuas alterações, o ORM descobre automaticamente os update statements corretos e executa-os através do Core. Focas-te nos teus objetos de Python, e o ORM trata da sincronização com a base de dados. A coisa mais importante a lembrar é que o ORM não é uma caixa negra que substitui o Core. É uma layer opcional construída inteiramente com componentes do Core, permitindo-te descer de forma transparente para a geração explícita de SQL sempre que a tua domain logic exigir a máxima performance. Se achas estes episódios úteis e queres apoiar o programa, podes procurar por DevStoriesEU no Patreon. Obrigado por passares uns minutos comigo. Até à próxima, fica bem.
2

O Engine: A sua porta de entrada para a base de dados

3m 49s

Todas as aplicações SQLAlchemy começam com o Engine. Aprenda como estabelecer conectividade, o que é o connection pooling e como os dialetos e DBAPIs fazem a ponte para a sua base de dados.

Download
Olá, daqui é o Alex da DEV STORIES DOT EU. SQLAlchemy, episódio 2 de 7. Queres falar com uma base de dados, mas bases de dados diferentes falam dialects diferentes, e os drivers de Python têm todos as suas próprias peculiaridades. Precisas de um componente único e unificado que lide com o connection pooling, a tradução de dialects e a gestão de drivers, sem deixar que esses detalhes se misturem com a lógica da tua aplicação. Esse componente é o Engine. O Engine é o ponto de partida para qualquer aplicação SQLAlchemy. Atua como um registo central e uma factory para conexões à base de dados. Quando a tua aplicação precisa de falar com a base de dados, acaba sempre por passar pelo Engine. Nos bastidores, o Engine mantém uma connection pool. Isto significa que mantém uma cache de conexões ativas à base de dados, mantendo-as abertas e prontas a usar. Reutilizar conexões de uma pool é muito mais rápido do que negociar uma nova conexão de rede de cada vez que precisas de executar uma query. Para instanciar um Engine, usas uma função chamada create engine. Esta função requer um URL da base de dados. O URL é uma string que fornece a localização da base de dados, as credenciais e duas peças críticas de configuração: o dialect e a DBAPI. O dialect é a família de bases de dados que estás a usar. O SQL é um standard, mas cada motor de base de dados implementa-o de forma ligeiramente diferente. O dialect diz ao SQLAlchemy se está a formatar queries para PostgreSQL, MySQL, Oracle ou SQLite. Ele lida com todas as variações específicas de cada fornecedor para que o teu código Python possa permanecer consistente. Aqui está o ponto crucial. O SQLAlchemy não se liga diretamente à tua base de dados. Delega sempre a comunicação de rede real a um driver Python de terceiros. Este driver é conhecido como DBAPI. Se estiveres a usar PostgreSQL, a tua DBAPI pode ser psycopg2. Se estiveres a usar SQLite, normalmente é pysqlite. O URL da base de dados especifica ambos em conjunto. Por exemplo, o URL pode começar com a string sqlite plus pysqlite. Isto diz explicitamente ao Engine para formatar as queries usando o dialect SQLite e para as transmitir usando o driver pysqlite. Vamos ver como configurar uma base de dados SQLite in-memory. Este é um pattern muito comum para testes, porque a base de dados vive inteiramente na RAM e desaparece quando o programa termina. Chamas a função create engine e passas-lhe a tua string de URL. Para uma base de dados in-memory, o URL é sqlite plus pysqlite dois pontos barra barra barra dois pontos memory dois pontos. Quando corres essa linha de código, o objeto Engine é criado, mas não se liga imediatamente à base de dados. O Engine é lazy. Prepara a configuração e cria a connection pool, mas espera até à primeira vez que lhe pedes explicitamente para executar uma task, antes de realmente contactar a DBAPI para estabelecer uma conexão. Durante o desenvolvimento, muitas vezes precisas de verificar exatamente o que o SQLAlchemy está a enviar para a base de dados. A função create engine aceita um parâmetro opcional chamado echo. Se definires o echo como true, o Engine vai fazer log de todas as statements SQL raw que gera, diretamente para o teu standard output. Funciona como uma ferramenta de debugging built-in, permitindo-te ver a tradução exata entre as tuas operações em Python e o SQL resultante. O Engine existe para abstrair a realidade confusa das conexões de rede e dos drivers de bases de dados. Dá à tua aplicação uma interface limpa e estável, garantindo que o resto do teu código nunca tenha de se preocupar com a forma como a base de dados é alcançada fisicamente. Obrigado por estares aí. Espero que tenhas aprendido algo novo.
3

Mapear o Domínio: Declarative Base e Modelos

4m 06s

Traduza as suas classes Python para tabelas de base de dados automaticamente. Abordamos a DeclarativeBase, os tipos Mapped e como a mapped_column constrói os metadados da sua base de dados.

Download
Olá, daqui é o Alex da DEV STORIES DOT EU. SQLAlchemy, episódio 3 de 7. Tu escreves os teus domain models em Python e, a seguir, tens de escrever um monte de SQL à parte só para criar as tabelas para os guardar. Com o tempo, essas duas definições separadas quase sempre ficam fora de sincronia. E se as tuas classes Python pudessem desenhar automaticamente o schema da tua base de dados? É exatamente isso que resolvemos hoje com Mapping the Domain: Declarative Base and Models. Antes de olharmos para as classes em si, temos de falar sobre metadata da base de dados. No SQLAlchemy, a metadata é, na prática, um catálogo estrutural. É um registry central de Python que guarda o blueprint exato da tua base de dados. Monitoriza cada tabela, cada coluna e cada constraint que tu defines. Sempre que mapeias uma classe no SQLAlchemy, os detalhes dessa classe alimentam diretamente este catálogo único. Para ligar as tuas classes Python a este catálogo de metadata, usas um construct chamado DeclarativeBase. Tu não usas o DeclarativeBase diretamente. Em vez disso, crias a tua própria base class customizada, herdando dela. A partir desse momento, cada model que construíres na tua aplicação vai herdar da tua base class customizada. No momento em que uma classe herda dela, o SQLAlchemy regista silenciosamente esse novo model no catálogo de metadata subjacente. Pensa em como crias um model User. Defines uma classe Python chamada User e herdas da tua base class customizada. A primeira coisa que defines dentro desta classe é um atributo chamado tablename, escrito com dois underscores de cada lado. Defines isto como a string user_account. Esta atribuição explícita diz ao SQLAlchemy exatamente qual tabela da base de dados subjacente vai guardar estes objetos Python. A seguir, estabeleces as colunas. O SQLAlchemy 2.0 depende dos type hints standard do Python para fazer isto. Defines os teus atributos usando uma anotação especial chamada Mapped. Para um identificador, anotas o teu atributo ID como Mapped contendo um integer. Para um username, anotas como Mapped contendo uma string. Este type hint é estritamente para Python. Diz ao teu IDE e ao teu type checker que dados esperar. Mas o engine da base de dados precisa de instruções mais específicas do que um simples tipo de Python. É aqui que entra em jogo uma função chamada mapped_column. Atribuis o mapped_column ao atributo da tua classe e, dentro dos parênteses, configuras as regras específicas da base de dados. Para o teu user ID, chamas o mapped_column e passas uma flag que o marca explicitamente como primary key. Para uma coluna de string, podes passar um limite máximo de caracteres ou uma flag a exigir que o campo seja unique. Se o teu type hint de Python fornecer todo o contexto de que o SQLAlchemy precisa, podes mesmo omitir o mapped_column por completo. O SQLAlchemy vai inferir uma coluna básica e standard de base de dados diretamente da anotação Mapped. Mas para primary keys, ou quaisquer constraints específicas da base de dados, o mapped_column é estritamente necessário. Aqui está o ponto chave. Assim que as tuas classes Python estiverem escritas, o design do teu schema já está terminado. O catálogo de metadata contém agora um mapa perfeito do teu domain. Podes chamar um método chamado create_all na metadata da tua base class, passando o teu database engine. O SQLAlchemy olha para a tua classe User, lê as colunas mapeadas, traduz tudo instantaneamente em statements CREATE TABLE perfeitamente formatados, e aplica-os. Exatamente o mesmo código Python que escreves para correr a tua aplicação torna-se na single source of truth que constrói a estrutura da tua base de dados. Obrigado por estares aí. Espero que tenhas aprendido algo novo.
4

Layout do Projeto: Estruturar a sua Aplicação

3m 35s

A organização do código é importante. Aprenda as melhores práticas para estruturar um repositório de projeto SQLAlchemy para que o seu engine, modelos e sessions se mantenham limpos e fáceis de manter.

Download
Olá, daqui é o Alex do DEV STORIES DOT EU. SQLAlchemy, episódio 4 de 7. Já escreveste os teus models, o teu engine e as tuas queries. Mas colocar tudo num único ficheiro é um caminho rápido para um pesadelo de manutenção e circular imports. A solução é o Project Layout: Estruturar a tua Aplicação. Quando vês tutoriais, o código geralmente está contido num único script. Vês o setup do engine, a base class, os models e as queries empilhados de cima a baixo. Esse design é ótimo para aprender a sintaxe, mas se tentares construir um sistema em produção dessa forma, ele vai abaixo. Assim que adicionas web routes ou background workers, arranjas problemas. Se uma web route precisa de importar um user model, mas o user model está no mesmo ficheiro que inicia o application server ou inicializa a ligação à base de dados, o Python fica confuso. Acabas com circular imports e o teu código torna-se impossível de testar. Precisas de uma separation of concerns rigorosa. O standard pattern é dividir esse script único em módulos distintos com base na sua função. Primeiro, crias um ficheiro dedicado, tipicamente chamado database dot py. Este ficheiro tem exatamente uma responsabilidade, que é gerir a ligação à base de dados. É aqui que colocas a tua chamada ao create engine e fazes o setup do teu session maker. Não defines tabelas aqui, e não corres queries aqui. Ao isolar o engine, garantes que a tua aplicação cria apenas um único connection pool. Qualquer outro módulo no teu projeto pode importar o session maker deste ficheiro em segurança, sem acionar acidentalmente a lógica de startup da aplicação. A seguir, moves as tuas definições de tabelas para um ficheiro chamado models dot py. Este ficheiro contém a tua Declarative Base e todas as tuas mapped classes, como um objeto User ou Address. Não importa nada do teu ficheiro de base de dados. Esta é a parte que interessa. Os teus models definem a estrutura dos teus dados, mas não sabem como se ligar à base de dados. Como o models dot py não tem dependências do engine ou da active session, podes importar as tuas mapped classes em qualquer lugar da tua aplicação sem te preocupares com side effects. Se o teu projeto crescer muito, podes até dividir os models num diretório com ficheiros separados para diferentes domínios. Desde que todos importem exatamente a mesma instância da Declarative Base, o SQLAlchemy sabe que pertencem à mesma metadata collection. Finalmente, precisas de um lugar para realmente executar as tuas queries. Manténs a execução das queries inteiramente separada dos teus models e ficheiros de ligação. Normalmente, isto vai para os teus route handlers ou ficheiros de service dedicados. Quando uma aplicação precisa de dados, o ficheiro de service importa o session maker do teu módulo de database, e as mapped classes necessárias do teu módulo de models. Abre uma session usando um context manager, corre um select statement, recupera os objetos, e deixa o context manager fechar a ligação em segurança quando o bloco termina. O ficheiro de service atua como coordenador. Estruturar o teu projeto desta forma mantém a tua lógica de ligação isolada, os teus data schemas portáteis, e as tuas business operations limpas. A regra mais importante a reter é que os teus data models nunca devem importar o teu database engine. É tudo por este episódio. Obrigado por ouvires, e continua a desenvolver!
5

A Session: Dominar o Unit of Work

3m 43s

Descubra o padrão Unit of Work através da Session do ORM. Aprenda como adicionar objetos, quando ocorrem os flushes e como fazer o commit de transações na perfeição.

Download
Olá, daqui é o Alex da DEV STORIES DOT EU. SQLAlchemy, episódio 5 de 7. Imagina uma staging area onde podes colocar em queue dezenas de alterações na base de dados e fazer push de todas de forma perfeita de uma só vez, ou cancelá-las instantaneamente. Esse é exatamente o problema resolvido pelo conceito deste episódio: a Session: Dominar o Unit of Work. A Session do SQLAlchemy é a principal forma de os teus objetos Python comunicarem com a base de dados. Ela opera com base num pattern chamado Unit of Work. Quando usas a Session, não estás a enviar comandos individuais diretamente para a base de dados. Estás a interagir com um workspace inteligente. A Session monitoriza as tuas operações, reúne as adições, modificações e eliminações, e calcula a forma mais eficiente de as traduzir para SQL no momento certo. Dentro deste workspace existe um mecanismo chamado Identity Map. Trata-se de um dicionário interno que liga a primary key de uma linha da base de dados ao endereço de memória específico do teu objeto Python. Se pedires um utilizador com o ID cinco, a Session verifica primeiro o Identity Map. Se o objeto já estiver carregado, recebes de volta exatamente a mesma instância Python. Isto garante que nunca terás dois objetos Python distintos a representar a mesma linha da base de dados ao mesmo tempo. Vamos olhar para a inserção de novos dados. Começas por criar uma instância da tua classe User mapeada, dando-lhe um nome como Alice. Nesta fase, o teu objeto está num estado chamado transient. Ele existe exclusivamente na memória do Python. A base de dados não faz ideia de que ele existe, e a Session também não. Para ligar o objeto ao teu workspace, passas o objeto para o método add da Session. O objeto passa para um estado chamado pending. A Session registou a tua intenção de inserir este novo utilizador, mas ainda não enviou nenhum SQL pela rede. O objeto está simplesmente à espera na queue. Isto introduz a diferença estrita entre um flush e um commit. Quando chamas o flush na Session, ela pega na tua pending queue e faz push das alterações para a transação atual da base de dados. Ela emite o statement SQL INSERT. A base de dados executa-o e atribui uma primary key. De volta ao teu código Python, o teu objeto de utilizador é instantaneamente populado com esse novo ID da base de dados. O objeto transitou para um estado persistent. Aqui está a ideia principal. Mesmo que o objeto esteja persistent e tenha um ID, a alteração ainda não é permanente. A base de dados está a isolar esta transação. Supõe que o teu código deteta de repente um erro, como um endereço de email inválido para o utilizador em que acabaste de fazer flush. Como ainda não fizeste commit, podes emitir um rollback. A base de dados descarta a transação por completo, e nada é guardado nas tabelas reais. Quando tens a certeza absoluta de que os dados estão corretos, chamas o commit. Um commit finaliza a transação e guarda os dados no disco. Chamar o commit aciona automaticamente um flush primeiro, por isso geralmente não precisas de chamar o flush manualmente, a não ser que precises especificamente de ler uma primary key gerada antes de concluir o resto da tua lógica. A Session atua como um buffer entre a memória da tua aplicação e o armazenamento permanente, permitindo-te orquestrar alterações complexas de dados com segurança antes de as fixares na realidade. Obrigado por ouvirem. Fiquem bem.
6

Consultar Dados: O Construto Select Moderno

3m 09s

Obtenha os seus dados exatamente como precisa. Exploramos o construto unificado select() do SQLAlchemy 2.0, a filtragem com where() e a execução de consultas com a session.

Download
Olá, daqui é o Alex do DEV STORIES DOT EU. SQLAlchemy, episódio 6 de 7. Nas versões mais antigas do SQLAlchemy, fazer queries parecia trabalhar com um cérebro dividido. Tinhas de memorizar uma sintaxe para as operações do Core e um conjunto de regras completamente diferente para o ORM. Sempre que escrevias uma query, tinhas de parar e perguntar-te se estavas a obter raw rows ou mapped objects. A versão 2.0 eliminou essa linha com o construct moderno Select. O conceito é simples. Agora existe uma única função unificada chamada select. Passas a entidade que queres consultar diretamente para ela. Se tiveres uma classe ORM chamada User, passas a classe User para a função select. Isto cria um objeto select que representa uma query à tabela user subjacente. Este objeto select é generativo. Chamar a função select não executa nada na base de dados. Em vez disso, cria um expression object em memória. Quando chamas um método neste objeto para adicionar condições, ele retorna um novo objeto select com essas novas condições aplicadas. Isto permite-te construir queries complexas dinamicamente, passo a passo, passando o objeto pela tua aplicação antes sequer de falar com a base de dados. Isto cobre a query base. Como é que a filtras? Adicionas o método where ao teu objeto select. Dentro do método where, usas operadores standard de Python diretamente nos atributos da tua classe mapeada. Por exemplo, para encontrar um utilizador específico, escreves a classe User ponto name, seguido de dois sinais de igual, e a target string. O SQLAlchemy interceta isto. Ele faz overload de operadores standard de Python, como o duplo igual ou o sinal de maior que. Em vez de avaliar a expressão para true ou false em Python, traduz isso para a sintaxe SQL correta para a cláusula WHERE da base de dados. Agora tens um objeto query totalmente construído. Para realmente recuperares os teus dados, passas este objeto para uma session. Podes passá-lo para o método execute standard da session, mas isso retorna um objeto result onde cada row é essencialmente um tuple. Mesmo que tenhas feito uma query a uma única classe ORM, o teu mapped object fica preso dentro de um tuple de um só item, e tens de o extrair manualmente no retorno. Aqui está o ponto chave. Quando quiseres os objetos ORM de volta, usa antes o método scalars na session. Passas o teu objeto select para session ponto scalars. A session corre a query, faz o unwrap automático desses tuples do result set subjacente, e faz yield de um iterável dos teus objetos ORM totalmente preenchidos. Obténs imediatamente uma coleção limpa de instâncias User, prontas para modificar ou ler. O construct select unificado significa que exatamente os mesmos blocos de construção de queries que usas para objetos ORM podem ser executados diretamente contra ligações Core de baixo nível, deixando-te com exatamente um modelo mental para manter em toda a tua camada de base de dados. É tudo por este episódio. Obrigado por ouvires, e continua a desenvolver!
7

Ligar os Pontos: Relacionamentos e JOINs

4m 07s

Ligue as suas tabelas de forma perfeita. Aprenda a configurar relacionamentos, a utilizar o back_populates e a gerir automaticamente JOINs de SQL entre modelos relacionados.

Download
Olá, daqui é o Alex da DEV STORIES DOT EU. SQLAlchemy, episódio 7 de 7. As bases de dados relacionais existem porque os dados se ligam, mas escrever joins complexos manualmente sempre que precisas de um registo relacionado torna-se cansativo rapidamente. Acabas com lógica de query repetitiva espalhada por toda a tua aplicação. Ligar os Pontos: Relationships e Joins resolve isto ao permitir que o object-relational mapper trate das ligações por ti. Ao nível da base de dados, as tabelas ligam-se usando foreign keys. Se um utilizador tem vários endereços, a tabela de endereços tem uma coluna a guardar o user ID. Essa é uma relação one-to-many padrão. Mas quando escreves código Python, não queres extrair um ID e escrever uma segunda query só para encontrar esses endereços. Queres aceder a uma propriedade no teu objeto de utilizador e ver instantaneamente uma lista de objetos de endereço. É exatamente isso que o construto relationship oferece. Ele faz a ponte entre as foreign keys da base de dados e os atributos dos objetos Python. Para configurar isto, precisas de duas coisas. Primeiro, declaras a foreign key do lado da base de dados. No teu modelo Address, defines uma coluna chamada user ID e marcas explicitamente como uma foreign key a apontar para a tabela User. Segundo, defines o relationship do lado do Python. No teu modelo User, defines uma propriedade chamada addresses, usando a função relationship, a apontar para o modelo Address. Também defines uma propriedade relationship no modelo Address a apontar de volta para o User. Aqui está o ponto-chave. O SQLAlchemy precisa de saber que estas duas propriedades representam os dois lados da mesma ligação. Dizes-lhe isto usando o parâmetro back populates em ambas as definições de relationship. No modelo User, o relationship addresses define o back populates para a string com o nome da propriedade user. No modelo Address, o relationship user define o back populates para a string com o nome da propriedade addresses. Graças ao back populates, o ORM mantém os teus objetos Python sincronizados em memória. Se pegares num objeto address e o atribuíres a um utilizador, esse endereço aparece instantaneamente na lista de addresses do utilizador. A framework trata da sincronização antes sequer de fazeres commit de alguma coisa para a base de dados. Assim que os teus objetos estão ligados, fazes as tuas queries. Supõe que executas um statement select para encontrar uma utilizadora chamada Alice. A base de dados devolve a linha do utilizador, e tu obténs um objeto. Neste exato momento, os addresses da Alice não estão carregados. O ORM não faz o fetch deles porque ainda não os pediste. Quando finalmente acedes à propriedade addresses no teu código, talvez para iterar sobre os nomes das ruas, o ORM repara que os dados estão em falta. Ele pausa automaticamente o teu programa, emite um segundo statement select para a base de dados para encontrar todos os addresses com o ID da Alice, e preenche a lista. A isto chama-se lazy loading. É o comportamento padrão porque impede que a aplicação puxe milhares de linhas relacionadas para a memória, a menos que sejam estritamente necessárias. Fazes uma query a um utilizador, acedes às suas propriedades, e o sistema navega de forma transparente pelas foreign keys e emite as queries necessárias em background. O verdadeiro poder do construto relationship é que esconde a complexidade mecânica dos joins, permitindo-te navegar por uma base de dados inteira apenas a interagir com objetos Python ligados. Para dominares estes conceitos, explora a documentação oficial e experimenta configurar os teus próprios modelos na prática. Sente-te à vontade para visitar devstories dot eu para sugerir tópicos que queiras ver abordados em futuras séries. É tudo por este episódio. Obrigado por ouvires, e continua a desenvolver!