Django-6.0 미리보기 - 하위호환

[전 포스트]https://kimjj81.github.io/2025/10/01/Django-6-0-미리보기-01-신규-기능/)에 이어서 Django 6.0의 하위 호환성, 폐기 결정, 제거된 것에 대해 알아보겠습니다.

꼭 마지막의 제거된 것은 확인해야 합니다.

하위 호환

파이썬 3.12 이상

가장 큰 문제는 파이썬 3.12 이상만 지원한다는 것입니다. 이에 따라 사용하던 라이브러리 중 오래 되어 지원이 끊긴 것들은 호환이 안될 수도 있습니다.
git worktree 를 이용해서 점검 하는게 좋겠습니다.

MariaDB 10.5 지원 종료

MariaDB 10.5는 지원이 종료되었기 때문에 MariaDB 10.6 이상을 사용해야 합니다.

DB backend API 변경

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Django 6.0 이전: CASCADE로 자동 삭제
migrations.RemoveField(
model_name='book',
name='author', # 이 컬럼을 참조하는 뷰나 제약조건이 자동 삭제됨
)

# Django 6.0 이후: 의존 객체가 있으면 에러 발생
# 해결 방법 1: 의존 객체를 먼저 수동으로 제거
migrations.RunSQL("DROP VIEW IF EXISTS author_books_view"),
migrations.RemoveField(
model_name='book',
name='author',
)

# 해결 방법 2: Raw SQL로 CASCADE 명시
migrations.RunSQL(
"ALTER TABLE book DROP COLUMN author_id CASCADE"
)
  • DatabaseOperations 메서드 이름 변경 : 사용하고 있는 메서드가 있는지 전체 검색 하는게 좋습니다.

    • return_insert_columns() → returning_columns()
    • fetch_returned_insert_rows() → fetch_returned_rows()
    • INSERT뿐만 아니라 UPDATE에서도 RETURNING 절을 사용할 수 있음을 명확히 표현
  • DatabaseOperations 메서드 시그니처 변경

    • fetch_returned_insert_columns() 제거
    • fetch_returned_rows()가 cursor와 returning_params 두 개의 인자를 요구
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Django 6.0 이전
class MyDatabaseOperations(BaseDatabaseOperations):
def return_insert_columns(self, fields):
# INSERT 전용으로 명명됨
return [field.column for field in fields]

def fetch_returned_insert_rows(self, cursor):
return cursor.fetchall()

# Django 6.0 이후 - 이름 변경 필수
class MyDatabaseOperations(BaseDatabaseOperations):
def returning_columns(self, fields):
# INSERT와 UPDATE 모두 사용 가능
return [field.column for field in fields]

def fetch_returned_rows(self, cursor, returning_params):
# cursor와 returning_params 모두 필요
return cursor.fetchall()

  • UPDATE … RETURNING 지원
    • 데이터베이스가 지원하면 can_return_rows_from_update=True 설정 가능
    • 현시점에서는 아직 구현은 보이지 않습니다.

Email

1
2
3
4
5
6
7
8
from email.charset import Charset

# Django 6.0 이전
msg = EmailMessage(...)
msg.encoding = Charset('utf-8') # ❌ 더 이상 작동 안 함

# Django 6.0 이후
msg.encoding = 'utf-8' # ✅ 문자열만 가능

DEFAULT_AUTO_FIELD -> ‘BigAutoField’

  • DEFAULT_AUTO_FIELD 는 django.db.models.AutoField 유지
  • Django 3.2 부터 DEFAULT_AUTO_FIELD 설정이 없어도 models.W042 경고를 발생하지 않기 때문에 대부분 영향 없음.
  • 만약 영향을 받고 있다면 다음과 같이 추가
1
2
3
4
5
6
# settings.py
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"

# AppConfig
class MyAppConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"

커스텀 ORM 반환 타입 변경

  • as_sql() 을 통해 custom lookupscustom expressions 구현 할 때 반환값의 두번째는 튜플이어야 합니다.
  • 이전에는 리스트도 가능했지만, 이제는 튜플만 가능합니다.
1
def as_sql(self, compiler, connection) -> tuple[str, tuple]: ...
  • 하위 호환성을 위해 다음과 같이 튜플로 변경해서 반환해야 합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# params 를 언팩 후 튜플로 다시 패킹
