반응형
안녕하세요. 글쓰는 개발자 입니다.
Django ORM 을 사용하면, 간편하게 Model을 정의하고 강력한 migration 기능 덕분에 손 쉽게 DB에 Table을 생성 할 수 있습니다. 하지만, 아래와 같이 매번 class Meta 코드를 작성하고 db_table을 정의하는 것이 불편하게 느껴졌습니다.

그래서 BaseModel을 만들고 이를 상속 받기만 하면 Model을 생성 할 때 마다 중복 되는 작업을 없앨 수 있도록 하였습니다. 요구 사항은 다음과 같습니다.
1. Model 의 Class name을 "Model" 을 제외하고 snake case 로 변환한 값을 Table name으로 세팅
2. id 는 Auth Increment로 적용
3. Mixin을 활용해 TimeStamped Column 적용
아래와 같이 BaseModel, TimeStampedModel, TimeStampedUserModel 를 정의 합니다. 각 Model은 abstract = True 로 설정하여, migration 대상에서 제외 시킵니다.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# project_root/models/base.py | |
import re | |
from django.db import models | |
class BaseModel(models.Model): | |
""" | |
기본 Django Model | |
1. primary_key 를 AutoIncrement로 지정 | |
2. 상속 받은 Model의 Class Name을 snake case 로 변환하여, db_table로 지정 | |
- class name 만 뒤에 붙은 Model은 무시 | |
""" | |
id = models.BigAutoField(primary_key=True) | |
class Meta: | |
abstract = True | |
def __init_subclass__(cls, **kwargs): | |
cls.Meta.db_table = cls._set_table_name(cls.__name__) | |
@staticmethod | |
def _set_table_name(raw_name: str) -> str: | |
class_name: str = re.sub(re.compile(r'Model$', re.IGNORECASE), '', raw_name).strip().replace(' ', '_') | |
return re.sub(r"(?<!^)(?=[A-Z])", '_', class_name).lower() | |
class TimestampedModel(models.Model): | |
""" DB Column으로 자주 사용되는 TimeStamp """ | |
created_at = models.DateTimeField(auto_now_add=True) | |
updated_at = models.DateTimeField(auto_now=True) | |
class Meta: | |
abstract = True | |
class TimestampedUserModel(TimestampedModel): | |
""" TimeStamp에 user 정보 필요시 추가 """ | |
created_by = models.CharField(max_length=20) | |
updated_by = models.CharField(max_length=20) | |
class Meta: | |
abstract = True |
- BaseModel
- primary key를 Auto Increment로 자동 생성
- 상속 받은 자식 db_table 설정
- __init_subclass__ : 상속 받은 자식 class name 설정
- _set_table_name : 상속 받은 자식 class name에서 "Model" 을 제외하고 snake case로 변환
- TimeStampedModel : 생성, 수정 시간 컬럼 추가
- TimeStampledUserModel : 생성, 수정 시간 및 생성 사용자, 수정 사용자 컬럼 추가
사용법은 아래와 같습니다. 상속 받아 사용하고, BaseModel과 TimeStampedModel을 같이 써야하는 경우, Mixin을 활용 합니다.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# project_root/models/models.py | |
from .base import ( | |
BaseModel, | |
TimestampedModel, | |
TimestampedUserModel, | |
) | |
class SampleBoardModel(BaseModel): | |
title = models.CharField(max_length=100) | |
content = models.TextField() | |
author = models.CharField(max_length=20) | |
class SampleBoardTimeStampedModel(BaseModel, TimestampedModel): | |
title = models.CharField(max_length=100) | |
content = models.TextField() | |
author = models.CharField(max_length=20) | |
class SampleBoardTimeStampedUserModel(BaseModel, TimestampedUserModel): | |
title = models.CharField(max_length=100) | |
content = models.TextField() | |
author = models.CharField(max_length=20) |
위와 같이 사용했을 때, 각 Model 별 실제 생성되는 Column은 다음과 같습니다.
- SampleBoardModel
- id = models.BigAutoField(primary_key=True)
- title = models.CharField(max_length=100)
- content = models.TextField()
- author = models.CharField(max_length=20)
- SampleBoardTimeStampedModel
- id = models.BigAutoField(primary_key=True)
- title = models.CharField(max_length=100)
- content = models.TextField()
- author = models.CharField(max_length=20)
- created_at = models.DateTimeField(auto_now_add=True)
- updated_at = models.DateTimeField(auto_now=True)
- SampleBoardTimeStampedUserModel
- id = models.BigAutoField(primary_key=True)
- title = models.CharField(max_length=100)
- content = models.TextField()
- author = models.CharField(max_length=20)
- created_at = models.DateTimeField(auto_now_add=True)
- updated_at = models.DateTimeField(auto_now=True)
- created_by = models.CharField(max_length=20)
- updated_by = models.CharField(max_length=20)
반응형