SQL Server용 Django 백엔드 - mssql-django

mssql-django는 SQL Server, Azure SQL Database, Azure SQL Managed Instance 및 Microsoft Fabric의 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 구성의 시작점으로 사용합니다. 4개의 파일 settings.py (Django 데이터베이스 구성, 미들웨어 등록 및 로깅), myproject/retry.py (일시적인 오류 카탈로그 및 retry_on_transient 데코레이터), myproject/middleware.py (요청 수준 재시도 미들웨어) 및 myapp/views.py (예: 트랜잭션 뷰)를 결합합니다.

# 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,
        },
    },
})

myproject/retry.py에서 공유 임시 오류 카탈로그와 retry_on_transient 데코레이터를 정의합니다:

# 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_REQUESTS이(가) False이므로, 변경하는 뷰는 자체 트랜잭션을 열어야 합니다. 각 재시도에서 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})

메모

이 기준값은 두 계층에서 재시도를 수행합니다. 미들웨어는 관리자, 신호 또는 기타 미들웨어와 같이 데코레이팅된 뷰 외부에서 데이터베이스 액세스를 위한 백스톱 역할을 합니다. 데코레이터는 @retry_on_transient 뷰 작성자가 재시도하는 작업을 더 세부적으로 제어할 수 있게 해줍니다. 일시적 오류가 데코레이터에서 처리되지 않고 넘어가면 미들웨어가 전체 요청을 다시 시도하므로, 최악의 경우 클라이언트에 오류가 표시되기 전까지 최대 아홉 번 시도할 수 있습니다. 그 상한이 지연 시간 예산에 비해 너무 높다면, 레이어를 하나 줄이거나 유지할 레이어에서 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 인증: extra_params를 통한 관리되는 ID, 서비스 주체, 대화형 및 통합 흐름의 암호 없는 연결.
  • Django 마이그레이션: SQL Server 전용 열 형식을 포함하여 SQL Server에 대한 스키마 마이그레이션.
  • JSONField 지원: JSONFieldnvarchar(max) 저장소와 Django 조회 기능을 기반으로 하는 기본 지원.
  • Always Encrypted: 중요한 열에 대한 클라이언트 쪽 암호화입니다.
  • 대량 작업: SQL Server를 대상으로 bulk_createbulk_update를 적절한 배치 크기로 수행합니다.
  • 일시적 재시도: 연결 및 쿼리 실행 중 일반적인 Azure SQL 일시적인 오류에 대한 기본 제공 처리입니다.
  • inspectdb: 기존 SQL Server 스키마에서 Django 모델을 생성합니다.

Get started

조항 Description
Installation mssql-django 및 SQL Server용 Microsoft ODBC Driver를 설치합니다.
빠른 시작: Django를 SQL Server 연결 Django 프로젝트를 연결하여 SQL Server 첫 번째 마이그레이션을 실행합니다.

구성 및 연결

조항 Description
구성 참조 mssql-django를 사용하는 Django DATABASES 사전에 대한 전체 참조입니다.
연결 옵션 OPTIONS, extra_params, 시간 제한 및 ODBC 드라이버 구성.
연결 풀링 CONN_MAX_AGE, CONN_HEALTH_CHECKS, 및 외부 풀 통합.
재시도 논리 및 연결 복원력 일시적인 오류를 검색하고 연결 및 쿼리를 다시 시도합니다.
Microsoft Entra 인증 관리 ID, 서비스 주체, 대화형 및 통합 흐름을 사용한 암호 없는 인증
보안 모범 사례 매개 변수화, 비밀 관리, 최소 권한 및 암호화.
Always Encrypted 중요한 열에 대한 클라이언트 쪽 암호화를 구성합니다.

모델, 마이그레이션 및 데이터 형식

조항 Description
데이터베이스 마이그레이션 SQL Server 특정 열 형식을 포함하여 SQL Server 대해 Django 마이그레이션을 실행합니다.
Django 필드의 SQL Server 형식 매핑 Django 모델 필드가 SQL Server 데이터 형식에 매핑되는 방식
JSONField 지원 JSONField를 SQL Server 및 Django 조회 기능과 함께 사용합니다.
inspectdb를 사용하여 모델 리버스 엔지니어링 기존 SQL Server 스키마에서 Django 모델을 생성합니다.
표준 시간대 지원 USE_TZ, datetimeoffset 및 시간대 정보가 포함된 날짜/시간.

데이터 쿼리 및 작업

조항 Description
대량 작업 bulk_create, bulk_update, 그리고 배치 크기 조정.
트랜잭션 관리 atomic, 격리 수준, 저장점 및 교착 상태 처리
원시 SQL 쿼리 RawSQL, connection.cursor()및 SQL Server 관련 구문입니다.
저장 프로시저 Django에서 SQL Server 저장 프로시저를 호출합니다.

배포, 테스트 및 튜닝

조항 Description
Azure App Service에 배포 mssql-django를 사용하여 Django 사이트를 Azure App Service에 배포합니다.
컨테이너 및 로컬 개발 Django + SQL Server용 Docker 컨테이너, devcontainers 및 CI 파이프라인
Testing SQL Server 대해 Django 테스트 제품군을 실행합니다.
성능 튜닝 인덱스, 쿼리 패턴, 연결 재사용 및 일괄 처리 크기입니다.
Troubleshooting 일반적인 오류, ODBC 진단 및 로깅.

mssql-django로 마이그레이션

조항 Description
django-mssql-backend에서 마이그레이션 커뮤니티 django-mssql-backend 패키지에서 mssql-django로 이동합니다.
다른 데이터베이스에서 마이그레이션 Django 프로젝트를 다른 데이터베이스 백 엔드에서 SQL Server 이동합니다.
PostgreSQL에서 마이그레이션 PostgreSQL에서 SQL Server 이동하는 Django 개발자를 위한 원스톱 가이드입니다.
조항 Description
지원 수명 주기 지원되는 Django, Python 및 SQL Server 버전입니다.
새로운 기능 버전 기록 및 릴리스 하이라이트.
mssql-django의 제한 사항 및 지원되지 않는 기능 백 엔드 제한 사항 및 지원되지 않는 기능.
자주 묻는 질문(FAQ) 자주 묻는 질문