SQL Server用 Django バックエンド - mssql-django

mssql-djangoは、Microsoft FabricのSQL Server、Azure SQL Database、Azure SQL Managed Instance、および SQL データベース用の Microsoft の Django データベース バックエンドです。 接続するには、Django の DATABASES 設定で ENGINE"mssql" に設定します。 バックエンドは pyodbcMicrosoft ODBC Driver for SQL Server 上に構築され、Django 3.2 から 6.0、Python 3.8 から 3.14、および 2016 から 2025 SQL Serverをサポートします。

出発点を選択する

Azure SQLの運用ベースライン

このスニペットは、運用環境向けのAzure SQL構成の開始点として使用します。 settings.py (Django データベースの構成、ミドルウェアの登録、ログ記録)、myproject/retry.py (一時的なエラー カタログとretry_on_transientデコレーター)、myproject/middleware.py (要求レベルの再試行ミドルウェア)、myapp/views.py (トランザクション ビューの例) の 4 つのファイルが組み合わせられています。

# settings.py

import logging.config
import os

DATABASES = {
    "default": {
        "ENGINE": "mssql",
        "NAME": os.environ["SQL_DATABASE"],          # for example, appdb
        "HOST": os.environ["SQL_SERVER"],            # for example, contoso.database.windows.net
        "PORT": "1433",
        "CONN_MAX_AGE": 300,                         # reuse pooled connections for 5 minutes
        "CONN_HEALTH_CHECKS": True,                  # validate connections before reuse (Django 4.1 and later)
        "ATOMIC_REQUESTS": False,                    # wrap mutating views in transactions explicitly (see the following view example)
        "OPTIONS": {
            "driver": "ODBC Driver 18 for SQL Server",
            "extra_params": (
                "Authentication=ActiveDirectoryMsi;"
                "Encrypt=yes;"
                "TrustServerCertificate=no;"
                # ODBC driver reconnects connections dropped while idle.
                "ConnectRetryCount=3;"
                "ConnectRetryInterval=10;"
            ),
            # Backend-level retry for the initial connect call. Complements
            # ConnectRetryCount, which only covers idle drops on an
            # already-established connection.
            # See Retry logic and connection resilience for the recognized error list.
            "connection_retries": 3,
            "connection_retry_backoff_time": 5,
        },
    },
}

MIDDLEWARE = [
    # Defined in myproject/middleware.py. Catches transient OperationalErrors
    # and retries the request. Add "1205" (deadlock victim) and "1222"
    # (lock-request timeout) to TRANSIENT_ERROR_CODES to also retry
    # statement-level failures.
    "myproject.middleware.DatabaseRetryMiddleware",
    "django.middleware.security.SecurityMiddleware",
    # ... your other middleware
]

LOGGING_CONFIG = None
logging.config.dictConfig({
    "version": 1,
    "disable_existing_loggers": False,
    "formatters": {
        "json": {
            "format": (
                '{"time":"%(asctime)s","level":"%(levelname)s",'
                '"logger":"%(name)s","message":"%(message)s"}'
            ),
        },
    },
    "handlers": {
        "console": {
            "class": "logging.StreamHandler",
            "formatter": "json",
        },
    },
    "loggers": {
        "django.db.backends": {
            "handlers": ["console"],
            "level": "WARNING",      # raise to INFO or DEBUG to capture SQL
            "propagate": False,
        },
        "django.request": {
            "handlers": ["console"],
            "level": "WARNING",
            "propagate": False,
        },
        "mssql": {
            "handlers": ["console"],
            "level": "INFO",
            "propagate": False,
        },
    },
})

retry_on_transientで、共有一時的エラー カタログとmyproject/retry.py デコレーターを定義します。

# myproject/retry.py
import functools
import logging
import random
import re
import time

from django.db import OperationalError, connection

logger = logging.getLogger(__name__)

TRANSIENT_ERROR_CODES = {
    "64", "233", "4221",
    "10053", "10054", "10928", "10929",
    "40197", "40501", "40613",
    "49918", "49919", "49920",
    # Add "4060" only if targeting Azure SQL with geo-replication failover.
    # Add "1205" (deadlock victim) and "1222" (lock-request timeout) to
    # also retry statement-level failures.
}