params = (*lhs_params, *rhs_params)

# 예제
from django.db.models import Lookup

class CustomLookup(Lookup):
lookup_name = 'custom'

def as_sql(self, compiler, connection):
lhs, lhs_params = self.process_lhs(compiler, connection)
rhs, rhs_params = self.process_rhs(compiler, connection)

sql = f"{lhs} CUSTOM_OP {rhs}"

# Django 6.0: 반드시 튜플 반환
params = (*lhs_params, *rhs_params) # 안전한 방식
return sql, params # params는 항상 튜플

기타 사소한 변경사항

  • JSON 직렬화 개선 : indent 옵션 없이도 출력 끝에 개행(\n) 추가
  • 비공식인 django.utils.http.parse_header_parameters() 가 Python의 email.message.Message 를 파싱에 사용. 10000 자가 넘는 헤더는 ValueError 발생.
  • django.contrib.gis.forms.widgets 가 인라인 JavaScript 없이 렌더링. geometry 위젯이나 or 템플릿을 수정해 사용했다면, 새로운 레아이웃에 맞게 수정해야 함.
  • 어드민에서 messages.DEBUG , messages.INFO 가 구분되는 CSS 스타일로 변경 됨. 이전에는 messages.SUCCESS와 동일 했음.
    • ModelAdmin.message_user() 는 messages.INFO 이 기본이므로, 이전과 동일하게 아이콘과 스타일을 유지하려면 messages.SUCCESS 로 설정해야 함.
  • asgiref 최소 버전 3.8.1 에서 3.9.1 로 변경.
  • collectstatic 출력 간소화
    • 충돌/삭제 파일의 상세 정보는 --verbosity 2 이상에서만 표시.
    • 대규모 프로젝트에서 출력 노이즈 감소

Features deprecated in 6.0

django.core.mail API에서 자주 사용하지 않는 매개변수는 반드시 키워드 인자로 전달해야 함

  • get_connection(), mail_admins(), mail_managers(), send_mail(), and send_mass_mail() 에서 모든 옵션널한 매개변수(fail_silently 이후)는 키워드 인자로 입력해야 함
  • EmailMessage / EmailMultiAlternatives 처음 4개 매개변수(subject, body, from_email, to)를 제외한 모든 매개변수는 키워드 인자로 전달해야 함
  • API 명확성 향상. 많은 선택적 매개변수가 있을 때 위치 인자는 가독성이 떨어지고 실수하기 쉽기 때문. Model.save() 도 5.0 에서 비슷한 변경이 있었음.

사소한 폐기 사항

  • 테스트 데이터베이스 생성 메서드 변경
    • Deprecated : BaseDatabaseCreation.create_test_db(serialize=True)
    • 6.0 : BaseDatabaseCreation.serialize_db_to_string()
  • StringAgg 가 PostgreSQL 전용이 아닌 범용적인 클래스로 변경 됨.
    • Deprecated : from django.contrib.postgres.aggregates import StringAgg
    • 6.0 : from django.db.models import StringAgg
  • PostgreSQL OrderableAggMixin → Aggregate.order_by
    • Deprecated : from django.contrib.postgres.aggregates import OrderableAggMixin
    • 6.0 : from django.db.models import Aggregate # Aggregate 클래스에 order_by 속성 추가됨
  • urlize, urlizetrunc 기본 프로토콜 http:// → https:// 로 7.0 에서 변경 예정.
    • URLIZE_ASSUME_HTTPS 셋팅을 True 로 설정하여 6.0 에서도 https:// 로 변경하여 7.0을 대비.
    • 단, URLIZE_ASSUME_HTTPS 도 7.0 에서 제거 예정.
  • ADMINS/MANAGERS 설정 형식 변경
    • 튜플 -> 이메일 형식으로. Django 는 이메일의 이름 부분을 사용하지 않음
    • 또는 formataddr 사용
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Deprecated
ADMINS = [
('John Doe', 'john@example.com'), # 튜플 형식
('Jane Smith', 'jane@example.com'),
]

# 권장 방식
ADMINS = [
'john@example.com', # 이름 없이 이메일만
'"Jane Smith" <jane@example.com>', # 이름 포함 시 이 형식
]

