서론
커스텀 User Model
Django Admin User
이번 글에서는 Django에서 제공하는 기본 User Model을 상속해 커스텀 User Model을 구현하고, Django Admin User를 생성해 admin 페이지를 사용해보겠습니다.
username 대신 email을 ID로 사용해 로그인하고, JWT(JSON Web Token)을 사용해 인증하기 위해 커스텀 User Model을 구현해보겠습니다.
Django에서 제공하는 기본 User Model을 사용하다가 향후에 변경하려고 할 경우 DB 스키마를 변경해야하고, 이에 따라 코드도 수정해야하기 때문에 프로젝트 초기에 설정하는 것이 좋습니다.
python manage.py startapp authentication
: 사용자를 인증하는 기능의 app 생성
projectname/settings.py
INSTALLED_APPS = [
...
'authentication.apps.AuthenticationConfig',
]
커스텀 User Model을 만들기에 앞서, User Model을 관리하는 역할을 하는 UserManager를 커스텀해보겠습니다.
BaseUserManager 클래스의 일반 사용자를 생성하는 create_user(), 관리자를 생성하는 create_superuser() 메서드를 재정의합니다.
authentication/managers.py
from django.contrib.auth.models import BaseUserManager
class UserManager(BaseUserManager):
def create_user(self, username, email, password = None, **extra_fields):
if username is None:
raise TypeError("이름을 입력해주십시오.")
if email is None:
raise TypeError("이메일 주소를 입력해주십시오.")
if password is None:
raise TypeError("비밀번호를 입력해주십시오.")
user = self.model(username = username, email = self.normalize_email(email), **extra_fields)
user.setpassword(password)
user.save()
return user
def create_superuser(self, username, email, password, **extra_fields):
# 사용자 생성 후 관리자로 지정
user = self.create_user(username, email, password, **extra_fields)
user.is_superuser = True
user.is_staff = True
user.save()
return user
**extra_fields
: username, email, password 를 제외한 나머지 필드커스텀 User Model을 만드는 여러 가지 방법 중, 자유도가 가장 높은 AbstractBaseUser를 상속받는 방법을 사용하겠습니다.
authentication/models.py
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin
from django.db.models.fields import BooleanField
from .managers import UserManager
class User(AbstractBaseUser, PermissionsMixin):
username = models.CharField(max_length = 255, unique = True)
email = models.EmailField(db_index = True, unique = True)
is_active = BooleanField(default = True)
is_staff = BooleanField(default = False)
# username 대신 email을 로그인 ID로 사용
USERNAME_FIELD = "email"
REQUIRED_FIELDS = ["username"]
# 커스텀 UserManager 등록
objects = UserManager()
def __str__(self):
return self.email
JWT를 적용하기 위해 pyjwt 라이브러리를, 토큰 생성 시 시간을 지정하기 위해 datetime 라이브러리를 사용합니다.
authentication/models.py
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin
from django.db.models.fields import BooleanField
from .managers import UserManager
import jwt
from datetime import datetime, timedelta
from django.conf import settings
class User(AbstractBaseUser, PermissionsMixin):
username = models.CharField(max_length = 255, unique = True)
email = models.EmailField(db_index = True, unique = True)
is_active = BooleanField(default = True)
is_staff = BooleanField(default = False)
# username 대신 email을 로그인 ID로 사용
USERNAME_FIELD = "email"
REQUIRED_FIELDS = ["username"]
# 커스텀 UserManager 등록
objects = UserManager()
def __str__(self):
return self.email
# 사용자의 jwt token을 쉽게 확인하기 위한 함수
@property
def token(self):
return self.generate_jwt_token()
def generate_jwt_token(self):
# token의 기간 설정
exp_date = datetime.now() + timedelta(days = 60)
# token 생성
token = jwt.encode(
{"id": self.pk, "exp": exp_date.utcfromtimestamp(exp_date.timestamp)},
settings.SECRET_KEY, algorithm = "HS256"
)
return token
generate_jwt_token()
: jwt 토큰을 생성하는 메서드
token()
: generate_jwt_token() 메서드를 통해 생성된 토큰을 단순히 확인하기 위한 메서드
커스텀 User Model 구현 이후에는 외부 app의 models.py에서 외래 키로 해당 모델을 호출해 사용합니다.
이때 크게 두 가지 방법을 사용할 수 있는데, 하나는 커스텀 User Model을 직접 지정하는 것이고, 다른 하나는 Django settings.py에 AUTH_USER_MODEL을 설정해 사용하는 것입니다.
모델 직접 지정
class Task(models.Model):
...
owner = models.ForeignKey("authentication.User", ...)
settings.AUTH_USER_MODEL
projectname/settings.py
...
# 사용할 User Model 변경
AUTH_USER_MODEL = 'authentication.User'
todo/models.py
class Task(models.Model):
...
owner = models.ForeignKey("settings.AUTH_USER_MODEL", ...)
첫 번째 방법의 경우, authentication이라는 app의 이름 혹은 User라는 모델의 이름이 변경될 경우, 프로젝트 전체에서 이를 수정해야 합니다.
두 번째 방법의 경우, settings.py에서만 수정하면 되기 때문에 유연성과 유지보수성이 높아서 두 번째 방법을 사용하는 것을 권장합니다.
이제 커스텀 User Model을 기반으로 Admin User를 생성하고, DB에 데이터를 쉽게 CRUD할 수 있는 admin 페이지를 활용해보겠습니다.
python manage.py createsuperuser
: Admin User 생성
admin 페이지에서 사용할 모델 등록(admin.py)
from django.contrib import admin
from .models import *
# Register your models here.
admin.site.register(Task)
admin.site.register(Tag)
admin.site.register(Alarm)
생성한 Admin User를 확인하기 위해 shell_plus에서 User.objects.all()
을 실행하니, django.db.utils.OperationalError: no such table: authentication_user 에러 발생
Django ORM은 기본적으로 테이블 저장 시 appname_modelname의 형태로 저장하기 때문에, Meta 클래스에서 db_table 파라미터를 수정하는 것이 권장됨
User 모델에 아래와 같이 테이블 이름을 "user"로 지정해 해결
class User(AbstractBaseUser, PermissionsMixin):
...
class Meta:
db_table = "user"
shell_plus에서 User.objects.first().token()으로 Admin User의 jwt 토큰을 확인해보면, 아래와 같이 잘 출력됨을 확인 가능
서버 실행 후 127.0.0.1/admin
으로 admin 페이지 접속
아래와 같이 사용자 Group을 관리하고, admin.py에 등록했던 모델들을 CRUD할 수 있음
admin 페이지에서 아래와 같이 필드가 비어있을 경우 의도와는 다르게 일정 모델(Task) 생성이 불가능해서 모델을 수정했습니다.
todo/models.py
class Task(models.Model):
owner = models.ForeignKey("settings.AUTH_USER_MODEL", related_name = "tasks", on_delete = models.CASCADE, null = True)
title = models.CharField(max_length = 255)
description = models.TextField(null = True, blank = True)
due_date = models.DateField(default = timezone.now().date())
priority = models.IntegerField(null = True, blank = True)
status = models.CharField(choices = statusType.choices, max_length = 32, null = True, blank = True)
memo = models.CharField(max_length = 255, null = True, blank = True)
...
owner: Task를 소유하는 사용자
null = True
: DB에 NULL값이 저장되는 것을 허용
blank = True
: 입력 폼이 빈 칸으로 넘어오는 것을 허용
due_date의 경우, timezone 모듈을 사용해 오늘 날짜를 default로 지정