계정관리용 accounts app
accounts app 생성 및 프로젝트 settings
python manage.py startapp accounts
위 명령어를 입력해 새로운 앱 accounts 를 만들면 아래와 같이 디렉토리로 묶여진 accounts app 이 생성된다.
만들어진 accounts app 을 myBox 가 사용하기 위해서는 settings.py INSTALLED_APPS 목록에 추가해주어야 한다.
User 모델
Django 에는 직접 제공하고 있는 User Model이 존재한다. 많은 기능을 포함하고 있기 때문에 직접 User 를 정의해주기보단, 해당 클래스를 상속받아 커스텀하는 방식을 많이 사용한다.
표준 User 모델
표준 User 모델은 AbstractUser 를 상속받는다.
class User(AbstractUser):
class Meta(AbstractUser.Meta):
swappable = "AUTH_USER_MODEL"
아래는 AbstractUser 에서 필요한 부분만 가져온 소스이다.
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 Meta:
verbose_name = _("user")
verbose_name_plural = _("users")
abstract = True
username, first_name 등 미리 지정된 속성(칼럼)들이 존재하며, UserManager() 를 호출하여 objects 속성을 정의한다.
Custom User 모델
myBox 서비스에서는
- email, password 를 통해 회원가입
- unique nickname
으로 진행하려 하기 때문에 first_name 과 같은 칼럼은 불필요하다.
아래는 서비스에 맞게 커스텀한 User 모델의 예시이다.
class User(AbstractBaseUser, PermissionsMixin):
# email unique 추가
email = models.EmailField(
_("email address"),
unique=True,
error_messages={
"unique": _("이미 등록된 이메일입니다."),
},
)
# nickname 추가
nickname = models.CharField(
_("nickname"),
max_length=6,
unique=True,
help_text=_(
"6글자 이하의 한글, 영어, 숫자만 포함할 수 있습니다."
),
validators=[validate_nickname],
)
# AbstractUser 와 동일
is_staff = models.BooleanField(
_("staff status"),
default=False,
help_text=_("Designates whether the user can log into this admin site."),
)
is_active = models.BooleanField(
_("active"),
default=True,
help_text=_(
"Designates whether this user should be treated as active. "
"Unselect this instead of deleting accounts."
),
)
date_joined = models.DateTimeField(_("date joined"), default=timezone.now)
# 커스텀한 UserManager 사용
objects = CustomUserManager()
EMAIL_FIELD = "email"
# 회원가입 시 email 필드를 사용
USERNAME_FIELD = "email"
# 값을 필수로 받아야 하는 필드
REQUIRED_FIELDS = ["nickname"]
class Meta:
verbose_name = _("user")
verbose_name_plural = _("users")
def __str__(self):
return self.nickname
def email_user(self, subject, message, from_email=None, **kwargs):
send_mail(subject, message, from_email, [self.email], **kwargs)
AbstractUser 클래스를 참고해서 칼럼 정보를 변경해주었다.
email 의 경우 EmailField 에서 자동으로 이메일 포맷에 대한 유효성 검사를 해주지만, nickname 에 설정한 CharField 는 따로 지정해주어야 한다.
line20을 보면 validators=[validate_nickname], 이 있는데 validate_nickname 은 직접 정의한 validator이다.
# accounts/validators.py
import re
from django.core.exceptions import ValidationError
def validate_nickname(value):
if not re.match(r'^[가-힣a-zA-Z0-9]+$', value):
raise ValidationError("닉네임은 한글, 영어, 숫자만 포함할 수 있습니다.", params={'value': value})
표준 UserManager
Custom한 User 모델의 line 40을 보면 objects = CustomUserManager() 이 있다.
AbstractUser 클래스를 보면 objects = UserManager() 로 정의되어 있는데 Manager 는 장고 모델의 헬퍼클래스로 ORM 의 기능을 담당한다. (User.objects.all() User.objects.filter(~~))
UserManager 는 BaseUserManager 를 상속받아 유저 생성, 슈퍼유저 생성에 대한 메서드가 정의되어 있다.
class UserManager(BaseUserManager):
use_in_migrations = True
def _create_user(self, username, email, password, **extra_fields):
if not username:
raise ValueError("The given username must be set")
email = self.normalize_email(email)
username = GlobalUserModel.normalize_username(username)
user = self.model(username=username, email=email, **extra_fields)
user.password = make_password(password)
user.save(using=self._db)
return user
def create_user(self, username, email=None, password=None, **extra_fields):
extra_fields.setdefault("is_staff", False)
extra_fields.setdefault("is_superuser", False)
return self._create_user(username, email, password, **extra_fields)
def create_superuser(self, username, email=None, password=None, **extra_fields):
extra_fields.setdefault("is_staff", True)
extra_fields.setdefault("is_superuser", True)
if extra_fields.get("is_staff") is not True:
raise ValueError("Superuser must have is_staff=True.")
if extra_fields.get("is_superuser") is not True:
raise ValueError("Superuser must have is_superuser=True.")
return self._create_user(username, email, password, **extra_fields)
CustomUserManager
myBox 에서는 username 이 아닌 email, nickname 을 사용하기에 이 역시 커스텀해주었고 그것이 바로 CustomUserManager 이다.
class CustomUserManager(BaseUserManager):
use_in_migrations = True
def _create_user(self, nickname, email, password, **extra_fields):
# nickname, email 입력 확인
if not nickname:
raise ValueError("nickname을 입력해주세요.")
if not email:
raise ValueError("email을 입력해주세요.")
email = self.normalize_email(email)
user = self.model(email=email, nickname=nickname, **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user
def create_user(self, nickname, email, password=None, **extra_fields):
extra_fields.setdefault("is_staff", False)
extra_fields.setdefault("is_superuser", False)
return self._create_user(nickname, email, password, **extra_fields)
def create_superuser(self, nickname, email, password=None, **extra_fields):
extra_fields.setdefault("is_staff", True)
extra_fields.setdefault("is_superuser", True)
if extra_fields.get("is_staff") is not True:
raise ValueError("Superuser must have is_staff=True.")
if extra_fields.get("is_superuser") is not True:
raise ValueError("Superuser must have is_superuser=True.")
return self._create_user(nickname, email, password, **extra_fields)
Migrate
이제 만들어진 User 모델을 DB에 migrate 하자
makemigrations 명령어를 이용해 변경된 모델정보를 migration file 로 생성한다.
python manage.py makemigrations accounts
migration 에 성공하면 파일이 생성된다.
sqlmigrate 명령어를 이용해 SQL 을 확인할 수 있고, migrate 를 이용해 DB에 변경사항을 적용할 수 있다.
# DB 에 실행하는 SQL 확인
python manage.py sqlmigrate accounts 0001
# DB에 SQL 실행
python manage.py migrate accounts
만들어진 sqlite3 파일을 이용해 DB 연결 후 조회해보면, accounts_user 테이블이 생성됐음을 확인할 수 있다.
{app}_{model} 이 default 로 설정된 테이블이름이며, 이는 모델정의 시 Meta 를 통해 변경할 수 있으나, 여기에서는 변경하지 않도록 하겠다.
그 외 테이블은 상속받은 표준 모델들로 인해 만들어지는 것이며, 이를 이용해 장고에서 사용자를 관리한다.
다음 포스팅은 User 모델을 이용한 회원가입, 로그인, 로그아웃 구현이다.