Canvas 1 Layer 1

Django 3.0 릴리스와 주요 변경 사항
MariaDB 지원, ASGI 지원, 필터 표현식 등

이 글에서는 Django 3.0에 추가된 기능과 바뀐 점을 알아보려 합니다. Django 3.0 release notes를 참고하였습니다.

Django 2.2 버전 이후에 갑자기 3.x대로 바뀌어서 당황하신 분들이 계실 텐데, 다음의 Django 릴리스 타임라인 표를 보면 일종의 규칙이 눈에 들어옵니다.

Django 릴리스 타임라인 - 출처: Download 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 메서드에서 바로 사용할 수 있습니다.

이런 식이 되겠죠.

queryset.filter(Exists(subquery))

예를 들어, 예약이 한 번이라도 있었던 방들의 쿼리셋을 구하려면, Django 3.0 이전 버전에서는 다음과 같이 애너테이션을 한 번 거쳐야 했습니다.

reserved_rooms = Room.objects.annotate(
                has_reserved=Exists(
                    Reservation.objects.filter(
                        room=OuterRef('pk')
                    )
                )
            ).filter(
                has_reserved=True
            )

이제는 다음과 같이 작성할 수 있습니다.

reserved_rooms = Room.objects.filter(
                Exists(
                    Reservation.objects.filter(
                        room.OuterRef('pk')
                    )
                )
            )

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 3.0 공식 릴리스 노트를 꼼꼼히 살펴보시는 건 어떨까요?