[전 포스트]https://kimjj81.github.io/2025/10/01/Django-6-0-%EB%AF%B8%EB%A6%AC%EB%B3%B4%EA%B8%B0-01-%EC%8B%A0%EA%B7%9C-%EA%B8%B0%EB%8A%A5/)에 이어서 Django 6.0의 하위 호환성, 폐기 결정, 제거된 것에 대해 알아보겠습니다.
꼭 마지막의 제거된 것은 확인해야 합니다.
하위 호환
파이썬 3.12 이상
가장 큰 문제는 파이썬 3.12 이상만 지원한다는 것입니다. 이에 따라 사용하던 라이브러리 중 오래 되어 지원이 끊긴 것들은 호환이 안될 수도 있습니다. git worktree 를 이용해서 점검 하는게 좋겠습니다.
MariaDB 10.5 지원 종료
MariaDB 10.5는 지원이 종료되었기 때문에 MariaDB 10.6 이상을 사용해야 합니다.
DB backend API 변경
- BaseDatabaseSchemaEditor 와 PostgreSQL 은 더이상 컬럼 삭제 시 CASCADE 옵션을 지원하지 않습니다.
# 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 두 개의 인자를 요구
# 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 설정 가능
- 현시점에서는 아직 구현은 보이지 않습니다.
- EmailMessage , EmailMultiAlternatives 의 mixed_subtype, alternative_subtype 속성 제거(공식 속성이 아니었음)
- EmailMessage 의 비공식 속성인 encoding 에서 email.charset.Charset 을 더이상 사용하지 않음.
from email.charset import Charset
# Django 6.0 이전
msg = EmailMessage(...)
msg.encoding = Charset('utf-8') # ❌ 더 이상 작동 안 함
# Django 6.0 이후
msg.encoding = 'utf-8' # ✅ 문자열만 가능
- EmailMessage attachments 허용 인스턴스
- EmailAttachment
- MIMEPart
- (filename, content, mimetype) 튜플
- MIMEBase 는 더 이상 지원하지 않음.
DEFAULT_AUTO_FIELD -> 'BigAutoField'
- DEFAULT_AUTO_FIELD 는 django.db.models.AutoField 유지
- Django 3.2 부터 DEFAULT_AUTO_FIELD 설정이 없어도 models.W042 경고를 발생하지 않기 때문에 대부분 영향 없음.
- 만약 영향을 받고 있다면 다음과 같이 추가
# settings.py
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
# AppConfig
class MyAppConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
커스텀 ORM 반환 타입 변경
- as_sql() 을 통해 custom lookups 나 custom expressions 구현 할 때 반환값의 두번째는 튜플이어야 합니다.
- 이전에는 리스트도 가능했지만, 이제는 튜플만 가능합니다.
def as_sql(self, compiler, connection) -> tuple[str, tuple]: ...
- 하위 호환성을 위해 다음과 같이 튜플로 변경해서 반환해야 합니다.
# 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 사용
# Deprecated
ADMINS = [
('John Doe', '[email protected]'), # 튜플 형식
('Jane Smith', '[email protected]'),
]
# 권장 방식
ADMINS = [
'[email protected]', # 이름 없이 이메일만
'"Jane Smith" <[email protected]>', # 이름 포함 시 이 형식
]
# 또는
from email.utils import formataddr
ADMINS = [
formataddr(('John Doe', '[email protected]')),
]
- 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으로 업그레이드할 때 사용했던 임시 호환 레이어
# 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 하지 않음.
- Features deprecated in 5.1 를 참고
- 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 속성 제거
답글 남기기