SQL Server용 Django 백엔드 - mssql-django

mssql-django는 SQL Server, Azure SQL Database, Azure SQL Managed Instance 및 Microsoft Fabric의 SQL 데이터베이스를 위한 Microsoft의 Django용 데이터베이스 백엔드입니다. 연결하려면 Django ENGINE 구성에서 "mssql"을(를) DATABASES(으)로 설정하세요. 버전 2.0은 Django 5.2부터 6.1, Python 3.10부터 3.14, SQL Server 2017부터 2025까지 지원합니다. 기본값인 Microsoft ODBC Driver for SQL Server와 함께 pyodbc를 통해 또는 Microsoft의 mssql-python 드라이버를 통해 연결됩니다.

시작점 선택

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;"
                # Parallel dials to all resolved IPs; safe on single-IP targets.
                "MultiSubnetFailover=Yes;"
                # 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_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에서 작동합니다.
  • mssql-python 지원: 데이터베이스 별칭에 대해 Microsoft 드라이버를 mssql-python 선택하고 별도의 ODBC 드라이버 설치를 건너뛰세요. 기본 드라이버는 외부에 설치된 Microsoft ODBC 드라이버를 pyodbc 사용합니다.
  • 기본적으로 암호화된 연결: 경로상 pyodbc 의 기본값인 SQL Server용 ODBC 드라이버 18은 Windows, Linux, macOS에서 연결을 암호화하고 서버 인증서를 검증합니다.
  • 현재 버전 매트릭스: Django 5.2부터 6.1, Python 3.10부터 3.14, SQL Server 2017부터 2025까지.
  • Microsoft Entra ID 인증: extra_params를 통한 관리되는 ID, 서비스 주체, 대화형 및 통합 흐름의 암호 없는 연결.
  • Django 마이그레이션: SQL Server 전용 열 형식을 포함하여 SQL Server에 대한 스키마 마이그레이션.
  • JSONField 지원: JSONFieldnvarchar(max) 저장소와 Django 조회 기능을 기반으로 하는 기본 지원.
  • Always Encrypted: 중요한 열에 대한 클라이언트 쪽 암호화입니다.
  • 대량 작업: SQL Server를 대상으로 bulk_create 및 bulk_update를 적절한 배치 크기로 수행합니다.
  • 일시적 재시도: 연결 및 쿼리 실행 중 일반적인 Azure SQL 일시적인 오류에 대한 기본 제공 처리입니다.
  • inspectdb: 기존 SQL Server 스키마에서 Django 모델을 생성합니다.

Get started

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

구성 및 연결

조항 Description
구성 참조 mssql-django를 사용하는 Django DATABASES 사전에 대한 전체 참조입니다.
연결 옵션 OPTIONS extra_params, , 타임아웃, MARS와 ODBC 드라이버 구성이 포함됩니다.
데이터베이스 드라이버를 선택합니다 기본 pyodbc 드라이버 대신 데이터베이스 별칭을 선택하세요mssql-python.
연결 풀링 CONN_MAX_AGE, CONN_HEALTH_CHECKS, 및 외부 풀 통합.
재시도 논리 및 연결 복원력 일시적인 오류를 검색하고 연결 및 쿼리를 다시 시도합니다.
Microsoft Entra 인증 관리 ID, 서비스 주체, 대화형 및 통합 흐름을 사용한 암호 없는 인증
보안 모범 사례 매개 변수화, 비밀 관리, 최소 권한 및 암호화.
항상 암호화되어 있습니다 중요한 열에 대한 클라이언트 쪽 암호화를 구성합니다.

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

조항 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) 자주 묻는 질문