나는 현재 멘토링을 진행하고 있다. 멘토링을 진행하면서, 어제 받은 멘티의 질문이 과거 나 또한 고민했던 내용이었기에 해당 내용을 정리해둔다면, 다른 사람들에게 도움이 될 수 있는 정보가 될 수 있지 않을까 하는 생각에서 내용을 정리해본다.
질문의 내용을 정리한다면 다음과 같다.
- 주요 질문: AbstractBaseUser를 상속받는 AbstractUser는 따로 사용할 필요가 없는가?(AbstractBaseUser의 사용 시기는 언제인가?)
- 추가 질문: 데이터 설계 시, 필요없는 필드를 포함해도 괜찮은가?
과거 django(이하 장고)를 사용할 때, 나 또한 이러한 고민을 했었다. 장고를 사용하면서 가장 좋았던 점은 필요한 것들은 이미 거의 대부분 모듈로 제공되고 있었고, User 또한 그러하였으며, 추후 사이드 프로젝트를 진행하면서 이와 같은 고민을 했었다.
- 나는 내가 원하는 필드만 사용하고 싶었다.(장고에서 제공하는 User 클래스에는 내가 필요로하지 않는 속성 값들이 너무 많았다.)
- 사용하지 않는 필드 값들에 나는 의미 없는 값들을 넣거나, 불편한 과정을 억지로 넣어야 했다.
다시 지금으로 돌아와 질문을 해주신 멘티님 또한 과거 나와 비슷한 상황이지 않을까? 하는 생각을 해본다.
Djagno User 모델 커스터마이징: AbstractBaseUser vs AbstractUser
AbstractBaseUser 클래스와 AbstractUser 클래스 중 어떤 클래스를 사용해야 할까? 이에 대한 답은 상황에 따라 다르다.
AbstractBaseUser
class AbstractBaseUser(models.Model):
REQUIRED_FIELDS: list[str] = ...
password = models.CharField(max_length=128)
last_login = models.DateTimeField(blank=True, null=True)
is_active: bool | BooleanField[Any] = ...
def get_username(self) -> str: ...
def natural_key(self) -> tuple[str]: ...
@property
def is_anonymous(self) -> Literal[False]: ...
@property
def is_authenticated(self) -> Literal[True]: ...
def set_password(self, raw_password: str | None) -> None: ...
def check_password(self, raw_password: str) -> bool: ...
def set_unusable_password(self) -> None: ...
def has_usable_password(self) -> bool: ...
def get_session_auth_hash(self) -> str: ...
@classmethod
def get_email_field_name(cls) -> str: ...
@classmethod
def normalize_username(cls, username: _T) -> _T: ...
장고를 사용하면서, User 모델을 완전히 커스텀하고 싶을 경우 사용한다. 즉 AbstractBaseUser는 최소한의 속성만 제공하면서 사용자가 직접 커스텀 할 수 있도록 하면서 장고가 제공하는 인증 모듈을 사용할 수 있게한다.
- 최소한의 기본 속성(password, last_login)만제공한다.(장고에서 제공하는 기본적인 인증 기능 구현을 구현하는데 최소한의 필드(속성)값만을 갖고 있음
- 최소한의 속성 값 + 내가 만들고자 하는 속성 값을 추가할 때 사용 됨
AbstractUser
그렇다면 AbstractUser 은 언제 사용해야 할까? 그 답은 AbstractUser 내 작성된 코드를 보면 알 수 있다.
# models.py
class PermissionsMixin(models.Model):
is_superuser = models.BooleanField()
groups = models.ManyToManyField[Group, Any](Group)
user_permissions = models.ManyToManyField[Permission, Any](Permission)
def get_user_permissions(self, obj: _AnyUser | None = ...) -> set[str]: ...
def get_group_permissions(self, obj: _AnyUser | None = ...) -> set[str]: ...
def get_all_permissions(self, obj: _AnyUser | None = ...) -> set[str]: ...
def has_perm(self, perm: str, obj: _AnyUser | None = ...) -> bool: ...
def has_perms(
self, perm_list: Iterable[str], obj: _AnyUser | None = ...
) -> bool: ...
def has_module_perms(self, app_label: str) -> bool: ...
class AbstractUser(AbstractBaseUser, PermissionsMixin):
username_validator: UnicodeUsernameValidator = ...
username = models.CharField(max_length=150)
first_name = models.CharField(max_length=150, blank=True)
last_name = models.CharField(max_length=150, blank=True)
email = models.EmailField(blank=True)
is_staff = models.BooleanField()
is_active = models.BooleanField()
date_joined = models.DateTimeField()
EMAIL_FIELD: str = ...
USERNAME_FIELD: str = ...
def get_full_name(self) -> str: ...
def get_short_name(self) -> str: ...
def email_user(
self, subject: str, message: str, from_email: str | None = ..., **kwargs: Any
) -> None: ...
objects: ClassVar[UserManager[Self]] # type: ignore[assignment]
class User(AbstractUser): ...
AbstractUser 는 AbstractBaseUser 모델을 상속받고 있다. 즉, "최소한의 속성 + 장고에서 기본 제공하는 속성을 포함" 하는 것을 의미한다.
- 장고에서 제공하는 모든 속성 값을 사용하나, 약간의 속성 값 추가를 할 경우 사용하기에 적절
정리한다면, 프로젝트에서 생성하는 User 클래스를 어떻게 설계하느냐에 따라 AbstractBaseUser 를 사용할지 AbstractUser 를 사용할지 결정하면 된다.
- 1. Django에서 제공하는 User 클래스를 그대로 사용한다.
- 2. Djaogo에서 제공하는 User 클래스의 속성을 모두 사용하면서, 약간의 커스텀 필드를 추가한다면 AbstractUser 를 사용한다.
- 3. Django에서 제공하는 User 클래스를 사용하지 않고, 나만의 커스텀 클래스를 설계하는 경우, AbstractBaseUser 를 사용한다.
'개인공부' 카테고리의 다른 글
Gradle의 apply from을 통해 의존성 관리하기 (0) | 2024.11.13 |
---|---|
[ SQLAlchemy ] selecteload 와 joinload의 차이는 무엇일까? (1) | 2024.11.12 |
[ Pydantic ] Pydantic을 활용한 Serialization(직렬화)와 Deserialization(역직렬화) (0) | 2024.05.30 |
[ Pydantic ] Pydantic의 BaseModel 사용하기 (0) | 2024.05.28 |
[ JPA ] 다양한 어노테이션을 사용한 효과적인 엔터티 매핑_2.연관관계 매핑 (0) | 2024.02.12 |