Django 4.0 릴리스와 주요 변경 사항
이 글에서는 Django 4.0에 추가된 기능과 바뀐 점을 알아보려 합니다. Django 4.0 release notes를 참고하였습니다.
Django는 3년 마다 LTS를 위해 메이저 버전을 하나씩 올리고, 8개월마다 마이너 버전을 올립니다. 현재 LTS 버전은 3.2.x입니다. 이번에 출시한 4.0은 LTS 버전은 아닙니다.
파이썬 호환성
Django 4.0은 파이썬 3.8과 3.9, 3.10을 지원합니다. 파이썬 3.6과 3.7을 지원하는 마지막 버전은 Django 3.2.x입니다.
새 기능
기본 타임존은 zoneinfo
이제부터 파이썬 기본 라이브러리인 zoneinfo가 기본 타임존입니다. Django 3.2부터 시작한 pytz를 zoneinfo로 이전하는 작업의 일환입니다. pytz 지원은 중단 예정이며 Django 5.0에서 완전히 삭제됩니다.
함수형 고유키 제약
UniqueContraint() 표현식으로 함수형 고유키를 생성할 수 있습니다. 예를 들어 온라인 상점의 영문 이름을 소문자화하여 고유키로 사용하고 싶다고 해보면 다음과 같이 선언하면 됩니다.
class Shop(models.Model):
name = models.CharField(max_length=255)
class Meta:
constraints = [
UniqueConstraint(Lower('name'),
name='unique_name')
]이렇게 선언한 후, 이름이 OnlineShop인 상점이 존재할 때 다른 상점의 이름을 onlineshop으로 등록할 수 없습니다.
scrypt 암호 해시기
PBKDF2보다 안전한 scrypt 암호 해시기를 도입했습니다. 이를 사용하려면 OpenSSL 1.1 버전 이상이 필요합니다. 또한 메모리를 더 사용하기 때문에 기본값으로 지정하진 않았습니다.
Redis 캐시 백엔드 내장
Redis 캐시를 지원하는 django.core.cache.backends.redis.RedisCache 내장 백엔드를 추가했습니다. 이를 사용하려면 redis-py 3.0.0 버전 이상이 필요합니다.
템플릿 기반의 폼 렌더링
Form과 Formset, ErrorList에 템플릿 기반의 렌더링 기능을 추가했습니다. Form과 Formset 렌더링에 필요한 render(), get_context(), template_name 등의 메서드와 속성도 추가했습니다.
예를 들어 다음과 같은 뷰가 있다고 가정하죠.
from shop.forms import CartForm
def cart(request):
CartFormSet = formset_factory(CartForm)
if request.method == 'POST':
formset = CartFormSet(request.POST)
else:
formset = CartFormSet()
return render(request, 'cart.html', {'formset': formset})그동안 cart.html에서는 이런 식으로 폼을 렌더링했습니다.
<form method="post">
{{ formset.management_form }}
<table>
{% for form in formset %}
{{ form }}
{% endfor %}
</table>
</form>이제는 뷰에서 formset.template_name 속성을 선언한 후 cart.html을 다음과 같이 줄일 수 있습니다.
<form method="post">
<table>
{{ formset }}
</table>
</form>자잘한 변경
관리자 화면(django.contrib.admin)
admin/base.html 템플릿 파일에 header 블럭을 추가했습니다. 여기에는 관리자 화면의 헤더 부분(아래 화면의 빨간 테두리 부분)이 들어갑니다.
관리자 화면의 내비게이션 바에 필터를 추가했습니다.
django.contrib.postgres
서비스 이름으로 연결하기를 지원합니다. 예를 들어 .pg_service.conf 파일을 다음과 같이 선언해두었다고 가정합니다.
[shops_service]
host=localhost
user=superadmin
dbname=shops
port=5432데이터베이스 접속 비밀번호는 .shops_pgpass에 저장해두었고요.
localhost:5432:shops:superadmin:PASSWORD이제 settings.py의 DATABASES 부분은 다음과 같이 선언할 수 있습니다.
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'OPTIONS': {
'service': 'shops_service',
'passfile': '.shops_pgpass',
},
}
}- 기존 데이터는 무시하고 새 데이터에만 제약 조건을 적용하고 싶을 때 사용할 수 있도록 AddConstraintNotValid 오퍼레이터를 추가했습니다.
- 기존 데이터가 제약 조건을 위반하는지 확인할 수 있는 ValidateConstraint를 추가했습니다.
- QuerySet.value() 메서드의 리턴 값을 배열 처럼 접근할 수 있는 ArraySubquery 표현식을 추가했습니다. 예를 들어 온라인 상점(Shop)과 주문 목록(Order)이 서로 외래키로 연결된 상태라고 가정합시다. 다음과 깉이 한 상점의 주문 목록 중 특정한 값을 배열로 가져올 수 있습니다.
from django.contrib.postgres.expressions import ArraySubquery
orders = Order.objects.filter(shop=OuterRef('pk')).values('product_name')
shop = Shop.objects.annotate(orders=ArraySubquery(orders)).first()
shop.orders # ['Route53', 'ELB', 'EC2']
orders = Order.objects.filter(shop=OuterRef('pk')).values(json=JSONObject(product='product_name', id='id'))
shop = Shop.objects.annotate(orders=ArraySubquery(orders)).first()
shop.orders # [{'id': 3, 'product': 'Route53'}, {'id': 4, 'product': 'ELB'}, {'id': 5, 'product': 'EC2'}]- 문자열/단어 유사성을 계산하는 Trigram 방식을 사용하는 trigram_word_similar 룩업과 TrigramDistance와 TrigramWordDistance 표현식을 추가했습니다.
기타
- 관리자 화면에서 사용하는 jQuery 버전을 3.5.1에서 3.6.0으로 변경하였습니다.
- 인증(
django.contrib.auth)- PBKDF2 비밀번호 해시의 반복 횟수를 260,000회에서 320,000회로 늘었습니다. (Django 3.2에서는 216,000회에서 260,000회로 늘었습니다.)
- 로그인 후 리디렉션 페이지를 바꾸려면
LoginView의 next_page 속성과 get_default_redirect_url() 메서드를 사용할 수 있습니다.
- 캐시
-
django.core.cache.backends.base.BaseCache가 비동기 API를 지원합니다. 비동기 메서드 앞에는a글자가 붙습니다. 예를 들어add()메서드의 비동기 버전은aadd(),get()메서드의 비동기 버전은aget()입니다.
-
- CSRF
- CSRF 방어시
Origin헤더가 있으면 이를 참고합니다. 그리고'https://*.44bits.io'처럼 HTTP 스킴과*문자도 지원합니다.
- CSRF 방어시
- 관리 명령어
- run_server 명령어에 –skip-checks 옵션을 추가했습니다.
- (PostgreSQL을 사용할 때) dbshell 명령이 비밀번호 파일을 인식합니다.
- shell 명령어가 sys.__interactivehook__ 값에 영향을 받습니다. 이를 통해 인터랙티브 셸 간 명령 이력을 불러옵니다.
-
startapp과startproject명령어에 템플릿을 적용할 때--exclude옵션을 사용하여 특정 디렉터리를 제외할 수 있습니다.
- 모델
- 쿼리셋에 특정 개체가 포함됐는지 확인할 수 있는 QuerySet.contains(obj) 메서드를 추가했습니다.
-
Round() 데이터베이스 함수에 정밀도를 지정할 수 있는
precision전달인자를 추가했습니다. - (SQLite 3.35 이상을 사용할 때) QuerySet.bulk_create() 메서드로 개체들의 기본키를 설정할 수 있습니다.
-
QuerySet.bulk_update()가 수정된 개체 수를 리턴합니다. (Django 3.2까지는
None을 리턴했습니다.) - 데이터베이스 함수의 결과 값이 비었을 때 리턴할 값을 Expression.empty_result_set_value로 지정할 수 있습니다.
- Lookup 표현식을 애너테이션과 애그리게이션, 필터 안에서 사용할 수 있습니다.
- aggregate() 결과가 비었을 때 리턴할 값을 default 전달인자로 선언할 수 있습니다.
- 요청과 응답
- SecurityMiddleware에 Cross-Origin Opener Policy 헤더를 추가할 수 있습니다.
- 템플릿
-
floatformat 템플릿 필터에서
u접미사를 사용하여 지역화를 끌 수 있습니다.
-
floatformat 템플릿 필터에서
- 테스트
- –buffer 옵션이 병렬 테스트도 지원합니다.
- –shuffle 옵션을 사용하여 테스트 순서를 무작위로 섞을 수 있습니다.
-
test –parallel 옵션에
auto를 지정하면 자동으로 각 프로세서마다 테스트 프로세스를 실행합니다.
하위 호환되지 않는 기능
데이터베이스 API
-
DatabaseOperations.year_lookup_bounds_for_date_field()메서드와year_lookup_bounds_for_datetime_field()메서드에iso_year매개변수를 선택적으로 전달할 수 있습니다. 이 매개변수는 ISO-8601 표준에 따른 주(week) 번호를 지원합니다. -
DatabaseSchemaEditor._unique_sql()메서드와_create_unique_sql()의 두 번째 매개변수가columns대신fields로 바뀌었습니다.
PostgreSQL 9.6 지원 중단
Oracle 12.2와 18c 지원 중단
SecurityMiddleware가 X-XSS-Protection 헤더를 설정하지 않음
마이그레이션 자동 감지 변경
마이그레이션 자동 감지기가 모델 클래스 대신 모델 스테이트를 참고합니다. 또한 ForeignKey와 ManyToManyField 필드에 대한 마이그레이션 파일 생성시 몇몇 속성을 특정하지 않습니다. 따라서 간혹 아무 일도 하지 않는 AlterField가 마이그레이션 파일 안에 선언되기도 합니다.
자잘한 변경
-
STATIC_URL의 기본 값이/static/에서static/으로 바뀌었습니다. -
AdminSite의
index뷰에@never_cache데코레이터가 기본으로 적용되지 않습니다. - 부분 쿼리셋이 지원하지 않는 연산에 대해
AssertionError대신TypeError예외를 일으킵니다. -
assertHTMLEqual() 테스트시 불린 속성이 아니면, 값 없이 이름만 선언한 경우와 값과 이름을 모두 선언한 경우를 같다고 간주하지 않습니다. (예를 들어
checked를checked="checked"와 같다고 간주하지만alt는alt="alt"와 같지 않습니다. Django 3.2까지는 같다고 간주했습니다.)
중단 예정인 기능
pytz 타임존
pytz 대신 zoneinfo를 권장합니다. 따라서 몇몇 시간 관련 메서드에서 사용하던 is_dst 전달인자도 중단 예정입니다.
pytz는 Django 5.0에서는 사용할 수 없습니다.
타임존 지원
Django 5.0 버전부터는USE_TZ 설정의 기본 값이 False에서 True로 바뀔 예정입니다. 사실 Django 1.4 버전부터startproject 명령어가 자동 생성하는 settings.py 파일에 USE_TZ = True가 들어 있었지만, 기본 값은 False였습니다.
지역화
USE_L10N 설정의 기본 값 역시 False에서 True로 바뀌었습니다. 아울러 USE_L10N 설정은 중단 예정입니다. Django 5.0에서는 기본으로 날짜나 숫자를 지역화합니다.
삭제된 기능
Django 3.0부터 중단 예정이었던 기능
-
django.utils.http.urlquote()와urlquote_plus(),urlunquote(),urlunquote_plus() -
django.utils.encoding.force_text()와smart_text() -
django.utils.translation.ugettext()와ugettext_lazy(),ugettext_noop(),ungettext(),ungettext_lazy() django.utils.http.is_safe_url()
Django 3.1부터 중단 예정이었던 기능
-
PASSWORD_RESET_TIMEOUT_DAYS설정 -
django-admin.py(이제django-admin만 작동합니다.) HttpRequest.is_ajax()- 모델의
NullBooleanField필드 django.conf.urls.url()-
django.contrib.postgres.fields.JSONField와django.contrib.postgres.forms.JSONField -
{% ifequal %}와{% ifnotequal %}템플릿 태그 -
DEFAULT_HASHING_ALGORITHM설정
요약은 여기까지입니다. 개인적으론 중요하지 않아 보여서 적지 않은 내용이 여러분에겐 더 중요할 수도 있으니, Django 4.0 공식 릴리스 노트를 한 번 살펴보시길 추천합니다.