Django 3.2 릴리스와 주요 변경 사항
이 글에서는 Django 3.2에 추가된 기능과 바뀐 점을 알아보려 합니다. Django 3.2 release notes를 참고하였습니다.
Django는 3년 마다 LTS를 위해 메이저 버전을 하나씩 올리고, 8개월마다 마이너 버전을 올립니다. 이번에 출시한 3.2는 3년간 지원되는 LTS 업데이트입니다.
파이썬 호환성
Django 3.2는 파이썬 3.6, 3.7, 3.8, 3.9를 지원합니다.
44BITS 소식과 클라우드 뉴스를 전해드립니다. 지금 5,000명 이상의 구독자와 함께 하고 있습니다 📮
새 기능
함수형 인덱스(functional index)
함수형 인덱스를 생성할 수 있는 표현식을 지원합니다. 다음과 같이 인덱스를 생성했다고 가정합시다.
class Person(models.Model):
...class Meta:
= [
indexes 'name').desc(), 'birthday', name='name_and_birthday_idx')
Index(Lower( ]
앞의 코드는 이름의 역순과 생일순 조합으로 정렬된 인덱스를 생성합니다.
pymemcache 지원
캐시 백엔드에 pymemcache를 지원합니다.
관리자 도구에서 사용할 수 있는 display
데코레이터
사용자 정의 필드에 속성을 부여하다보면 조금 지저분해보이던 코드를 display 데코레이터로 해결할 수 있습니다.
def was_born(self, obj):
return obj.birthday is not None
= True
was_born.boolean = '-birthday'
was_born.admin_order_field = 'Was Born?' was_born.short_description
앞의 코드를 다음처럼 수정하면 됩니다.
@admin.display(
=True,
boolean='-birthday',
ordering='Was Born?',
description
)def was_born(self, obj):
return obj.birthday is not None
자잘한 변경
- 관리자 화면(
django.contrib.admin
)- ModelAdmin.search_fields에 대해, 따옴표로 감싼 공백 섞인 문자열을 검색할 수 있습니다.
- 읽기 전용 연관 필드가 Admin에 등록된 모델이라면, 링크 형태로 렌더링됩니다.
- 테마 기능을 지원합니다. (브라우저 설정이 다크 테마라면 이를 따르는 등)
- 자동 완성 필드(ModelAdmin.autocomplete_fields)도 ForeignKey.to_field와 ForeignKey.limit_choices_to의 영향을 받습니다.
- model enumeration privacy 침해 사고를 방지하는 차원에서 허가되지 않은 사용자는 URL이 존재하든 하지 않든, 로그인 페이지로 리디렉션합니다. 이 기능을 끄려면 AdminSite.final_catch_all_view 값을
False
로 바꾸면 되지만, 권장하지는 않습니다.
- 인증(
django.contrib.auth
)- PBKDF2 비밀번호 해시의 반복 횟수가 216,000회에서 260,000회로 늘었습니다.
- Argon2, MD5, PBKDF2, SHA-1 비밀번호 해시의 솔트 엔트로피가 71비트에서 128비트로 증가했습니다.
- 사이트맵(
django.contrib.sitemaps
)- 사이트맵을 지역화할 때 사용할 수 있는 alternates, languages, x_default 속성을 추가했습니다.
- 피드 프레임워크(
django.contrib.syndication
)- 피드 항목마다 댓글용 URL을 특정할 수 있는
item_comments
훅을 추가했습니다.
- 피드 항목마다 댓글용 URL을 특정할 수 있는
- 데코레이터
-
no_append_slash() 데코레이터를 사용하면 특정한 뷰만 마지막 슬래시(
/
)를 붙이지 않을 수 있습니다.
-
no_append_slash() 데코레이터를 사용하면 특정한 뷰만 마지막 슬래시(
- 파일 업로드
- FileUploadHandler.upload_interrupted() 콜백을 사용하면 업로드가 중단된 경우를 제어할 수 있습니다.
- 제네릭 뷰
-
WeekMixin과 WeekArchiveView의
week_format
속성에 ISO 8601 포맷인%V
를 사용할 수 있습니다.
-
WeekMixin과 WeekArchiveView의
- 관리 명령어
-
loaddata 명령어가 XZ 압축파일(
.xz
)과 LZMA 압축 파일(.lzma
)을 지원합니다. -
dumpdata 명령어가
bz2
,gz
,lzma
,xz
포맷을 지원합니다. - makemigrations 명령어를 데이터베이스 연결 없이도 실행할 수 있습니다. 이 경우 데이터베이스 일관성(consistent migration history) 검사는 실행되지 않습니다.
- BaseCommand.requires_system_checks 속성이 목록/튜플형으로 바뀌었습니다.
-
loaddata 명령어가 XZ 압축파일(
- 마이그레이션
- 마이그레이션에서도 pathlib이나 os.PathLike 인스턴스를 지원합니다.
- 모델
-
QuerySet.select_for_update() 에 추가된
no_key
파라미터를 사용하면, PostgresSQL의 약한 잠금(weaker lock)을 적용할 수 있습니다. (특정 행이 잠기더라도, 이 행들을 외래키로 참조하는 데이터를 생성할 수는 있습니다.) -
When() 표현식에
condition
전달인자를 사용할 수 있습니다. - Index.include와 UniqueConstraint.include 속성을 사용하여 PostgresSQL의 커버링 인덱스(covering index)를 생성할 수 있습니다.
- MySQL과 MariaDB에 대해 QuerySet.update() 메서드가
order_by()
의 영향을 받습니다. - FilteredRelation() 이 중첩된 연관 모델(nested relation)을 지원합니다.
-
Value() 표현식이
output_field
를 자동으로 판단할 수 있습니다. -
QuerySet.alias() 메서드를 사용하여 재사용할 수 있는 축약 표현식을 만들 수 있습니다. 이 표현식은
annotate
,exclude
,filter
,order_by
,update
메서드에서 사용할 수 있습니다.aggregate
에 사용하려면annotate
을 거쳐야 합니다.
-
QuerySet.select_for_update() 에 추가된
# alias를 filter에 사용할 수 있습니다.
= Person.objects.alias(kids=Count('kids')).filter(kids__gt=2)
people
# aggregate에서 사용하려면 annotate을 거쳐야 합니다.
=Count('kids')).annotate(kids=F('kids')).aggregate(Sum('kids')) Person.objects.alias(kids
- 모델 2
- Collate 데이터베이스 함수가 추가되어, 데이터베이스 고유의 정렬과 필터링 기능을 사용할 수 있습니다.
-
TruncDate와 TruncTime 데이터베이스 함수에
tzinfo
파라미터를 넘기면 특정 타임존 기준으로 datetime 데이터를 가공할 수 있습니다. -
CharField와 TextField에
db_collation
을 설정하면, 필드별로 데이터베이스 정렬(collation) 방식을 특정할 수 있습니다. - Random 데이터베이스 함수가 추가되었습니다.
- Avg, Count 등의 Aggregate 함수와 F() , OuterRef() 등의 표현식에서 값 변형(transform)을 사용할 수 있습니다.
# 태어난 해에 죽은 사람을 필터링합니다
filter(birtyday__year=F('deathday__year'))
Person.objects.
# 가장 최근에 죽은 사람의 년도를 찾습니다
=Max('deathday__year')) Person.objects.aggregate(last_dead_year
- 모델 3 - JSONObject 데이터베이스 함수가 추가되었습니다.
Person.objects.create(name='Raccoony', num_of_articles=2)
person = Person.objects.annotate(json_object=JSONObject(
name=Lower('name'),
num_of_articles=F('num_of_articles') * 10
)).get()
person.json_object # {'name': 'raccoony', 'num_of_articles': 20}
- 페이지네이션
-
django.core.paginator.Paginator.get_elided_page_range()를 사용하여 페이지 목록을 축약할 수 있습니다. (축약된 페이지들은
...
로 표시됩니다.) 예를 들어 마지막 페이지가 100인 목록에 대해get_elided_page_range(50, on_each_side=2, on_ends=2)
라고 선언하면,[1, 2, '...', 48, 49, 50, 51, 52, '...', 99, 100]
이 리턴됩니다. (on_ends
가 2이므로 1, 2, 99, 100이 포함되었고,on_each_side
가 2이므로 48, 49, 51, 52가 포함됩니다.)
-
django.core.paginator.Paginator.get_elided_page_range()를 사용하여 페이지 목록을 축약할 수 있습니다. (축약된 페이지들은
- 요청과 응답
- 응답 헤더가 HttpResponse.headers에 저장됩니다. 이 값 대신
HttpResponse
객체에 직접 접근할 수도 있습니다. - 다음의 예처럼, 응답 객체를 생성할 때
headers
를 선언할 수 있습니다.
- 응답 헤더가 HttpResponse.headers에 저장됩니다. 이 값 대신
response = HttpResponse(headers={'Age': 120})
# 다음 두 행은 결과가 같습니다.
response.headers['Age'] = 120
response['Age'] = 120
- 시리얼라이제이션
- 행마다 하나의 JSON 객체를 저장하는 JSONL 포맷의 시리얼라이저를
dumpdata
나loaddata
에서 사용할 수 있습니다. 대량 데이터를 생성할 때 행별로 메모리에 적재할 수 있기 때문에 유용합니다.
- 행마다 하나의 JSON 객체를 저장하는 JSONL 포맷의 시리얼라이저를
- 템플릿
-
floatformat 템플릿 필터에서
g
접미어를 사용하여 천단위 구분자를 강제할 수 있습니다.
-
floatformat 템플릿 필터에서
float_value = 12345.67000
# {{ value|floatformat:"2g" }} -> 12,345.67
# {{ value|floatformat:"5g" }} -> 12,345.67000
# {{ value|floatformat:"g" }} -> 12,345.7
# {{ value|floatformat:"-3g" }} -> 12,345.67
# {{ value|floatformat:"-5g" }} -> 12,345.67
- 테스트
-
TestCase.setUpTestData() 클래스 메서드에서 할당한 클래스 프로퍼티도 테스트 메서드마다 독립적으로 격리됩니다. 단 할당에 사용한 객체가 copy.deepcopy()를 지원해야 합니다.
deepcopy()
를 지원하지 않는 객체는 Django 4.1부터 TestCase.setUpTestData() 클래스 메서드에서 할당할 수 없습니다. - test –timing 옵션을 사용하여 테스트 실행 시간을 측정할 수 있습니다.
- TransactionTestCase.assertQuerysetEqual()을 사용하여 쿼리셋을 비교할 수 있습니다.
-
TestCase.setUpTestData() 클래스 메서드에서 할당한 클래스 프로퍼티도 테스트 메서드마다 독립적으로 격리됩니다. 단 할당에 사용한 객체가 copy.deepcopy()를 지원해야 합니다.
중단 예정인 기능(화살표 뒤의 기능을 사용하길 권합니다)
-
python-memcached
에 문제가 발생했고 관리도 되지 않고 있기 때문에 이를 사용하는django.core.cache.backends.memcached.MemcachedCache
가 중단 예정입니다.django.core.cache.backends.memcached.PyMemcacheCache
나django.core.cache.backends.memcached.PyLibMCCache
를 사용하세요.
마치며
요약은 여기까지입니다. 개인적으론 중요하지 않아 보여서 적지 않은 내용이 여러분에겐 더 중요할 수도 있으니, Django 3.2 공식 릴리스 노트를 한 번 살펴보시길 추천합니다.