# 또는
from email.utils import formataddr
ADMINS = [
formataddr(('John Doe', 'john@example.com')),
]
  • Paginator의 orphans 제한
    • orphans는 마지막 페이지의 최소 항목 수
    • per_page보다 크거나 같으면 논리적으로 모순
  • 컬럼 별칭에 % 기호 사용 금지
  • 이메일 첨부 - MIMEBase → MIMEPart
  • 이메일 관련 Deprecated 항목들. Python 표준 라이브러리 사용 변경.
    • 다음 항목 폐기 결정
      • from django.core.mail import BadHeaderError
      • from django.core.mail import SafeMIMEText, SafeMIMEMultipart
      • from django.core.mail import forbid_multi_line_headers
      • from django.core.mail.message import sanitize_address
    • BadHeaderError 대신 → ValueError
    • SafeMIMEText/SafeMIMEMultipart → Python의 EmailMessage

6.0에서 제거된 항목들

  • BaseConstraint 의 위치 인자 대신 키워드 인자만 사용.
  • 과도기적 폼 렌더러 DjangoDivFormRenderer 및 Jinja2DivFormRenderer 제거.
    • Django 4.x에서 5.0으로 업그레이드할 때 사용했던 임시 호환 레이어
1
2
3
4
5
6
7
# settings.py
# Django 5.0 (과도기)
FORM_RENDERER = 'django.forms.renderers.DjangoDivFormRenderer' # deprecated
# Django 6.0 (현재)
FORM_RENDERER = 'django.forms.renderers.DjangoTemplates' # 기본값
# 또는
FORM_RENDERER = 'django.forms.renderers.Jinja2'
  • BaseDatabaseOperations.field_cast_sql() 제거
  • ModelAdmin.lookup_allowed() 메서드를 오버라이드할 때 반드시 request 매개변수를 포함해야 함
  • format_html() 에 args 또는 kwargs 필수
  • forms.URLField 기본 “http” -> “https”
  • 과도기적 FORMS_URLFIELD_ASSUME_HTTPS 세팅 제거
  • Django의 내부 ORM 구조인 Join 클래스가 더 이상 get_joining_columns() 메서드로 폴백(fallback)하지 않음. (django.db.models.sql.datastructures.Join)
    • 자동 폴백이 없으므로 필요한 정보가 명시적으로 제공되어야 함
    • 커스텀 모델 필드에서 직접 조인 로직을 구현한 경우
    • Django ORM 의 내부 SQL 생성 로직을 확장하거나 수정한 경우
    • 써드파티 라이브러리가 Django 내부 Join 클래스에 의존하는 경우
    • 내부 구현의 정리 작업으로 일반적으로 영향 없고, 아래 두가지가 영향을 받은 것
  • ForeignObject, ForeignObjectRel 의 get_joining_columns() 제거 됨
  • ForeignObject.get_reverse_joining_columns() 제거 됨
  • cx_Oracle 지원 제거
  • ChoicesMeta 의 django.db.models.enums.ChoicesType alias 제거.
  • Prefetch.get_current_queryset() 제거
  • get_prefetch_queryset() 의 연관된 manager 와 descriptor 제거
  • get_prefetcher() , prefetch_related_objects() 가 get_prefetch_queryset() 로 Fall Back 하지 않음.
  • django.urls.register_converter() 존재하는 converter를 오버라이드 할 수 없음
  • ModelAdmin.log_deletion() , LogEntryManager.log_action() 제거
  • undocumented django.utils.itercompat.is_iterable() 함수 , django.utils.itercompat 모듈 제거
  • django.contrib.gis.geoip2.GeoIP2.coords() 제거
  • django.contrib.gis.geoip2.GeoIP2.open() 제거
  • Model.save() 과 Model.asave() 위치 인자 사용 금지
  • django.contrib.gis.gdal.OGRGeometry.coord_dim 의 setter 제거
  • CheckConstraint 키워드 인자 제거
  • FieldCacheMixin 의 get_cache_name() 메소드 제거
  • FileSystemStorage 의 OS_OPEN_FLAGS 속성 제거

Django-6.0 미리보기 - 하위호환
https://kimjj81.github.io/2025/10/01/Django-6-0-미리보기-02-하위호환/
Author
김 정진
Posted on
October 1, 2025
Licensed under