Django 3.0 릴리스와 주요 변경 사항
MariaDB 지원, ASGI 지원, 필터 표현식 등
이 글에서는 Django 3.0에 추가된 기능과 바뀐 점을 알아보려 합니다. Django 3.0 release notes를 참고하였습니다.
Django 2.2 버전 이후에 갑자기 3.x대로 바뀌어서 당황하신 분들이 계실 텐데, 다음의 Django 릴리스 타임라인 표를 보면 일종의 규칙이 눈에 들어옵니다.
Django에서는 앞으로 3년 마다 LTS를 위해 메이저 버전을 하나씩 올리고, 8개월마다 마이너 버전을 하나씩 올리려는 듯 합니다. 앞으로 이 규칙을 따른다면 마이너 버전 x.2가 나온 후에는 항상 새 매이저 버전이 출시되겠네요.
파이썬 호환성
Django 3.0은 파이썬 3.6, 3.7, 3.8을 지원합니다. 파이썬 3.5를 지원하는 버전은 Django 2.2.x입니다.
새 기능
MariaDB 지원
MariaDB 10.1과 그 이상 버전을 지원합니다. MariaDB에 연결하려면 MySQL 백엔드를 사용하면 됩니다.
ASGI 지원
비동기 웹서버/애플리케이션을 제작할 수 있는 ASGI를 지원합니다. startproject
명령어 실행시 <프로젝트_이름>/asgi.py
파일이 생성됩니다. 기본 개발 서버인 runserver
는 이 파일을 사용하지 않지만 Uvicorn이나 Daphne 같은 별도 개발 서버에서는 사용할 수 있습니다. DJANGO_SETTINGS_MODULE
역시 별도로 지정하지 않으면 <프로젝트_이름>/asgi.py
파일로 설정됩니다.
PostgreSQL에서 제외exclusion 제약 조건 지원
ExclusionConstraint 클래스를 사용하면 PostgreSQL에서 제외 제약을 추가할 수 있습니다. 예를 들어 호텔 예약 시스템에서는 이미 예약된 방은 다른 사람이 예약할 수 없어야 하는데요. 이를 구현하기 위해 다음과 같이 제외 제약 조건을 추가할 수 있습니다.
class Reservation(models.Model):
room = models.ForeignKey('Room', on_delete=models.CASCADE)
timespan = DateTimeRangeField()
cancelled = models.BooleanField(default=False)
class Meta:
constraints = [
ExclusionConstraint(
name='exclude_overlapping_reservations',
expressions=[
('timespan', RangeOperators.OVERLAPS),
('room', RangeOperators.EQUAL),
],
condition=Q(cancelled=False),
),
]
생성하려는 예약 내역과 room
이 같고 timespan
이 겹치는 기존 예약이 존재하는데 취소 상태가 아니라면(condition=Q(cancelled=False)
), 예약을 생성하지 못한다는 예외(IntegrityError
)를 발생합니다.
필터 표현식
조건 표현식conditional expression의 출력이 BooleanField
형태라면 쿼리셋의 filter
메서드에서 바로 사용할 수 있습니다.
이런 식이 되겠죠.
예를 들어, 예약이 한 번이라도 있었던 방들의 쿼리셋을 구하려면, Django 3.0 이전 버전에서는 다음과 같이 애너테이션을 한 번 거쳐야 했습니다.
reserved_rooms = Room.objects.annotate(
has_reserved=Exists(
Reservation.objects.filter(
room=OuterRef('pk')
)
)
).filter(
has_reserved=True
)
이제는 다음과 같이 작성할 수 있습니다.
choices에 사용하는 열거형 추가
모델의 choices에 TextChoices
, IntegerChoices
, Choices
클래스를 사용할 수 있습니다. 예전에는 Enum 클래스를 상속받아 다음과 같이 구현하고, 모델에서 사용하였습니다.
class RoomType(Enum):
SINGLE = 'single'
DOUBLE = 'double'
@classmethod
def choices(cls):
return tuple((i.value, i.name) for i in cls)
class Room(models.Model):
room_type = models.CharField(...,
choices=RoomType.choices(),
default=RoomType.SINGLE.value)
이제 다음처럼 TextChoices를 상속받아 사용하면 됩니다.
class RoomType(models.TextChoices):
SINGLE = 'single'
DOUBLE = 'double'
class Room(models.Model):
room_type = models.CharField(...,
choices=RoomType.choices,
default=RoomType.SINGLE)
이 열거형들은 동적으로 생성할 수도 있습니다.
>>> RoomType = models.TextChoices('RoomType', 'SINGLE DOUBLE')
>>> RoomType.choices
[('SINGLE', 'Single'), ('DOUBLE', 'Double')]
이 외에 다음과 같은 특징이 있습니다. - .choices
, .labels
, .values
, .names
같은 프로퍼티가 내장되어 있습니다. - IntegerChoices는 Int 형을 열거할 수 있고, Choices는 str이나 int를 넘어 복잡한 데이터를 열거할 수 있습니다.
자잘한 변경
- 관리자 화면(
django.contrib.admin
)-
admin_order_field
프로퍼티를 지정하면, 관리자 화면에 나타나는 특정 컬럼의 데이터 정렬 방식을 변경할 수 있습니다. -
ModelAdmin.get_inlines() 메서드를 사용하여
inlines
항목을 동적으로 생성할 수 있습니다.
-
- 인증(
django.contrib.auth
)-
reset_url_token
매개변수를 지정하면 비밀번호 재설정(PasswordResetConfirmView) URL에 나타낼 토큰 파라미터를 변경할 수 있습니다. 기본 값은set-password
입니다. - 인증 백엔드를 쉽게 변경하도록 BaseBackend를 추가했습니다.
-
createsuperuser 명령어로 관리자 계정을 추가할 때 대화식 콘솔이 아닌 환경변수를 사용할 수 있습니다. 계정 이름은
DJANGO_SUPERUSER_대문자계정이름
환경변수를, 비밀번호는DJANGO_SUPERUSER_PASSWORD
환경변수를, 이메일은DJANGO_SUPERUSER_EMAIL
환경변수를 사용합니다. -
MantToManyField
도 REQUIRED_FIELDS로 지정할 수 있습니다.
-
- PostgreSQL
-
RangeField
와 함께 사용할 수 있는 RangeOperators를 추가하였습니다. -
RangeField
와 함께 사용할 수 있는 RangeBoundary() 표현식을 추가하였습니다.
-
- 세션(
django.contrib.sessions
)-
get_session_cookie_age()
메서드를 사용하여 세션 쿠키의 만료 시간을 동적으로 지정할 수 있습니다.
-
- 모델(
django.db.models
)- 해시용 데이터베이스 함수를 추가하였습니다. MD5 , SHA1 , SHA224 , SHA256 , SHA384 , SHA512 .
- FilePathField에 동적으로 생성한 경로를 지정할 수 있습니다.
- FileField.upload_to 속성에 pathlib 객체를 지정할 수 있습니다.
- 요청과 응답
- 템플릿이나 HttpRequest.headers에서
user-agent
같은 하이픈으로 연결된 문자열 외에user_agent
같이 밑줄로 연결된 문자열도 지원합니다.
- 템플릿이나 HttpRequest.headers에서
- 보안
-
X_FRAME_OPTIONS의 기본 값이
DENY
로 바뀝니다. 이전 버전까지는SAMEORIGIN
이었습니다. -
SECURE_CONTENT_TYPE_NOSNIFF의 기본 값이
True
로 바뀝니다. (몇몇 브라우저가 응답의 콘텐트 타입을 스스로 추측하는 기능을 막습니다.)
-
X_FRAME_OPTIONS의 기본 값이
- 테스트
-
Client에
raise_request_exception
매개변수를 지정하여 예외가 발생했을 때 테스트 클라이언트에서도 예외를 발생시킬지 여부를 결정할 수 있습니다. (기본 값은True
이고, 이는 이전 버전의 작동 방식과 같습니다.) - 테스트 실행시
--headless
옵션을 통해 헤드리스 모드로 작동하는 셀레니엄selenium 같은 브라우저 테스트 등을 지원합니다. - 테스트 실행시
--pdb
옵션을 추가하면, 코드 오류가 발생하거나 테스트가 실패한 지점에서 파이썬 디버거가 실행됩니다.
-
Client에
- Because accessing the language in the session rather than in the cookie is deprecated, LocaleMiddleware no longer looks for the user’s language in the session and django.contrib.auth.logout() no longer preserves the session’s language after logout.
하위 호환이 중단된 기능
- PostgreSQL 9.4를 지원하지 않습니다.
- 오라클 12.1을 지원하지 않습니다.
- 파이썬 2 호환용 API들
django.utils.lru_cache.lru_cache()
django.utils._os.abspathu()
django.utils.six
- 등
-
FILE_UPLOAD_PERMISSION
설정의 기본 값이0o644
로 바뀝니다. - 기타
-
django.utils.html.escape()가
html.escape()
을 사용합니다. 따라서 이전에는'
기호를 escape하면'
였지만 이제는'
이 됩니다. - 테스트시
-k
옵션을--keepdb
의 축약어가 아닌, 특정 테스트를 가리키는 데 사용합니다. - To limit creation of sessions and hence favor some caching strategies, django.views.i18n.set_language() will stop setting the user’s language in the session in Django 4.0. Since Django 2.1, the language is always stored in the LANGUAGE_COOKIE_NAME cookie.
-
django.utils.html.escape()가
중단 예정인 기능(화살표 뒤의 기능을 사용하길 권합니다)
-
smart_text()
와force_text()
대신smart_str()
와force_str()
을 사용하세요. - URL 파싱 관련
-
django.utils.http.urlquote()
-> urllib.parse.quote() -
urlquote_plus()
-> quote_plus() -
urlunquote()
-> unquote() -
urlunquote_plus()
-> unquote_plus()
-
- gettext 관련
- django.utils.translation.ugettext() -> django.utils.translation.gettext()
- ugettext_lazy() -> gettext_lazy()
- ugettext_noop() ->gettext_noop()
- ungettext() -> ngettext()
- ungettext_lazy() -> ngettext_lazy()
-
is_safe_url()
메서드의 기능을 명확히 드러내고자url_has_allowed_host_and_scheme()
으로 이름을 바꿉니다.
삭제된 기능
-
django.db.backends.postgresql_psycopg2
모듈이 삭제되었습니다. -
django.shortcuts.render_to_response()
가 삭제되었습니다. -
DEFAULT_CONTENT_TYPE
설정 값이 삭제되었습니다. -
HttpRequest.xreadlines()
가 삭제되었습니다. -
Field.from_db_value()
와Expression.convert_value()
에서 사용하던contenxt
전달인자가 삭제되었습니다. -
QuerySet.earliest()
와latest()
메서드에서 사용하던field_name
키워드 인자가 삭제되었습니다. - 템플릿 태그 중
staticfiles
와admin_static
태그가 삭제되었습니다. -
django.contrib.staticfiles.templatetags.staticfiles.static()
가 삭제되었습니다.
요약은 여기까지입니다. 개인적으론 중요하지 않아 보여서 적지 않은 내용이 여러분에겐 더 중요할 수도 있으니, Django 3.0 공식 릴리스 노트를 꼼꼼히 살펴보시는 건 어떨까요?