이 글은 모범 사례로 배우는 Django 테크닉에 관한 명서 "Two Scoops of DJango"(대니얼 로이 그린펠드, 오드리 로이 그린펠드 저, 2016)를 읽고 요약 정리하여 작성한 것입니다. 이 책을 읽고 새로웠던 것, 기존에 잘 알지 못했던 것을 위주로 작성했습니다.
모델 작업에 사용 가능한 장고 패키지들
- django-model-utils
- TimeStampedModel과 같은 일반적인 패턴들을 처리하는 데 이용 가능
- django-extensions
- 모든 앱에 모델 클래스를 자동으로 로드해 주는 shell_plus라는 강력한 관리 명령을 제공함
장고 모델 기본
모델이 너무 많으면 앱을 나눈다
각 앱이 가진 모델 수가 다섯개를 넘지 않는 것이 좋다.
모델 상속에 주의하자
모델의 상속 스타일
추상화 기초 클래스
class Meta:
abstract = True
모델 클래스에 위 코드를 추가하면 된다.
- 장점
- 추상화된 클래스에 공통적인 부분을 추려 놓음으로써 한 번만 타이핑을 하면 된다.
- 추가 테이블이 생성되지 않고, 여러 테이블에 걸쳐 조인을 하며 생기는 성능 저하 역시 발생하지 않는다.
멀티 테이블 상속
- 장점
- 부모와 자식 모델에 대해서도 모두 테이블이 생성된다.
- OneToOneField는 부모와 자식 간에 적용된다.
- 부모 객체로부터 자식 객체를 호출할 수 있다.
- 단점
- 자식 테이블에 대한 각 쿼리에 대해 부모 테이블로의 조인이 필요하다.
- 따라서 이에 따른 부하가 발생한다.
- 이 책에서는 아주 강력하게 멀티테이블 상속을 이용하지 말 것을 권한다(그 대신 모델들 사이에서 좀 더 명확한 OneToOneField와 ForeignKeys를 사용해서 조인을 수월하게 컨트롤 할 것을 권하고 있다).
프락시 모델
- 장점
- 원래 모델에 대해서만 테이블이 생성 된다.
- 각기 다른 파이썬 작용(behavior)을 하는 모델들을 별칭으로 정의할 수 있다.
- 단점
- 모델의 필드를 변경할 수 없다는 단점이 있다.
데이터베이스 마이그레이션
- 자체적인 django.db.migrations 스타일로 이루어지지 않은 외부 앱에 대해서 마이그레이션을 처리할 때는
Migration_MODULES
세팅을 이용해야 한다.
- 마이그레이션 개수가 너무 많다고 생각 되면
squashmigratioins
를 이용해 볼 것
마이그레이션의 배포와 관리
- 배포 전에 꼭 마이그레이션을 rollback 할 수 있는지 확인해 보는 것이 좋다.
- MySQL의 경우 스키마 변경에 대해 트랜잭션을 지원하지 않기 때문에 롤백이 불가능하다.
모델 디자인 하기
캐시와 비정규화
- 기본적으로 데이커베이스 정규화에 익숙해져야 한다.
- 그러나, 적절한 위치에서 캐시를 세팅하는 것든 모델을 비전규화할 때 발생하는 문제점들을 상당 부분 해결해 준다.
- 따라서, 비정규화를 생각하기 전에 캐시에 대해 좀 더 연구해 보는 것이 좋다. 왜냐하면, 비정규화에 의해 프로젝트가 복잡해지며 데이터를 손상시킬 위험이 증가할 수 있기 때문이다.
모델 필드
null과 blank
문자와 관련된 필드
CharField, TextField, SlugField, EmailField 등
- null=True: 이용 X, 장고 표준은 빈 값을 빈 문자열로 저장하는 것이기 때문에 널 또는 빈 값을 빈 문자열을 대신해 반환해야 한다.
- blank=True: 이용 O, 위젯이 빈 값을 허용하기를 원한다면 설정하며 해당 설정에 의해 빈 값은 "빈 문자열"로 저장된다.
파일과 관련된 필드
FileField, ImageField
- null=True: 이용 X, 장고에서 MEDIA_ROOT의 경로를 CharField에서 파일 또는 이미지로 저장하며 이는 FieldField도 마찬가지이다.
- blank=True: 이용 O, 위의 문자와 관련된 필드에서와 마찬가지의 기준이 적용된다. 왜냐하면 ImageField가 어차피 CharField로 저장되기 때문이다.
불리언 필드
BooleanField
- null=True: 이용 X
- blank=True: 이용 X
숫자와 관련된 필드
IntegerField, FloatField, DecimalField, DurationFIeld 등
- null=True: 이용 O
- blank=True: 이용 O, null=True와 같이 이용해야 하며 위젯에서 해당 값이 빈 값을 받아와도 문제가 없을 경우 사용한다.
날짜와 관련된 필드
DataField, DateTimeField 등
- null=True: 이용 O
- blank=True: 이용 O, null=True와 같이 이용해야 하며 위젯에서 해당 값이 빈 값을 받아와도 문제가 없거나 auto_now나 auto_now_add를 이용하고 있을 경우 쓴다(앞서 설명한 django-model-utils를 쓴다면 이는 문제가 되지 않을 것이다).
관계와 관련된 필드
ForeignKey, ManyToManyField, OneToOneField
- null=True: 이용 O
- blank=True: 이용 O, 위젯에서 해당 값이 빈 값을 받아와도 문제가 없을 경우 사용한다.
모델 메니저
모델에 질의를 할 경우 장고의 ORM을 통하게 되는데, 이 때 데이터베이스와 연동하는 인터페이스인 모델 메니저를 호출하게 된다. 이는 클래스를 제어하고자 할 때 사용이 가능하며 모델 클래스의 모든 인스턴스 세트에 작동한다.
아래와 같이 사용할 수 있다.
from django.db import models
from django.utils import timezone
class PublishedManager(models.Manager):
use_for_related_fields = True
def graduated(self, **kwargs):
return self.filter(created__lte=timezone.now(), **kwargs)
class Student(models.Model)
name = models.CharField(max_length=10)
created = models.DateTimeField()
objects = PublishManager()
그러면 Student 모델에 대해 published가 몇 개인지 아래와 같이 확인할 수 있다.
Student.objects.graduated().count()
커스텀 메니저를 만들기 위해서는 models.Manager를 상속 받아 사용해야 한다.
또한, 사용하려는 모델에 objects
로 꼭 정의해 주어야 한다.
거대 모델
거대 모델은 데이터 관련 코드를 뷰에 넣지 않고 모델 메서드, 클래스 메서드, 프로퍼티 또는 매니저 메서드 안에 넣어 캡슐화 하는 것을 말한다.
예를 들면, 아래와 같이 옷 리뷰를 보여주는 모델에 메서드를 덧붙일 수 있다.
product_average
라는 함수는 리뷰의 평균 점수를 반환하는 리뷰 인스턴스의 속성이다.
review.product_average
거대 모델을 이용하면 코드 재사용을 개선할 수 있다.
그런데, 거대 모델이 너무 커질 경우 가독성도 떨어지고 복잡해질 수 있다.
그럴 때는 거대 모델의 코드를 보통 기본적인 형태로 분리하게 되는데 메서드들과 클래스 메서드, 프로퍼티들을 그대로 유지한 채로 이들이 지닌 로직들을 모델 행동
혹은 상태 없는 헬퍼 함수
로 이전한다.
모델 행동
헬퍼 함수
- 상태가 없는 헬퍼 함수
- 모델로부터 로직을 떼어내 유틸리티 함수로 넣어 독립적으로 구성할 수 있다.
- 로직 테스트가 쉬워진다는 장점이 있다.
- 그러나, 해당 함수들이 상태를 가지지 않기 때문에 함수에 더 많은 인자가 필요하게 된다.
성능 높이기
데이터베이스 성능을 높이기 위해 비정규화를 고려해 보기 전에 다음과 같은 사항을 먼저 생각해 봐야 한다.
- 로우 쿼리 적용을 통해 복잡한 쿼리 단순화 하기
- 캐시 이용하기
- 인덱스 이용하기
- 1~3번을 전부 다 실행해 봐도 소용이 없다면 그 때는 비정규화 하기
참고자료
Two Scoops of DJango"(대니얼 로이 그린펠드, 오드리 로이 그린펠드 저, 2016)