Backend/Django

Django Base Model 을 통한 Model 중복 제거 (db_table, primary_key), __init_subclass__

뒷골목프로그래머 2023. 5. 2. 19:31
반응형

안녕하세요. 글쓰는 개발자 입니다.

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

 

일반적인 Model 생성

 그래서 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 대상에서 제외 시킵니다.

 

# 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
view raw 0_base_model.py hosted with ❤ by GitHub

 

  • BaseModel
    • primary key를 Auto Increment로 자동 생성
    • 상속 받은 자식 db_table 설정
      • __init_subclass__ : 상속 받은 자식 class name 설정
      • _set_table_name : 상속 받은 자식 class name에서 "Model" 을 제외하고 snake case로 변환

 

  • TimeStampedModel : 생성, 수정 시간 컬럼 추가
  • TimeStampledUserModel : 생성, 수정 시간 및 생성 사용자, 수정 사용자 컬럼 추가

 

사용법은 아래와 같습니다. 상속 받아 사용하고, BaseModel과 TimeStampedModel을 같이 써야하는 경우, Mixin을 활용 합니다.

# 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)
view raw 1_example.py hosted with ❤ by GitHub

 

위와 같이 사용했을 때, 각 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)
반응형