Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
SQLAlchemy adalah toolkit Python ORM dan database yang paling banyak digunakan. Dimulai dengan SQLAlchemy 2.1.0b2, dialek bawaan untuk driver mssql-python memungkinkan Anda menggunakan SQLAlchemy ORM dan Core dengan Microsoft SQL dan Azure SQL Database.
Penting
Dialek mssql-python ditambahkan di SQLAlchemy 2.1.0b2 (dirilis 16 April 2026). SQLAlchemy 2.1 saat ini merupakan seri pra-rilis dan tidak direkomendasikan untuk penggunaan produksi. Sebelum memutakhirkan dari SQLAlchemy 2.0, pahami:
- API mungkin berubah sebelum rilis stabil akhir (2.1 GA)
- Uji beban kerja Anda secara menyeluruh sebelum penerapan
- Gunakan SQLAlchemy 2.0.x yang stabil untuk sistem produksi hingga 2.1 mencapai GA
-
Sematkan dependensi Anda ke versi tertentu (misalnya,
sqlalchemy==2.1.0b2) daripada menggunakan rentang versi
Lihat bagian Batasan yang Diketahui untuk detail tentang kapan harus menggunakan versi prarilis.
Prasyarat
- Python 3.10 atau yang lebih baru. SQLAlchemy 2.1 membatalkan dukungan untuk Python 3.9 dan yang lebih lama.
- Paket
mssql-pythondansqlalchemy(2.1.0b2 atau lebih baru).
Contoh dalam artikel ini menggunakan database sampel AdventureWorksLT . Jika Anda belum menginstal AdventureWorksLT, lihat Database sampel AdventureWorks.
Instal versi prarilis
Karena SQLAlchemy 2.1 dalam versi beta, pip install sqlalchemy menginstal rilis stabil terbaru 2.0.x secara default. Instal pra-rilis secara eksplisit:
pip install mssql-python "sqlalchemy>=2.1.0b2"
Verifikasi versi yang diinstal:
import sqlalchemy
print(sqlalchemy.__version__) # Should show 2.1.0b2 or later
URL koneksi
Dialek mssql-python digunakan mssql+mssqlpython sebagai skema URL. Format umumnya adalah:
mssql+mssqlpython://<username>:<password>@<host>:<port>/<database>
Autentikasi SQL
Untuk autentikasi SQL, sertakan nama pengguna dan kata sandi dalam URL koneksi:
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>"
)
Microsoft Entra otentikasi
Untuk autentikasi Microsoft Entra, gunakan nama pengguna kosong dan authentication parameter kueri:
from sqlalchemy import create_engine
engine = create_engine(
"mssql+mssqlpython://@<server>.database.windows.net/<database>"
"?authentication=ActiveDirectoryDefault&encrypt=yes"
)
Note
ActiveDirectoryDefault menggunakan DefaultAzureCredential, yang mencoba beberapa penyedia kredensial secara berurutan. Koneksi pertama bisa lambat karena SDK menelusuri rantai hingga menemukan penyedia yang berfungsi. Dalam lingkungan produksi, jika Anda mengetahui jenis kredensial yang digunakan oleh lingkungan Anda, tentukan secara langsung (misalnya, ActiveDirectoryMSI untuk identitas terkelola) agar terhindar dari penelusuran berantai. Untuk informasi selengkapnya, lihat Autentikasi Microsoft Entra.
Membuat URL secara terprogram
Gunakan sqlalchemy.engine.URL.create untuk menghindari pengodean URL manual:
from sqlalchemy.engine import URL
url = URL.create(
"mssql+mssqlpython",
username="dbuser",
password="<password>",
host="localhost",
port=1433,
database="<database>",
)
engine = create_engine(url)
Tentukan model ORM
Gunakan pemetaan deklaratif SQLAlchemy untuk menentukan model yang dipetakan ke tabel Microsoft SQL.
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 digunakan IDENTITY untuk kolom penambahan otomatis.
SQLAlchemy memetakan ini secara otomatis untuk kolom kunci primer bilangan bulat. Eksplisit Identity() yang ditampilkan di atas bersifat opsional kecuali Anda perlu mengontrol nilai awal dan peningkatan.
Operasi CRUD
Contoh berikut menunjukkan cara menyisipkan, mengkueri, memperbarui, dan menghapus baris dengan menggunakan sesi ORM. Setiap contoh menggunakan kembali new_id, yaitu ProductID yang dikembalikan ketika Anda menyisipkan baris. Untuk menjalankan keempat operasi secara bersamaan, lihat contoh lengkapnya.
Membuat sesi
Buat sesi untuk menjalankan operasi dalam transaksi:
from sqlalchemy.orm import Session
with Session(engine) as session:
# Use session for queries and modifications
pass
Untuk aplikasi yang membuat banyak sesi, gunakan sessionmaker:
from sqlalchemy.orm import sessionmaker
SessionLocal = sessionmaker(bind=engine)
Sisipkan baris
Tambahkan produk baru, terapkan sesi, dan tangkap produk yang dihasilkan ProductID untuk contoh berikut:
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
Di SalesLT.Product, keduanya Name dan ProductNumber memiliki batasan unik. Jika Anda menjalankan sisipan ini lebih dari sekali, ubah nilai ini atau hapus baris sebelumnya terlebih dahulu.
Contoh lengkap menghapus baris yang dibuatnya, sehingga dapat berjalan berulang kali.
Baris kueri
Ambil satu baris dengan kunci utama, atau gunakan select() untuk kueri yang difilter:
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}")
Memperbarui baris
Ubah bidang pada baris yang ada dan terapkan:
with Session(engine) as session:
product = session.get(Product, new_id)
if product:
product.list_price = Decimal("1349.99")
session.commit()
Menghapus baris
Hapus satu baris dan lakukan commit:
with Session(engine) as session:
product = session.get(Product, new_id)
if product:
session.delete(product)
session.commit()
Contoh lengkap
Bagian sebelumnya menunjukkan setiap bagian secara terpisah. Bagian ini menggabungkannya menjadi satu skrip mandiri yang dapat Anda salin, jalankan, dan jalankan lagi.
Buat file bernama crud.py dan tambahkan kode berikut. Ganti detail koneksi dengan detail create_engine Anda sendiri (lihat URL koneksi):
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}")
Jalankan skrip:
python crud.py
Anda melihat output yang mirip dengan berikut ini:
Inserted ProductID: 1019
Read: Classic Road Bike costs $1299.9900
Updated price to $1349.9900
Deleted ProductID: 1019
Skrip menghapus baris yang dibuatnya, sehingga tidak mencapai batasan unik pada Name dan ProductNumber saat Anda menjalankannya lagi. Setiap eksekusi menyisipkan baris baru, sehingga ProductID meningkat setiap kali.
Kueri utama
SQLAlchemy Core menyediakan API ekspresi SQL tingkat rendah. Anda dapat menggunakan Core dengan definisi mesin dan tabel yang sama, termasuk kelas yang dipetakan ORM.
from sqlalchemy import text
with engine.connect() as conn:
result = conn.execute(text("SELECT @@VERSION"))
print(result.scalar())
Gunakan konstruksi tingkat tabel untuk pembuatan SQL yang aman untuk jenis:
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
Saat Anda memilih kolom yang dipetakan secara individual yang nama database-nya berbeda dari nama atribut (misalnya, Product.name dipetakan ke kolom Name), baris Core diidentifikasi berdasarkan nama kolom database. Tambahkan .label("name") untuk mengakses nilai sebagai row.name alih-alih row.Name.
Pemanfaatan koneksi
SQLAlchemy mengelola kumpulan koneksi secara default. Sesuaikan pengaturan pool sesuai beban kerja Anda:
engine = create_engine(
"mssql+mssqlpython://dbuser:<password>@localhost/<database>",
pool_size=10,
max_overflow=20,
pool_timeout=30,
pool_recycle=3600,
)
| Parameter | Deskripsi |
|---|---|
pool_size |
Jumlah koneksi yang harus tetap terbuka (default: 5). |
max_overflow |
Koneksi diizinkan melampaui pool_size (default: 10). |
pool_timeout |
Detik untuk menunggu koneksi sebelum memunculkan kesalahan (default: 30). |
pool_recycle |
Detik setelah koneksi didaur ulang (default: -1, dinonaktifkan). Tetapkan nilai ini jika database Anda menutup koneksi yang tidak aktif. |
Gunakan dengan kerangka kerja web
SQLAlchemy biasanya digunakan sebagai lapisan database untuk Flask dan FastAPI. Dialek mssql-python bekerja dengan kerangka kerja apa pun yang mendukung SQLAlchemy.
Cuplikan berikut menunjukkan pola sesi per permintaan yang direkomendasikan untuk setiap kerangka kerja. Itu sekadar fragmen ilustratif yang mengasumsikan model engine dan Product yang dibahas pada bagian sebelumnya, bukan aplikasi yang lengkap. Untuk aplikasi lengkap yang dapat dijalankan, lihat artikel integrasi FastAPI dan integrasi Flask .
Contoh FastAPI
Gunakan dependensi generator untuk menyediakan sesi per permintaan:
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)}
Contoh Flask
Gunakan manajer konteks untuk membatasi sesi ke permintaan tersebut:
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)})
Migrasi Alembic
Alembic menangani migrasi skema untuk proyek SQLAlchemy , dan bekerja dengan dialek mssql-python. Fitur pembuatan otomatis Alembic membandingkan model Anda dengan database langsung, jadi beberapa langkah tambahan mencegahnya mengusulkan perubahan pada tabel yang tidak Anda kelola.
Siapkan Alembic
Instal Alembic dan inisialisasi direktori migrasi:
pip install alembic
alembic init migrations
Di alembic.ini, atur URL koneksi:
sqlalchemy.url = mssql+mssqlpython://dbuser:<password>@localhost/<database>
Arahkan Alembic ke model Anda
Pembuatan otomatis membutuhkan metadata model Anda. Tempatkan model yang dikelola Alembic dalam modul yang dapat diimpor, seperti models.py. Karena autogenerate mengusulkan untuk menghapus kolom apa pun yang dihilangkan oleh model, tentukan model yang sepenuhnya memiliki tabelnya daripada menggunakan kembali model yang disederhanakan Product dari sebelumnya dalam artikel ini:
# 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())
Caution
Secara bawaan, autogenerate menganggap setiap tabel dalam database yang tidak ada di target_metadata sebagai telah dihapus dan menghasilkan drop_table untuk tabel tersebut. Pada basis data yang sudah ada seperti AdventureWorksLT, tindakan itu dapat menghapus lusinan tabel. Tambahkan filter include_name sehingga Alembic hanya mengelola tabel yang ditentukan model Anda, dan selalu tinjau skrip yang dihasilkan sebelum Anda menerapkannya.
Di migrations/env.py, ganti target_metadata = None dengan kode berikut. Ini mengimpor model Anda dan membatasi pembuatan otomatis ke skema dan tabel yang mereka tentukan:
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
Teruskan include_name dan include_schemas=True ke context.configure baik di run_migrations_offline maupun run_migrations_online. Pengaturan ini include_schemas=True memungkinkan Alembic melihat tabel dalam skema nondefault seperti SalesLT:
context.configure(
connection=connection,
target_metadata=target_metadata,
include_name=include_name,
include_schemas=True,
)
Membuat dan menerapkan migrasi
Buat migrasi dari model Anda:
alembic revision --autogenerate -m "add product review table"
Alembic mendeteksi tabel baru dan menulis skrip migrasi:
INFO [alembic.autogenerate.compare.tables] Detected added table 'SalesLT.ProductReview'
Generating .../versions/xxxx_add_product_review_table.py ... done
upgrade() yang dihasilkan membuat tabel, dan downgrade() menghapusnya:
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")
Tinjau skrip, lalu terapkan semua migrasi yang tertunda:
alembic upgrade head
Perbedaan dari dialek pyodbc
Jika Anda bermigrasi dari mssql+pyodbc, dialek mssql-python serupa karena kedua driver didasarkan pada kerangka kerja ODBC yang sama. Perbedaan utama:
| Topik | mssql+pyodbc |
mssql+mssqlpython |
|---|---|---|
| Instalasi driver ODBC | Memerlukan driver ODBC terpisah (misalnya, Driver ODBC 18 untuk Microsoft SQL). | Driver disertakan. Tidak diperlukan driver ODBC terpisah. |
| URL Koneksi | mssql+pyodbc://user:pass@host/db?driver=ODBC+Driver+18+for+SQL+Server |
mssql+mssqlpython://user:pass@host/db |
fast_executemany |
Didukung melalui create_engine(..., fast_executemany=True). |
Tidak dapat diterapkan. driver menangani kinerja batch secara internal. |
| Availability | Stabil, termasuk dalam SQLAlchemy sejak 1.x. | Pra-rilis (SQLAlchemy 2.1.0b2+). |
Batasan yang Diketahui
Dialek mssql-python untuk SQLAlchemy berada dalam pra-rilis. Sebelum digunakan dalam produksi, pahami implikasi ini:
Perubahan API: Tanda tangan metode, jenis pengecualian, dan perilaku dapat berubah sebelum rilis stabil akhir. Selalu sematkan versi SQLAlchemy Anda ke build pra-rilis tertentu (misalnya,
sqlalchemy==2.1.0b2) dan uji peningkatan secara menyeluruh.Pengujian Terbatas: Dialek ini memiliki pengujian komunitas yang lebih sedikit daripada dialek stabil
mssql+pyodbc. Anda mungkin menemukan kasus tepi atau fitur yang hilang.Kesenjangan Fitur: Beberapa fitur ORM atau Inti tingkat lanjut mungkin tidak berfungsi. Lihat dokumentasi dialek SQLAlchemy MSSQL dan uji kasus penggunaan Anda sebelum berkomitmen pada proyek.
Tidak Ada Jaminan Dukungan: Microsoft dan SQLAlchemy memberikan dukungan terbaik, tetapi masalah mungkin tidak terselesaikan sebelum rilis stabil.
Kapan Menggunakan Pra-Rilis:
- Lingkungan pengembangan dan pengujian
- Proyek pembuktian konsep
- Bermigrasi dari
mssql+pyodbcjika Anda ingin menghindari dependensi driver ODBC eksternal - Proyek tempat Anda dapat merespons perubahan API dan melakukan pengujian regresi
Kapan TIDAK Menggunakan Pra-Rilis:
- Sistem produksi dengan persyaratan stabilitas yang ketat
- Aplikasi warisan yang telah digunakan selama bertahun-tahun dan jarang mengalami pembaruan dependensi
- Beban kerja bisnis penting hingga SQLAlchemy 2.1 mencapai GA yang stabil
Untuk status dialek pra-rilis terbaru dan masalah yang diketahui, periksa repositori GitHub mssql-python.
Troubleshooting
"Tidak ada modul bernama 'sqlalchemy.dialects.mssql.mssqlpython'"
Kesalahan ini berarti versi SQLAlchemy yang Anda instal tidak menyertakan dialek mssql-python. Pastikan Anda memiliki 2.1.0b2 atau yang lebih baru:
pip install "sqlalchemy>=2.1.0b2"
Kegagalan koneksi
Jika create_engine berhasil tetapi kueri gagal, verifikasi parameter koneksi Anda berfungsi dengan mssql-python secara langsung:
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()
Jika koneksi langsung berfungsi, tetapi SQLAlchemy tidak, periksa masalah pengkodean URL dalam karakter khusus dalam kata sandi atau nama server Anda.