# Microsoft ODBC driver formats native error codes as "(<number>)" in the
# message. Parenthesized matches avoid false positives for short codes like "64".
_CODE_RE = re.compile(r"\((\d+)\)")


def is_transient(error):
    codes_in_message = set(_CODE_RE.findall(str(error)))
    return bool(codes_in_message & TRANSIENT_ERROR_CODES)


def retry_on_transient(max_retries=3, base_delay=1, max_delay=30):
    """Retry on transient database errors with exponential backoff and full jitter."""

    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            for attempt in range(max_retries + 1):
                try:
                    return func(*args, **kwargs)
                except OperationalError as e:
                    if attempt < max_retries and is_transient(e):
                        capped = min(max_delay, base_delay * (2 ** attempt))
                        delay = random.uniform(0, capped)
                        logger.warning(
                            "Transient error in %s (attempt %d/%d), retrying in %.2fs: %s",
                            func.__name__, attempt + 1, max_retries, delay, e
                        )
                        connection.close()
                        time.sleep(delay)
                        continue
                    raise
        return wrapper
    return decorator

myproject/middleware.pyで要求レベルのミドルウェアを定義します。 両方のレイヤーが同じエラー コードのセットを認識できるように、 is_transient を再利用します。

# myproject/middleware.py
import logging
import random
import time

from django.db import OperationalError, connection

from myproject.retry import is_transient

logger = logging.getLogger(__name__)


class DatabaseRetryMiddleware:
    """Retry the entire request on transient database errors."""

    def __init__(self, get_response):
        self.get_response = get_response
        self.max_retries = 3
        self.base_delay = 1   # seconds; doubled each attempt
        self.max_delay = 30   # cap on a single sleep, regardless of attempt

    def __call__(self, request):
        for attempt in range(self.max_retries + 1):
            try:
                return self.get_response(request)
            except OperationalError as e:
                if attempt < self.max_retries and is_transient(e):
                    capped = min(self.max_delay, self.base_delay * (2 ** attempt))
                    delay = random.uniform(0, capped)
                    logger.warning(
                        "Transient DB error (attempt %d/%d), retrying in %.2fs: %s",
                        attempt + 1, self.max_retries, delay, e
                    )
                    connection.close()
                    time.sleep(delay)
                    continue
                raise

ATOMIC_REQUESTSFalseされているため、変更ビューは独自のトランザクションを開く必要があります。 各再試行で新しいトランザクションが実行されるように、 atomic() ブロックを @retry_on_transient でラップします。

# myapp/views.py
from django.db import transaction
from django.http import JsonResponse

from myproject.retry import retry_on_transient
from .models import Order


# Exponential backoff with full jitter: sleeps random within [0,2], [0,4], [0,8] seconds.
@retry_on_transient(max_retries=3, base_delay=2)
def submit_order(request, order_id):
    with transaction.atomic():
        order = Order.objects.select_for_update().get(id=order_id)
        order.status = "submitted"
        order.save()
    return JsonResponse({"id": order.id, "status": order.status})

Note

このベースラインは、2 つのレイヤーで再試行を登録します。 ミドルウェアは、管理者、シグナル、その他のミドルウェアなど、装飾されたビュー以外のデータベース アクセスのバックストップとして機能します。 @retry_on_transient デコレーターを使用すると、ビュー作成者は、再試行する操作を細かく制御できます。 一時的なエラーがデコレーターをエスケープした場合、ミドルウェアは要求全体を再試行するため、最悪の場合は最大 9 回試行してからクライアントにエラーが表示されます。 その上限がレイテンシ予算に対して高すぎる場合は、レイヤーを1つ減らすか、残すレイヤーの max_retries を下げてください。

この構成の各部分の詳細については、「構成リファレンス」、接続オプション接続プール再試行ロジックと接続の回復性、およびMicrosoft Entra認証を参照してください。

