Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
SQLAlchemy é o conjunto de ferramentas de Python ORM e banco de dados mais amplamente utilizado. A partir do SQLAlchemy 2.1.0b2, um dialeto embutido para o driver mssql-python permite usar SQLAlchemy ORM e Core com Microsoft SQL e Banco de Dados SQL do Azure.
Importante
O dialeto mssql-python foi adicionado em SQLAlchemy 2.1.0b2 (lançado em 16 de abril de 2026). SQLAlchemy 2.1 é atualmente uma série pré-lançamento e não é recomendada para uso em produção. Antes de atualizar do SQLAlchemy 2.0, entenda:
- As APIs podem mudar antes da versão estável final (2.1 GA)
- Teste cuidadosamente sua carga de trabalho antes da implantação
- Use a versão estável do SQLAlchemy 2.0.x em sistemas de produção até que a versão 2.1 atinja disponibilidade geral (GA)
-
Fixe sua dependência em uma versão específica (por exemplo,
sqlalchemy==2.1.0b2) em vez de usar intervalos de versões
Veja a seção Limitações Conhecidas para detalhes sobre quando usar versões pré-lançamento.
Pré-requisitos
- Python 3.10 ou posterior. O SQLAlchemy 2.1 deixou de suportar Python 3.9 e anteriores.
- Os pacotes
mssql-pythonesqlalchemy(2.1.0b2 ou posterior).
Os exemplos neste artigo utilizam o banco de dados de exemplos AdventureWorksLT . Se você não tem o AdventureWorksLT instalado, veja bancos de dados de exemplo do AdventureWorks.
Instale o pré-lançamento
Como o SQLAlchemy 2.1 está em beta, pip install sqlalchemy instala a versão mais recente estável da 2.0.x por padrão. Instale o pré-lançamento explicitamente:
pip install mssql-python "sqlalchemy>=2.1.0b2"
Verifique a versão instalada:
import sqlalchemy
print(sqlalchemy.__version__) # Should show 2.1.0b2 or later
URLs de conexão
O dialeto mssql-python usa mssql+mssqlpython como esquema de URL. O formato geral é:
mssql+mssqlpython://<username>:<password>@<host>:<port>/<database>
Autenticação do SQL
Para autenticação SQL, inclua o nome de usuário e a senha na URL da conexão:
from sqlalchemy import create_engine
# Replace <password> with your actual password. Avoid using the sa account in production.
engine = create_engine(
"mssql+mssqlpython://dbuser:<password>@localhost:1433/<database>"
)
autenticação do Microsoft Entra
Para autenticação Microsoft Entra, use um nome de usuário vazio e o authentication parâmetro de consulta:
from sqlalchemy import create_engine
engine = create_engine(
"mssql+mssqlpython://@<server>.database.windows.net/<database>"
"?authentication=ActiveDirectoryDefault&encrypt=yes"
)
Note
ActiveDirectoryDefault usa DefaultAzureCredential, que tenta múltiplos provedores de credenciais em sequência. A primeira conexão pode ser lenta porque o SDK percorre a cadeia até encontrar um provedor funcionando. Em produção, se você sabe qual tipo de credencial seu ambiente usa, especifique-o diretamente (por exemplo, ActiveDirectoryMSI para identidade gerenciada) para evitar a caminhada em cadeia. Para obter mais informações, consulte Autenticação do Microsoft Entra.
Crie URLs de forma programática
Use sqlalchemy.engine.URL.create para evitar a codificação manual de URLs:
from sqlalchemy.engine import URL
url = URL.create(
"mssql+mssqlpython",
username="dbuser",
password="<password>",
host="localhost",
port=1433,
database="<database>",
)
engine = create_engine(url)
Defina modelos ORM
Use o mapeamento declarativo do SQLAlchemy para definir modelos que mapeiam tabelas do Microsoft SQL Server.
from datetime import datetime
from decimal import Decimal
from sqlalchemy import Identity, String, Numeric, Integer, DateTime, func
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
class Base(DeclarativeBase):
pass
class Product(Base):
__tablename__ = "Product"
__table_args__ = {"schema": "SalesLT"}
product_id: Mapped[int] = mapped_column(
"ProductID", Integer, Identity(), primary_key=True
)
name: Mapped[str] = mapped_column("Name", String(50))
product_number: Mapped[str] = mapped_column("ProductNumber", String(25))
color: Mapped[str | None] = mapped_column("Color", String(15))
list_price: Mapped[Decimal] = mapped_column("ListPrice", Numeric(19, 4))
standard_cost: Mapped[Decimal] = mapped_column("StandardCost", Numeric(19, 4))
size: Mapped[str | None] = mapped_column("Size", String(5))
product_category_id: Mapped[int | None] = mapped_column("ProductCategoryID", Integer)
sell_start_date: Mapped[datetime] = mapped_column("SellStartDate", DateTime)
modified_date: Mapped[datetime] = mapped_column(
"ModifiedDate", DateTime, server_default=func.getdate()
)
Tip
O Microsoft SQL utiliza IDENTITY para colunas com incremento automático.
SQLAlchemy mapeia isso automaticamente para colunas de chave primárias inteiras. O conteúdo explícito Identity() mostrado acima é opcional, a menos que você precise controlar os valores de início e incremento.
Operações CRUD
Os exemplos a seguir mostram como inserir, consultar, atualizar e excluir linhas usando a sessão ORM. Cada exemplo reutiliza new_id, o ProductID retornado ao inserir uma linha. Para rodar as quatro operações juntas, veja o exemplo completo.
Criar uma sessão
Crie uma sessão para executar operações dentro de uma transação:
from sqlalchemy.orm import Session
with Session(engine) as session:
# Use session for queries and modifications
pass
Para aplicações que criam muitas sessões, use sessionmaker:
from sqlalchemy.orm import sessionmaker
SessionLocal = sessionmaker(bind=engine)
Inserir linhas
Adicione um novo produto, efetue o commit da sessão e capture o ProductID gerado para os seguintes exemplos:
from datetime import datetime
with Session(engine) as session:
product = Product(
name="Classic Road Bike",
product_number="BK-C001",
color="Red",
list_price=Decimal("1299.99"),
standard_cost=Decimal("749.99"),
sell_start_date=datetime(2026, 1, 1),
product_category_id=6,
)
session.add(product)
session.commit()
new_id = product.product_id
print(f"Inserted ProductID: {new_id}")
Note
Em SalesLT.Product, tanto Name quanto ProductNumber possuem restrições únicas. Se você executar esse insert mais de uma vez, mude esses valores ou exclua a linha anterior primeiro. O exemplo completo elimina a linha que ele cria, para que ele possa ser executado repetidamente.
Linhas de consulta
Recupere uma única linha por chave primária, ou use select() para consultas filtradas:
from sqlalchemy import select
with Session(engine) as session:
# Single row by primary key (new_id is from the insert example)
product = session.get(Product, new_id)
if product:
print(f"{product.name}: ${product.list_price}")
# Filtered query
stmt = select(Product).where(Product.list_price < 500).order_by(Product.name)
products = session.scalars(stmt).all()
for p in products:
print(f"{p.name}: ${p.list_price}")
Atualizar linhas
Modifique um campo em uma linha existente e confirme:
with Session(engine) as session:
product = session.get(Product, new_id)
if product:
product.list_price = Decimal("1349.99")
session.commit()
Excluir linhas
Remova uma linha e faça o commit
with Session(engine) as session:
product = session.get(Product, new_id)
if product:
session.delete(product)
session.commit()
Exemplo completo
As seções anteriores mostravam cada peça separadamente. Esta seção os combina em um único script autônomo que você pode copiar, rodar e executar novamente.
Crie um arquivo nomeado crud.py e adicione o código a seguir. Substitua os detalhes create_engine da conexão pelos seus próprios (veja URLs de Conexão):
from datetime import datetime
from decimal import Decimal
from sqlalchemy import create_engine, Identity, String, Numeric, Integer, DateTime, func
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, Session
# Replace <password> and <database> with your connection details.
engine = create_engine(
"mssql+mssqlpython://dbuser:<password>@localhost:1433/<database>"
)
class Base(DeclarativeBase):
pass
class Product(Base):
__tablename__ = "Product"
__table_args__ = {"schema": "SalesLT"}
product_id: Mapped[int] = mapped_column(
"ProductID", Integer, Identity(), primary_key=True
)
name: Mapped[str] = mapped_column("Name", String(50))
product_number: Mapped[str] = mapped_column("ProductNumber", String(25))
color: Mapped[str | None] = mapped_column("Color", String(15))
list_price: Mapped[Decimal] = mapped_column("ListPrice", Numeric(19, 4))
standard_cost: Mapped[Decimal] = mapped_column("StandardCost", Numeric(19, 4))
size: Mapped[str | None] = mapped_column("Size", String(5))
product_category_id: Mapped[int | None] = mapped_column("ProductCategoryID", Integer)
sell_start_date: Mapped[datetime] = mapped_column("SellStartDate", DateTime)
modified_date: Mapped[datetime] = mapped_column(
"ModifiedDate", DateTime, server_default=func.getdate()
)
with Session(engine) as session:
# Create
product = Product(
name="Classic Road Bike",
product_number="BK-C001",
color="Red",
list_price=Decimal("1299.99"),
standard_cost=Decimal("749.99"),
sell_start_date=datetime(2026, 1, 1),
product_category_id=6,
)
session.add(product)
session.commit()
new_id = product.product_id
print(f"Inserted ProductID: {new_id}")
# Read
product = session.get(Product, new_id)
print(f"Read: {product.name} costs ${product.list_price}")
# Update
product.list_price = Decimal("1349.99")
session.commit()
print(f"Updated price to ${product.list_price}")
# Delete
session.delete(product)
session.commit()
print(f"Deleted ProductID: {new_id}")
Executar o script:
python crud.py
Você vê uma saída semelhante à seguinte:
Inserted ProductID: 1019
Read: Classic Road Bike costs $1299.9900
Updated price to $1349.9900
Deleted ProductID: 1019
O script apaga a linha que cria, então não viola as restrições de unicidade em Name e ProductNumber quando você o executa novamente. Cada execução insere uma nova linha, portanto, o ProductID aumenta a cada vez.
Consultas principais
SQLAlchemy O Core oferece uma API de expressões SQL de nível inferior. Você pode usar o Core com o mesmo motor e definições de tabelas, incluindo classes mapeadas por ORM.
from sqlalchemy import text
with engine.connect() as conn:
result = conn.execute(text("SELECT @@VERSION"))
print(result.scalar())
Use construções em nível de tabela para geração de SQL segura em tipos:
from sqlalchemy import insert, select, update, delete
with engine.connect() as conn:
# Insert
conn.execute(
insert(Product).values(
name="Touring Bike",
product_number="BK-T002",
list_price=Decimal("999.99"),
standard_cost=Decimal("575.00"),
sell_start_date=datetime(2026, 1, 1)
)
)
conn.commit()
# Select
stmt = select(
Product.name.label("name"),
Product.list_price.label("list_price"),
).where(Product.list_price > 100)
for row in conn.execute(stmt):
print(row.name, row.list_price)
# Delete the inserted row so this example can run again
conn.execute(delete(Product).where(Product.product_number == "BK-T002"))
conn.commit()
Note
Quando você seleciona colunas mapeadas individuais cujo nome no banco de dados difere do nome do atributo (por exemplo, Product.name é mapeado para a coluna Name), as linhas do Core têm como chave o nome da coluna no banco de dados. Somar .label("name") para acessar o valor como row.name em vez de row.Name.
Agrupamento de conexões
SQLAlchemy gerencia um pool de conexões por padrão. Ajuste as configurações do pool para sua carga de trabalho:
engine = create_engine(
"mssql+mssqlpython://dbuser:<password>@localhost/<database>",
pool_size=10,
max_overflow=20,
pool_timeout=30,
pool_recycle=3600,
)
| Parâmetro | Descrição |
|---|---|
pool_size |
Número de conexões para manter abertas (padrão: 5). |
max_overflow |
Conexões permitidas além pool_size (padrão: 10). |
pool_timeout |
Segundos para esperar uma conexão antes de gerar um erro (padrão: 30). |
pool_recycle |
Segundos após o qual uma conexão é reciclada (padrão: -1, desativada). Defina esse valor se seu banco de dados fecha conexões ociosas. |
Uso com frameworks web
SQLAlchemy é comumente usado como camada de banco de dados para Flask e FastAPI. O dialeto mssql-python funciona com qualquer framework que suporte SQLAlchemy.
Os trechos a seguir mostram o padrão recomendado de uma sessão por requisição para cada framework. São fragmentos ilustrativos que assumem o engine modelo e Product das seções anteriores, não aplicativos completos. Para aplicações completas e executáveis, veja os artigos sobre integração FastAPI e integração com Flask .
Exemplo do FastAPI
Use uma dependência geradora para fornecer uma sessão por requisição:
from fastapi import Depends, FastAPI, HTTPException
from sqlalchemy.orm import Session, sessionmaker
from sqlalchemy import create_engine
engine = create_engine("mssql+mssqlpython://dbuser:<password>@<server>/<database>")
SessionLocal = sessionmaker(bind=engine)
app = FastAPI()
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@app.get("/products/{product_id}")
def read_product(product_id: int, db: Session = Depends(get_db)):
product = db.get(Product, product_id)
if not product:
raise HTTPException(status_code=404, detail="Product not found")
return {"name": product.name, "price": float(product.list_price)}
Exemplo de Flask
Use um gerenciador de contexto para delimitar a sessão à requisição:
from flask import Flask, jsonify
from sqlalchemy.orm import Session, sessionmaker
from sqlalchemy import create_engine
engine = create_engine("mssql+mssqlpython://dbuser:<password>@<server>/<database>")
SessionLocal = sessionmaker(bind=engine)
app = Flask(__name__)
@app.route("/products/<int:product_id>")
def read_product(product_id):
with SessionLocal() as session:
product = session.get(Product, product_id)
if not product:
return jsonify({"error": "Not found"}), 404
return jsonify({"name": product.name, "price": float(product.list_price)})
Migrações alembicas
O Alembic gerencia migrações de esquemas para projetos SQLAlchemy e funciona com o dialeto mssql-python. O recurso autogenerate do Alembic compara seus modelos com o banco de dados ativo, então alguns passos extras impedem que ele proponha alterações em tabelas que você não gerencia.
Configurar o Alembic
Instale o Alembic e inicialize um diretório de migrações:
pip install alembic
alembic init migrations
Em alembic.ini, defina a URL da conexão:
sqlalchemy.url = mssql+mssqlpython://dbuser:<password>@localhost/<database>
Aponte Alembic para seus modelos
O Autogenerate precisa dos metadados dos seus modelos. Coloque os modelos gerenciados pela Alembic em um módulo importável, como models.py. Como o autogenerate propõe eliminar qualquer coluna que um modelo omita, defina um modelo que possua totalmente sua tabela em vez de reutilizar o modelo simplificado Product mencionado anteriormente neste artigo:
# models.py
from datetime import datetime
from sqlalchemy import Identity, String, Integer, DateTime, func
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
class Base(DeclarativeBase):
pass
class ProductReview(Base):
__tablename__ = "ProductReview"
__table_args__ = {"schema": "SalesLT"}
review_id: Mapped[int] = mapped_column("ReviewID", Integer, Identity(), primary_key=True)
product_id: Mapped[int] = mapped_column("ProductID", Integer)
reviewer_name: Mapped[str] = mapped_column("ReviewerName", String(50))
rating: Mapped[int] = mapped_column("Rating", Integer)
comments: Mapped[str | None] = mapped_column("Comments", String(500))
modified_date: Mapped[datetime] = mapped_column("ModifiedDate", DateTime, server_default=func.getdate())
Cuidado
Por padrão, o autogenerate trata cada tabela do banco de dados que não esteja em target_metadata como removida e emite drop_table para ela. Em um banco de dados existente, como o AdventureWorksLT, essa ação pode excluir dezenas de tabelas. Adicione um include_name filtro para que o Alembic gerencie apenas as tabelas que seus modelos definem e sempre revise o script gerado antes de aplicá-lo.
Em migrations/env.py, substitua target_metadata = None pelo seguinte código. Ele importa seus modelos e limita a geração automática para os esquemas e tabelas que eles definem:
from models import Base
target_metadata = Base.metadata
# Limit autogenerate to the tables your models define.
managed_schemas = {table.schema for table in target_metadata.tables.values()}
managed_tables = {table.name for table in target_metadata.tables.values()}
def include_name(name, type_, parent_names):
if type_ == "schema":
return name in managed_schemas
if type_ == "table":
return name in managed_tables
return True
Passe include_name e include_schemas=True para context.configure em ambos run_migrations_offline e run_migrations_online. A include_schemas=True configuração permite que a Alembic veja tabelas em esquemas não padrão, como SalesLT:
context.configure(
connection=connection,
target_metadata=target_metadata,
include_name=include_name,
include_schemas=True,
)
Gerar e aplicar uma migração
Gerar uma migração a partir dos seus modelos:
alembic revision --autogenerate -m "add product review table"
O Alembic detecta a nova tabela e escreve um script de migração:
INFO [alembic.autogenerate.compare.tables] Detected added table 'SalesLT.ProductReview'
Generating .../versions/xxxx_add_product_review_table.py ... done
O comando gerado upgrade() cria a tabela, e downgrade() a exclui:
def upgrade() -> None:
op.create_table(
"ProductReview",
sa.Column("ReviewID", sa.Integer(), sa.Identity(always=False), nullable=False),
sa.Column("ProductID", sa.Integer(), nullable=False),
sa.Column("ReviewerName", sa.String(length=50), nullable=False),
sa.Column("Rating", sa.Integer(), nullable=False),
sa.Column("Comments", sa.String(length=500), nullable=True),
sa.Column("ModifiedDate", sa.DateTime(), server_default=sa.text("getdate()"), nullable=False),
sa.PrimaryKeyConstraint("ReviewID"),
schema="SalesLT",
)
def downgrade() -> None:
op.drop_table("ProductReview", schema="SalesLT")
Revise o script e então aplique todas as migrações pendentes:
alembic upgrade head
Diferenças em relação ao dialeto pyodbc
Se você está migrando de mssql+pyodbc, o dialeto mssql-python é semelhante porque ambos os drivers são baseados no mesmo framework ODBC. Principais diferenças:
| Tópico | mssql+pyodbc |
mssql+mssqlpython |
|---|---|---|
| Instalação do driver ODBC | Requer driver ODBC separado (por exemplo, o Driver ODBC 18 para Microsoft SQL). | O driver vem em pacote. Não é necessário um driver ODBC separado. |
| URL de conexão | mssql+pyodbc://user:pass@host/db?driver=ODBC+Driver+18+for+SQL+Server |
mssql+mssqlpython://user:pass@host/db |
fast_executemany |
Suportado via create_engine(..., fast_executemany=True). |
Não aplicável. O driver gerencia o desempenho em lote internamente. |
| Disponibilidade | Estável, incluído em SQLAlchemy desde a versão 1.x. | Pré-lançamento (SQLAlchemy 2.1.0b2+). |
Limitações conhecidas
O dialeto mssql-python para SQLAlchemy está em pré-lançamento. Antes de usar em produção, compreenda estas implicações:
Alterações na API: Assinaturas de métodos, tipos de exceção e comportamento podem mudar antes da versão estável final. Sempre fixe sua versão SQLAlchemy em uma build pré-lançamento específica (por exemplo,
sqlalchemy==2.1.0b2) e teste as atualizações cuidadosamente.Testes Limitados: O dialeto tem menos testes comunitários do que o dialeto estável
mssql+pyodbc. Você pode encontrar casos extremos ou funcionalidades ausentes.Lacunas de Recursos: Alguns recursos avançados de ORM ou Core podem não funcionar. Consulte a documentação do dialeto SQLAlchemy MSSQL e teste seus casos de uso antes de se comprometer com um projeto.
Sem Garantia de Suporte: Microsoft e SQLAlchemy oferecem suporte de melhor esforço, mas os problemas podem não ser resolvidos antes da versão estável.
Quando usar o pré-lançamento:
- Ambientes de desenvolvimento e teste
- Projetos de prova de conceito
- Migrando de
mssql+pyodbcse você quiser evitar a dependência de drivers ODBC externos - Projetos onde você pode responder a mudanças na API e realizar testes de regressão
Quando NÃO usar o pré-lançamento:
- Sistemas de produção com requisitos rigorosos de estabilidade
- Aplicações legadas de vários anos onde atualizações de dependência são raras
- Cargas de trabalho empresariais críticas até que SQLAlchemy 2.1 atinja a disponibilidade geral estável
Para consultar o status mais recente dos dialetos em pré-lançamento e os problemas conhecidos, confira o repositório GitHub do mssql-python.
Solução de problemas
Não há nenhum módulo chamado 'sqlalchemy.dialects.mssql.mssqlpython'
Esse erro significa que sua versão instalada do SQLAlchemy não inclui o dialeto mssql-python. Verifique se você tem a versão 2.1.0b2 ou posterior:
pip install "sqlalchemy>=2.1.0b2"
Falhas na conexão
Se create_engine der certo e as consultas falharem, verifique se seus parâmetros de conexão funcionam diretamente com mssql-python:
import mssql_python
conn = mssql_python.connect(
"Server=localhost;Database=<database>;UID=dbuser;PWD=<password>;Encrypt=yes"
)
cursor = conn.cursor()
cursor.execute("SELECT 1")
print(cursor.fetchone())
conn.close()
Se a conexão direta funcionar, mas SQLAlchemy não, verifique se há problemas de codificação de URL em caracteres especiais dentro da sua senha ou nome do servidor.