Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
SQLAlchemy è il toolkit Python ORM e database più utilizzato. A partire da SQLAlchemy 2.1.0b2, un dialetto integrato per il driver mssql-python ti permette di usare SQLAlchemy ORM e Core con Microsoft SQL e database SQL di Azure.
Importante
Il dialetto mssql-python è stato aggiunto in SQLAlchemy 2.1.0b2 (rilasciato il 16 aprile 2026). SQLAlchemy 2.1 è attualmente una serie pre-release e non è raccomandata per l'uso in produzione. Prima di aggiornare da SQLAlchemy 2.0, capisci:
- Le API potrebbero cambiare prima della release stabile finale (2.1 GA)
- Testa accuratamente il carico di lavoro prima della distribuzione
- Usa la versione stabile di SQLAlchemy 2.0.x per i sistemi di produzione finché la 2.1 non raggiunge lo stato GA
-
Fissa la tua dipendenza a una versione specifica (ad esempio,
sqlalchemy==2.1.0b2) invece di usare intervalli di versioni
Consulta la sezione Limitazioni Note per i dettagli su quando utilizzare le versioni pre-release.
Prerequisiti
- Python 3.10 o versione successiva. SQLAlchemy 2.1 ha eliminato il supporto per Python 3.9 e precedenti.
- I pacchetti
mssql-pythonesqlalchemy(2.1.0b2 o versione successiva).
Gli esempi in questo articolo utilizzano il database di esempio AdventureWorksLT . Se non hai installato AdventureWorksLT, consulta i database di esempio di AdventureWorks.
Installa la versione preliminare
Poiché SQLAlchemy 2.1 è in beta, pip install sqlalchemy installa di default l'ultima versione stabile 2.0.x. Installa esplicitamente il pre-release:
pip install mssql-python "sqlalchemy>=2.1.0b2"
Verificare la versione installata:
import sqlalchemy
print(sqlalchemy.__version__) # Should show 2.1.0b2 or later
URL di connessione
Il dialetto mssql-python usa mssql+mssqlpython come schema URL. Il formato generale è:
mssql+mssqlpython://<username>:<password>@<host>:<port>/<database>
Autenticazione SQL
Per l'autenticazione SQL, includere nome utente e password nell'URL di connessione:
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>"
)
Autenticazione Microsoft Entra
Per l'autenticazione con Microsoft Entra, usa un nome utente vuoto e il parametro di query authentication:
from sqlalchemy import create_engine
engine = create_engine(
"mssql+mssqlpython://@<server>.database.windows.net/<database>"
"?authentication=ActiveDirectoryDefault&encrypt=yes"
)
Note
ActiveDirectoryDefault usa DefaultAzureCredential, che prova più fornitori di credenziali in sequenza. La prima connessione può essere lenta perché l'SDK percorre la catena finché non trova un fornitore funzionante. In produzione, se sai quale tipo di credenziale utilizza il tuo ambiente, specificalo direttamente (ad esempio, ActiveDirectoryMSI per l'identità gestita) per evitare il chain walk. Per altre informazioni, vedere Autenticazione di Microsoft Entra.
Creare URL a livello di codice
Da usare sqlalchemy.engine.URL.create per evitare la codifica manuale degli URL:
from sqlalchemy.engine import URL
url = URL.create(
"mssql+mssqlpython",
username="dbuser",
password="<password>",
host="localhost",
port=1433,
database="<database>",
)
engine = create_engine(url)
Definire modelli ORM
Usa la mappatura dichiarativa di SQLAlchemy per definire modelli che si mappano alle tabelle SQL di Microsoft.
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
Microsoft SQL utilizza IDENTITY l'incremento automatico delle colonne.
SQLAlchemy mappa automaticamente questo parametro per le colonne chiave primarie intere. L'esplicito Identity() mostrato sopra è opzionale, a meno che tu non debba controllare i valori di inizio e incremento.
Operazioni CRUD
I seguenti esempi mostrano come inserire, interrogare, aggiornare ed eliminare righe utilizzando la sessione ORM. Ogni esempio riutilizza new_id, il valore ProductID restituito quando viene inserita una riga. Per eseguire tutte e quattro le operazioni insieme, vedi l'esempio completo.
Creare una sessione
Crea una sessione per eseguire operazioni all'interno di una transazione:
from sqlalchemy.orm import Session
with Session(engine) as session:
# Use session for queries and modifications
pass
Per applicazioni che creano molte sessioni, si utilizza sessionmaker:
from sqlalchemy.orm import sessionmaker
SessionLocal = sessionmaker(bind=engine)
Inserire righe
Aggiungi un nuovo prodotto, esegui il commit della sessione e acquisisci il tag ProductID generato per i seguenti esempi:
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
In SalesLT.Product, sia Name che ProductNumber hanno vincoli unici. Se esegui questo insert più di una volta, cambia questi valori o elimina prima la riga precedente.
L'esempio completo elimina la riga che crea, così da poterlo eseguire ripetutamente.
Righe di query
Recupera una singola riga tramite chiave primaria, oppure usa select() per query filtrate:
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}")
Aggiornamento delle righe
Modifica un campo su una riga esistente e effettua un commit:
with Session(engine) as session:
product = session.get(Product, new_id)
if product:
product.list_price = Decimal("1349.99")
session.commit()
Elimina righe
Rimuovi una riga e fai il commit di:
with Session(engine) as session:
product = session.get(Product, new_id)
if product:
session.delete(product)
session.commit()
Esempio completo
Le sezioni precedenti mostravano ogni pezzo separatamente. Questa sezione li combina in un unico script autonomo che puoi copiare, eseguire e ripetere.
Creare un file denominato crud.py e aggiungere il codice seguente. Sostituisci i dettagli create_engine di connessione con i tuoi (vedi URL di connessione):
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}")
Eseguire lo script:
python crud.py
Si vedono risultati simili ai seguenti:
Inserted ProductID: 1019
Read: Classic Road Bike costs $1299.9900
Updated price to $1349.9900
Deleted ProductID: 1019
Lo script elimina la riga che crea, così non colpisce i vincoli unici su Name e ProductNumber quando lo esegui di nuovo. Ogni esecuzione inserisce una nuova riga, quindi il valore di ProductID aumenta ogni volta.
Query principali
SQLAlchemy Core fornisce un'API di espressioni SQL di livello inferiore. Puoi usare Core con lo stesso motore e definizioni di tabelle, incluse le classi mappate con ORM.
from sqlalchemy import text
with engine.connect() as conn:
result = conn.execute(text("SELECT @@VERSION"))
print(result.scalar())
Usa costrutti a livello di tabella per la generazione di SQL con controllo dei tipi:
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 selezioni colonne mappate singole il cui nome del database differisce dal nome dell'attributo (ad esempio, Product.name mappa alla Name colonna), le righe Core sono codificate dal nome della colonna del database. Aggiungi .label("name") per accedere al valore come row.name invece di row.Name.
Pool di connessioni
SQLAlchemy gestisce di default un pool di connessioni. Regola le impostazioni del pool per il tuo carico di lavoro:
engine = create_engine(
"mssql+mssqlpython://dbuser:<password>@localhost/<database>",
pool_size=10,
max_overflow=20,
pool_timeout=30,
pool_recycle=3600,
)
| Parametro | Descrizione |
|---|---|
pool_size |
Numero di connessioni da mantenere aperte (predefinito: 5). |
max_overflow |
Connessioni consentite oltre pool_size (predefinito: 10). |
pool_timeout |
Pochi secondi per aspettare una connessione prima di generare un errore (predefinito: 30). |
pool_recycle |
Pochi secondi dopo di che una connessione viene riciclata (predefinito: -1, disabilitato). Imposta questo valore se il tuo database chiude le connessioni inattive. |
Utilizzo con framework web
SQLAlchemy è comunemente utilizzato come livello database per Flask e FastAPI. Il dialetto mssql-python funziona con qualsiasi framework che supporti SQLAlchemy.
I seguenti estratti mostrano il modello consigliato sessione-per-richiesta per ciascun framework. Sono frammenti illustrativi che assumono il engine modello e Product dalle sezioni precedenti, non app complete. Per applicazioni complete ed eseguibili, consulta gli articoli sull'integrazione FastAPI e sull'integrazione Flask .
Esempio di FastAPI
Usa una dipendenza dal generatore per fornire una sessione per richiesta:
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)}
Esempio di Flask
Usa un gestore di contesto per definire la sessione in base alla richiesta:
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)})
Migrazioni alambiche
Alembic gestisce le migrazioni degli schemi per i progetti SQLAlchemy e funziona con il dialetto mssql-python. La funzione autogenerate di Alembic confronta i tuoi modelli con il database live, quindi qualche passaggio in più impedisce che proponga modifiche alle tabelle che non gestisci.
Istituire Alembic
Installa Alembic e inizializza una directory di migrazione:
pip install alembic
alembic init migrations
In alembic.ini, imposta l'URL di connessione:
sqlalchemy.url = mssql+mssqlpython://dbuser:<password>@localhost/<database>
Indirizza Alembic ai tuoi modelli
Autogenerate ha bisogno dei metadati dei tuoi modelli. Inserisci i modelli gestiti da Alembic in un modulo importabile, come models.py. Poiché autogenerate propone di eliminare qualsiasi colonna omessa da un modello, definisci un modello che possiede completamente la sua tabella invece di riutilizzare il modello semplificato Product di questo articolo precedente:
# 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())
Attenzione
Per impostazione predefinita, autogenerate considera rimossa ogni tabella nel database che non è in target_metadata e genera drop_table per essa. Contro un database esistente come AdventureWorksLT, quell'azione può far cadere decine di tabelle. Aggiungi un include_name filtro così Alembic gestisce solo le tabelle definite dai tuoi modelli, e rivedi sempre lo script generato prima di applicarlo.
In migrations/env.py, sostituisci target_metadata = None con il seguente codice. Importa i tuoi modelli e limita la generazione automatica agli schemi e alle tabelle che definiscono:
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
Passa include_name e include_schemas=True a context.configure sia in run_migrations_offline sia in run_migrations_online. L'impostazione include_schemas=True permette ad Alembic di vedere le tabelle in schemi non predefiniti come SalesLT:
context.configure(
connection=connection,
target_metadata=target_metadata,
include_name=include_name,
include_schemas=True,
)
Genera e applica una migrazione
Genera una migrazione dai tuoi modelli:
alembic revision --autogenerate -m "add product review table"
Alembic rileva la nuova tabella e scrive uno script di migrazione:
INFO [alembic.autogenerate.compare.tables] Detected added table 'SalesLT.ProductReview'
Generating .../versions/xxxx_add_product_review_table.py ... done
Il comando generato upgrade() crea la tabella, e downgrade() la rimuove:
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")
Rivedi lo script e poi applica tutte le migrazioni in sospeso:
alembic upgrade head
Differenze rispetto al dialetto pyodbc
Se stai migrando da mssql+pyodbc, il dialetto mssql-python è simile perché entrambi i driver si basano sullo stesso framework ODBC. Differenze principali:
| Argomento | mssql+pyodbc |
mssql+mssqlpython |
|---|---|---|
| Installazione del driver ODBC | Richiede un driver ODBC separato (ad esempio, ODBC Driver 18 per Microsoft SQL). | Il driver è incluso in bundle. Non serve un driver ODBC separato. |
| URL connessione | mssql+pyodbc://user:pass@host/db?driver=ODBC+Driver+18+for+SQL+Server |
mssql+mssqlpython://user:pass@host/db |
fast_executemany |
Supportato tramite create_engine(..., fast_executemany=True). |
Non applicabile. Il driver gestisce internamente le prestazioni dell'elaborazione batch. |
| Disponibilità | Stabile, inclusa in SQLAlchemy fin dalla versione 1.x. | Versione preliminare (SQLAlchemy 2.1.0b2+). |
Limitazioni note
Il dialetto mssql-python per SQLAlchemy è in pre-release. Prima di usarlo in produzione, comprendi queste implicazioni:
Modifiche API: Firme di metodo, tipi di eccezione e comportamento potrebbero cambiare prima della release stabile finale. Fissa sempre la tua versione SQLAlchemy a una build pre-release specifica (ad esempio,
sqlalchemy==2.1.0b2) e testa accuratamente gli aggiornamenti.Test limitati: Il dialetto ha meno test comunitari rispetto al dialetto stabile
mssql+pyodbc. Potresti incontrare casi limitali o funzionalità mancanti.Lacune di funzionalità: alcune funzionalità avanzate di ORM o Core potrebbero non funzionare. Consulta la documentazione dei dialetti MSSQL di SQLAlchemy e testa i tuoi casi d'uso prima di impegnarti in un progetto.
Nessuna garanzia di supporto: Microsoft e SQLAlchemy forniscono un supporto al meglio dell'impegno, ma i problemi potrebbero non essere risolti prima della versione stabile.
Quando usare la versione preliminare:
- Ambienti di sviluppo e test
- Progetti di prova di concetto
- Migrazione da
mssql+pyodbcse vuoi evitare la dipendenza da driver ODBC esterno - Progetti in cui puoi rispondere a cambiamenti API ed eseguire test di regressione
Quando NON utilizzare la versione preliminare:
- Sistemi di produzione con requisiti di stabilità rigorosi
- Applicazioni legacy di lunga data in cui gli aggiornamenti delle dipendenze sono rari
- Carichi di lavoro aziendali critici in attesa che SQLAlchemy 2.1 raggiunga una versione GA stabile
Per lo stato dei dialetti pre-release più recenti e i problemi noti, controlla il repository GitHub di mssql-python.
Troubleshooting
"Nessun modulo chiamato 'sqlalchemy.dialects.mssql.mssqlpython'"
Questo errore significa che la versione installata di SQLAlchemy non include il dialetto mssql-python. Verifica di avere la versione 2.1.0b2 o successiva:
pip install "sqlalchemy>=2.1.0b2"
Errori di connessione
Se create_engine ha successo ma le query falliscono, verifica che i parametri di connessione funzionino direttamente con 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 la connessione diretta funziona ma SQLAlchemy no, controlla eventuali problemi di codifica URL in caratteri speciali all'interno della password o del nome del server.