Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
SQLAlchemy — самый широко используемый набор инструментов для Python ORM и баз данных. Начиная со SQLAlchemy 2.1.0b2, встроенный диалект драйвера mssql-python позволяет использовать SQLAlchemy ORM и Core с Microsoft SQL и База данных SQL Azure.
Important
Диалект mssql-python был добавлен в SQLAlchemy 2.1.0b2 (выпущен 16 апреля 2026 года). SQLAlchemy 2.1 в настоящее время является предрелизной серией и не рекомендуется для использования в производстве. Перед обновлением с SQLAlchemy 2.0 поймите:
- API могут измениться до финального стабильного релиза (2.1 GA)
- Тщательно протестируйте свою нагрузку перед развертыванием
- Используйте стабильную версию SQLAlchemy 2.0.x для производственных систем до выхода общедоступной версии 2.1
-
Прикрепите зависимость к конкретной версии (например,
sqlalchemy==2.1.0b2) вместо использования диапазонов версий
См. раздел «Известные ограничения » для подробностей о том, когда использовать версии до релиза.
Необходимые условия
- Python 3.10 или более поздней версии. SQLAlchemy 2.1 прекратила поддержку Python 3.9 и более ранних версий.
- Пакеты
mssql-pythonиsqlalchemy(2.1.0b2 или новее).
Примеры в этой статье используют базу данных AdventureWorksLT . Если у вас не установлен AdventureWorksLT, посмотрите примеры баз данных AdventureWorks.
Установите пре-релиз
Поскольку SQLAlchemy 2.1 находится в бета-версии, pip install sqlalchemy по умолчанию устанавливается последний стабильный релиз 2.0.x. Установите пререлиз явно:
pip install mssql-python "sqlalchemy>=2.1.0b2"
Проверьте установленную версию:
import sqlalchemy
print(sqlalchemy.__version__) # Should show 2.1.0b2 or later
URL-адреса соединений
Диалект mssql-python mssql+mssqlpython используется в качестве схемы URL. Общий формат:
mssql+mssqlpython://<username>:<password>@<host>:<port>/<database>
Проверка подлинности SQL
Для SQL-аутентификации указывайте имя пользователя и пароль в URL соединения:
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
Для аутентификации Microsoft Entra используйте пустое имя пользователя и authentication параметр запроса:
from sqlalchemy import create_engine
engine = create_engine(
"mssql+mssqlpython://@<server>.database.windows.net/<database>"
"?authentication=ActiveDirectoryDefault&encrypt=yes"
)
Замечание
ActiveDirectoryDefault использует DefaultAzureCredential, который последовательно пробует несколько поставщиков учетных данных. Первое соединение может быть медленным, потому что SDK идёт по цепочке, пока не найдёт работающего провайдера. В продакшене, если вы знаете, какой тип учетных данных использует ваша среда, укажите его напрямую (например, ActiveDirectoryMSI для управляемой идентичности), чтобы избежать цепной ходьбы. Дополнительные сведения см. в разделе проверки подлинности Microsoft Entra.
Программное построение URL
Используйте sqlalchemy.engine.URL.create для предотвращения ручного кодирования 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)
Определить модели ORM
Используйте декларативное отображение SQLAlchemy, чтобы определить модели, соответствующие таблицам 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 использует IDENTITY для автоинкрементируемых столбцов.
SQLAlchemy автоматически отображает это для целочисленных столбцов первичных ключей. Приведённое выше явное Identity() указание необязательно, если только вам не нужно управлять начальными и инкрементными значениями.
CRUD-операции
Следующие примеры показывают, как вставлять, задавать запросы, обновлять и удалять строки с помощью сессии ORM. В каждом примере повторно используется new_id — ProductID, возвращаемое при вставке строки. Чтобы выполнить все четыре операции вместе, см. полный пример.
Создание сеанса
Создайте сессию для выполнения операций внутри транзакции:
from sqlalchemy.orm import Session
with Session(engine) as session:
# Use session for queries and modifications
pass
Для приложений, создающих множество сессий, используйте sessionmaker:
from sqlalchemy.orm import sessionmaker
SessionLocal = sessionmaker(bind=engine)
Вставка строк
Добавьте новый продукт, зафиксируйте сессию и зафиксируйте сгенерированный ProductID материал для следующих примеров:
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}")
Замечание
В SalesLT.Product и Name, и ProductNumber имеют уникальные ограничения. Если вы запускаете эту вставку несколько раз, сначала измените эти значения или удалите предыдущую строку.
Полный пример удаляет созданную строку, чтобы она могла выполняться многократно.
Строки запросов
Получите одну строку по первичному ключу или используйте select() для отфильтрованных запросов:
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}")
Обновление строк
Измените поле на существующей строке и зафиксируйте:
with Session(engine) as session:
product = session.get(Product, new_id)
if product:
product.list_price = Decimal("1349.99")
session.commit()
Удаление строк
Удалите строку и сделайте коммит:
with Session(engine) as session:
product = session.get(Product, new_id)
if product:
session.delete(product)
session.commit()
Полный пример
В предыдущих разделах каждая работа была показана отдельно. Этот раздел объединяет их в один самостоятельный скрипт, который можно копировать, запускать и запускать заново.
Создайте файл с именем crud.py и добавьте следующий код. Замените данные create_engine соединения на свои (см. URL соединений):
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}")
Запустите скрипт:
python crud.py
Вы видите выход, похожий на следующий:
Inserted ProductID: 1019
Read: Classic Road Bike costs $1299.9900
Updated price to $1349.9900
Deleted ProductID: 1019
Скрипт удаляет строку, которую он создаёт, поэтому при повторном запуске не нарушает ограничения уникальности для Name и ProductNumber. Каждый запуск добавляет новую строку, поэтому ProductID увеличивается при каждом запуске.
Основные запросы
SQLAlchemy Core предоставляет низкоуровневый API для SQL-выражений. Вы можете использовать Core с тем же движком и определениями таблиц, включая классы, отображаемые в ORM.
from sqlalchemy import text
with engine.connect() as conn:
result = conn.execute(text("SELECT @@VERSION"))
print(result.scalar())
Используйте конструкции на уровне таблиц для генерации SQL, безопасной с точки зрения типов:
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()
Замечание
Когда вы выбираете отдельные сопоставленные столбцы, имя которых в базе данных отличается от имени атрибута (например, Product.name сопоставляется со столбцом Name), строки Core используют имя столбца базы данных в качестве ключа. Добавьте .label("name") для доступа к значению как row.name вместо row.Name.
Пулинг соединений
SQLAlchemy по умолчанию управляет пулом соединений. Настройте настройки пула под вашу нагрузку:
engine = create_engine(
"mssql+mssqlpython://dbuser:<password>@localhost/<database>",
pool_size=10,
max_overflow=20,
pool_timeout=30,
pool_recycle=3600,
)
| Parameter | Описание |
|---|---|
pool_size |
Количество подключений, которые нужно держать открытыми (по умолчанию: 5). |
max_overflow |
Разрешённые соединения дальше pool_size (по умолчанию: 10). |
pool_timeout |
Секунды на ожидание соединения, прежде чем появится ошибка (по умолчанию: 30). |
pool_recycle |
Через несколько секунд соединение перерабатывается (по умолчанию: -1, отключен). Установите это значение, если ваша база данных закрывает холостые соединения. |
Использование с веб-фреймворками
SQLAlchemy обычно используется в качестве уровня базы данных для Flask и FastAPI. Диалект mssql-python работает с любым фреймворком, поддерживающим SQLAlchemy.
Следующие фрагменты показывают рекомендуемый шаблон сессии на запрос для каждого фреймворка. Это иллюстративные фрагменты, которые основаны на модели engineиProduct, описанной в предыдущих разделах, а не полноценные приложения. Для полностью готовых к запуску приложений см. статьи об интеграции FastAPI и об интеграции Flask.
Пример FastAPI
Используйте зависимость генератора, чтобы предоставить сессию для каждого запроса:
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)}
Пример Flask
Используйте менеджер контекста, чтобы ограничить сессию рамками запроса:
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)})
Алембийские миграции
Alembic занимается миграцией схем для проектов SQLAlchemy и работает с диалектом mssql-python. Функция автогенерации Alembic сравнивает ваши модели с живой базой данных, поэтому несколько дополнительных шагов не позволяют ей предлагать изменения в таблицах, которыми вы не управляете.
Настройте Alembic
Установите Alembic и инициализуйте каталог миграций:
pip install alembic
alembic init migrations
В alembic.ini установите URL-адрес подключения:
sqlalchemy.url = mssql+mssqlpython://dbuser:<password>@localhost/<database>
Направьте Alembic на свои модели
Автогенерация требует метаданных ваших моделей. Поместите модели, которыми управляет Alembic, в импортируемый модуль, например models.py. Поскольку автогенерация предлагает убрать любой столбц, который модель опускает, определить модель, которая полностью владеет своей таблицей, а не повторяет упрощённую Product модель, описанную ранее в этой статье:
# 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())
Предостережение
По умолчанию автогенерация считает каждую таблицу в базе данных, которая не входит target_metadata , удаленной и эмитирует drop_table для неё. При работе с существующей базой данных, такой как AdventureWorksLT, это действие может удалить десятки таблиц. Добавьте include_name фильтр, чтобы Alembic управлял только таблицами, которые определяют ваши модели, и всегда проверяйте сгенерированный скрипт перед его применением.
В migrations/env.py замените target_metadata = None на следующий код. Он импортирует ваши модели и ограничивает автогенерацию только схемами и таблицами, которые они определяют:
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
Передайте include_name и include_schemas=True в context.configure как в run_migrations_offline, так и в run_migrations_online. Настройка include_schemas=True позволяет Alembic видеть таблицы в нестандартных схемах, таких как SalesLT:
context.configure(
connection=connection,
target_metadata=target_metadata,
include_name=include_name,
include_schemas=True,
)
Сгенерируйте и примените миграцию
Сгенерируйте миграцию из ваших моделей:
alembic revision --autogenerate -m "add product review table"
Alembic обнаруживает новую таблицу и пишет скрипт миграции:
INFO [alembic.autogenerate.compare.tables] Detected added table 'SalesLT.ProductReview'
Generating .../versions/xxxx_add_product_review_table.py ... done
Сгенерированный upgrade() создаёт таблицу и downgrade() сбрасывает её:
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")
Просмотрите скрипт, а затем примените все незавершённые миграции:
alembic upgrade head
Отличия от диалекта pyodbc
Если вы переходите с mssql+pyodbc, диалект mssql-python похож, потому что оба драйвера основаны на одном и том же фреймворке ODBC. Основные различия:
| Тема | mssql+pyodbc |
mssql+mssqlpython |
|---|---|---|
| Установка драйверов ODBC | Требуется отдельный драйвер ODBC (например, ODBC Driver 18 для Microsoft SQL). | Драйвер в комплекте. Отдельный драйвер ODBC не требуется. |
| URL-адрес соединения | mssql+pyodbc://user:pass@host/db?driver=ODBC+Driver+18+for+SQL+Server |
mssql+mssqlpython://user:pass@host/db |
fast_executemany |
Поддерживается через create_engine(..., fast_executemany=True). |
Неприменимо. Драйвер самостоятельно обеспечивает пакетную обработку. |
| Availability | Стабильный, включённый в SQLAlchemy начиная с версии 1.x. | Предварительный выпуск (SQLAlchemy 2.1.0b2+). |
Известные ограничения
Диалект mssql-python для SQLAlchemy находится в предварительном релизе. Перед использованием в производстве понимайте следующие последствия:
Изменения в API: Сигнатуры методов, типы исключений и поведение могут измениться до финального стабильного релиза. Всегда привязывайте свою версию SQLAlchemy к конкретной сборке до релиза (например)
sqlalchemy==2.1.0b2и тщательно тестируйте обновления.Ограниченное тестирование: У диалекта меньше тестирования в сообществах, чем у стабильного
mssql+pyodbc. Вы можете столкнуться с крайними случаями или отсутствующими функциями.Пробелы в функциях: некоторые продвинутые функции ORM или Core могут не работать. Обратитесь к документации по диалекту SQLAlchemy MSSQL и протестируйте свои сценарии использования перед тем, как браться за проект.
Нет гарантии поддержки: Microsoft и SQLAlchemy обеспечивают поддержку с максимальными усилиями, но проблемы могут не быть решены до стабильного релиза.
Когда использовать пре-релиз:
- среды для разработки и тестирования;
- Проекты по демонстрации концепции
- Переход с
mssql+pyodbc, если вы хотите избежать зависимости от внешнего драйвера ODBC - Проекты, где можно реагировать на изменения API и проводить регрессионное тестирование
Когда НЕ стоит использовать пре-релиз:
- Производственные системы с строгими требованиями к стабильности
- Многолетние устаревшие приложения, где обновления зависимостей встречаются редко
- Критически важные бизнес-нагрузки до тех пор, пока SQLAlchemy 2.1 не получит статус общедоступной версии
Чтобы узнать актуальный предрелизный статус диалекта и известные проблемы, см. репозиторий mssql-python на GitHub.
Troubleshooting
"Нет модуля с названием 'sqlalchemy.dialects.mssql.mssqlpython'"
Эта ошибка означает, что ваша установленная версия SQLAlchemy не включает диалект mssql-python. Убедитесь, что у вас есть версия 2.1.0b2 или более поздняя:
pip install "sqlalchemy>=2.1.0b2"
Сбои подключения
Если create_engine удаётся, но запросы не работают, проверьте, что параметры соединения работают напрямую с 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()
Если прямое соединение работает, а SQLAlchemy — нет, проверьте проблемы с кодированием URL в специальных символах в вашем пароле или имени сервера.