主要な機能

  • ドロップイン対応のDjangoバックエンド: ENGINE"mssql" に設定すると、Django の ORM、マイグレーション、管理サイト、管理コマンドが SQL Server で動作します。
  • pyodbc および ODBC Driver 18 上に構築: 既定で TLS で暗号化された接続と、Windows、Linux、および macOS での広範なプラットフォームサポート。
  • ワイド バージョン マトリックス: Django 3.2 から 6.0、Python 3.8 から 3.14、および SQL Server 2016 ~ 2025。
  • Microsoft Entra ID認証: マネージド ID、サービス プリンシパル、対話型、および統合されたフローを使用したパスワードレス接続extra_params
  • Django の移行: SQL Serverに対するスキーマの移行 (SQL Server固有の列の種類を含む)。
  • JSONField のサポート: JSONField ストレージと Django 参照によってサポートされるネイティブ
  • Always Encrypted: 機密性の高い列のクライアント側暗号化。
  • 一括操作: 適切なバッチサイズで SQL Server に対して実行する bulk_createbulk_update
  • 一時的な再試行: 接続とクエリの実行中に発生する一般的なAzure SQL一時的なエラーに対する組み込みの処理。
  • inspectdb: 既存のSQL Server スキーマから Django モデルを生成します。

概要

記事 説明
Installation mssql-djangoとMicrosoft ODBC Driver for SQL Serverをインストールします。
クイック スタート: Django をSQL Serverに接続する Django プロジェクトをSQL Serverに接続し、最初の移行を実行します。

構成と接続

記事 説明
構成リファレンス mssql-django を使用した Django DATABASES ディクショナリの完全なリファレンス。
接続オプション OPTIONSextra_params、タイムアウト、ODBC ドライバーの構成。
接続プール CONN_MAX_AGECONN_HEALTH_CHECKS、および外部プールとの連携。
再試行ロジックと接続の回復性 一時的なエラーを検出し、接続とクエリを再試行します。
Microsoft Entra 認証 マネージド ID、サービス プリンシパル、対話型、統合フローを使用したパスワードレス認証。
セキュリティのベスト プラクティス パラメーター化、シークレット管理、最小特権、暗号化。
常に暗号化 機密性の高い列のクライアント側暗号化を構成します。

モデル、移行、データ型

記事 説明
データベースの移行 SQL Server固有の列の種類など、SQL Serverに対して Django の移行を実行します。
Django フィールドから SQL Server 型へのマッピング Django モデル フィールドをSQL Serverデータ型にマップする方法。
JSONField のサポート SQL Serverおよび Django 参照でJSONFieldを使用します。
inspectdb を使用してモデルをリバース エンジニアリングする 既存のSQL Server スキーマから Django モデルを生成します。
タイム ゾーンのサポート USE_TZdatetimeoffset、およびタイムゾーン対応の日時。

データのクエリと操作

記事 説明
一括操作 bulk_createbulk_update、バッチ サイズのチューニング。
トランザクション管理 atomic、分離レベル、セーブポイント、デッドロック処理。
生の SQL クエリ RawSQLconnection.cursor()、およびSQL Server固有の構文。
ストアド プロシージャ Django から SQL Server のストアド プロシージャを呼び出す

デプロイ、テスト、チューニング

記事 説明
Azure App Service にデプロイする mssql-django を使用して Django サイトをAzure App Serviceに発送します。
コンテナーとローカル開発 Django + SQL Server 用の Docker コンテナー、devcontainers、CI パイプライン。
テスト SQL Serverに対して Django テスト スイートを実行します。
パフォーマンスチューニング インデックス、クエリ パターン、接続の再利用、バッチ サイズ。
Troubleshooting 一般的なエラー、ODBC 診断、およびログ記録。

mssql-django に移行する

記事 説明
django-mssql-backend から移行する コミュニティ django-mssql-backend パッケージから mssql-django に移行します。
他のデータベースからの移行 Django プロジェクトを別のデータベース バックエンドからSQL Serverに移動します。
PostgreSQL からの移行 PostgreSQL から SQL Server に移行する Django 開発者向けのワンストップ ガイド。
記事 説明
サポート ライフサイクル サポートされている Django、Python、および SQL Server バージョン。
新着情報 バージョン履歴とリリースのハイライト。
mssql-django の制限事項とサポートされていない機能 バックエンドの制限事項とサポートされていない機能。
よくある質問 よく寄せられる質問。