처음 django 프로젝트를 설정하면 기본으로 설정되어있는 user 모델이 있다. 그래서 따로 user 모델을 추가하지 않더라도 python manage.py createsuperuser
로 user 생성은 가능하다.
이렇듯 장고에는 숨겨진 기능들이 많은데, 사실 한눈에 잘 보이지 않을 뿐 없는 건 아니다. 가장 기본적인 사용자 모델을 코드 수준에서 눈으로 확인하고, AbstractUser, AbstractBaseUser을 사용해서 custom user model을 만드는 과정을 정리해본다.
지금 설정된 유저 모델은 무엇인가?
settings 파일 어디에도 그런 변수는 없을 수 있다. 그러면 global settings에 그 값이 있을 것이다. global settings는 pip로 설치한 django 패키지 내부 어딘가에 있을 테니 먼저 django 패키지부터 찾아야 한다.
파이썬 가상 환경에 따라 파일 구조가 다를 테니 python -c "import site; print(site.getsitepackages())"
로 현재 참조되는 sitepacakges 경로를 확인한다. 해당 경로에 django/conf/global_settings.py를 열어보면 AUTH_USER_MODEL = 'auth.User'라는 설정값을 확인할 수 있다.
그렇다면 기본 유저 모델이 auth.User라는 것을 찾았으니 django/contrib/auth/models.py 에서 User class를 확인한다.
class User(AbstractUser):
class Meta(AbstractUser.Meta):
swappable = 'AUTH_USER_MODEL'
User class에서 속성으로 추가된 건 없고 AbstractUser를 상속받고, AbstractUser는 AbstractBaseUser를, AbstractBaseUser는 models.Model을 상속받는다. 각각 class에 선언된 변수만 살펴보면 다음과 같다.
class AbstractUser(AbstractBaseUser, PermissionsMixin):
username_validator = UnicodeUsernameValidator()
username = models.CharField(
_('username'),
max_length=150,
unique=True,
validators=[username_validator],
)
first_name = models.CharField(_('first name'), max_length=150, blank=True)
last_name = models.CharField(_('last name'), max_length=150, blank=True)
email = models.EmailField(_('email address'), blank=True)
is_staff = models.BooleanField(
_('staff status'),
default=False,
)
is_active = models.BooleanField(
_('active'),
default=True,
)
date_joined = models.DateTimeField(_('date joined'), default=timezone.now)
objects = UserManager()
EMAIL_FIELD = 'email'
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['email']
class AbstractBaseUser(models.Model):
password = models.CharField(_('password'), max_length=128)
last_login = models.DateTimeField(_('last login'), blank=True, null=True)
is_active = True
상속 구조를 알면 나중에 custom user model을 만들 때 어떤 class를 상속받아야 할지가 보인다. 아무튼 이렇게 상속으로 완성된 User 모델이 현재 기본으로 설정되어있다. 이 User 모델이 기본 설정이라는 값은 settings.py에서 확인할 수 있다. auth 관련된 앱이 이미 등록되어있을 것이다.
INSTALLED_APPS = [
...
'django.contrib.auth',
...
]
기본 설정에서 python manage.py createsuerpuser
로 관리자 유저를 생성하면 AbastractUser, AbstractBaseUser 속성들이 적용된 auth_user 테이블에 계정이 추가된다.
커스텀 유저 모델
python manage.py startapp myapp
으로 유저 앱을 생성하고, 이 유저를 기본 사용자 모델로 변경을 해보자.
새로 생성한 myapp/models.py 에 아래와 같이 id, email을 가지는 커스텀 유저 모델을 정의했다. 유저 모델 생성을 관리하는 Manager도 같이 만들어야 한다. Manager는 일반 유저와 관리자 유저를 생성하는 create_user, create_superuesr 두 함수를 추가한다.
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin
from django.contrib.auth.base_user import BaseUserManager
from django.core.validators import validate_email
from django.db import models
import uuid
class UserManager(BaseUserManager):
def create_user(self, email, password=None, **extra_fields):
if not(email):
raise ValueError('Email required')
email = self.normalize_email(email)
validate_email(email)
user, created = self.model.objects.get_or_create(email=email, **extra_fields)
if created and password:
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, password):
user = self.create_user(email=email, password=password)
user.is_superuser = True
user.is_staff = True
user.save(using=self._db)
return user
class User(AbstractBaseUser, PermissionsMixin):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
email = models.CharField(unique=True, max_length=255)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
objects = UserManager()
USERNAME_FIELD = 'email'
이렇게 새로 작성한 유저 모델을 쓰기 위해 전역으로 설정되어있던 AUTH_USER_MODEL 값을 settings.py에서 다시 쓴다.
AUTH_USER_MODEL = 'myapp.User'
makemigrations && migrate로 디비에 스키마를 반영하면 이제 myapp_user 테이블에 커스텀 유저 모델로 생성한 필드들이 보인다. 이 과정을 그대로 해봤으면 알겠지만 AUTH_USER_MODEL을 auth_user에서 커스텀 유저 모델로 바꾸는 과정에 다른 의존성들이 엮여있어서 migration에 실패한다. 개발 단계라면 싹 지우고 다시 하면 되겠지만, 영 좋지 못한 방법이다. 따라서 프로젝트 처음부터 AbstractBaseUser을 상속받아 커스텀 유저 모델을 만드는 것부터 시작하는 게 좋아 보인다.
'python' 카테고리의 다른 글
django rest framework의 serializer, view 추상화 과정 (0) | 2020.06.25 |
---|---|
django model 정리 (0) | 2020.06.13 |
cookiecutter로 django 프로젝트 생성 (0) | 2020.06.11 |
Python 개발 가상환경 설정 (0) | 2019.10.20 |
댓글