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):
= models.CharField(max_length=255)
name class Meta:
= [
constraints 'name'),
UniqueConstraint(Lower(='unique_name')
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):
= formset_factory(CartForm)
CartFormSet if request.method == 'POST':
= CartFormSet(request.POST)
formset else:
= CartFormSet()
formset 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]=localhost
host=superadmin
user=shops
dbname=5432 port
데이터베이스 접속 비밀번호는 .shops_pgpass
에 저장해두었고요.
5432:shops:superadmin:PASSWORD localhost:
이제 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
= Order.objects.filter(shop=OuterRef('pk')).values('product_name')
orders = Shop.objects.annotate(orders=ArraySubquery(orders)).first()
shop # ['Route53', 'ELB', 'EC2']
shop.orders
= Order.objects.filter(shop=OuterRef('pk')).values(json=JSONObject(product='product_name', id='id'))
orders = Shop.objects.annotate(orders=ArraySubquery(orders)).first()
shop # [{'id': 3, 'product': 'Route53'}, {'id': 4, 'product': 'ELB'}, {'id': 5, 'product': 'EC2'}] shop.orders
- 문자열/단어 유사성을 계산하는 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 공식 릴리스 노트를 한 번 살펴보시길 추천합니다.