본문 바로가기

BackEnd/DRF Project

[# DRF Project_User] 2. Custom User model(커스텀 User)

반응형

 


Django Rest Framework

 

Part.2

Django는 User Model을 기본적으로 제공합니다. (*기본 User Model DOC) 하지만 대부분의 경우 User Model을 재구성해야 합니다. 각자 필요에 맞게 User Model을 재구성하는 방법을 알아보도록 하겠습니다.

이번 파트에서는 상황에 맞는 User Model을 재구성하는 방법에 대해 공부해보도록 하겠습니다.

목차

1. BaseUserManager
2. User Model 만들기(1) - TimestampedModel, UserModel
3. User Model 만들기(2) - UserModel에 JWT 적용
4. CreateSuperUser

 


1. BaseUserManager

Django는 커스텀 유저(Custom User)를 정의하기 위해서는 UserManager Class가 필요합니다. 우리는 BaseUserManager를 상속받아 create_user 함수를 재정의 하고, User Model에서 BaseUserManager를 상속받아 사용하도록 하겠습니다.

먼저 BaseUserManager에 대해 간단히 알아보도록 하겠습니다. BaseUserManager는 User를 생성할 때 사용하는 헬퍼 클래스(Helper class) 입니다. User를 생성할 때는 User data가 BaseUserManager를 거쳐 생성됩니다. 우리는 이번에 create_user 함수와 create_superuser 함수를 정의해 사용하도록 하겠습니다.

create_user 함수는 관리자를 포함한 모든 사용자를 생성할 때 실행되는 함수입니다. User에 해당하는 필드(Field) 값들을 받고, 이를 데이터베이스에 저장하는 역할을 합니다. 기본적으로 "is_superuser"와 "is_staff" 필드는 False로 지정되어 있습니다. 이 필드들은 관리자를 생성할 때는 아래에서 설명하는 create_superuser 함수를 거쳐 True값으로 변경됩니다.

create_superuser 함수는 관리자를 생성할 때 실행되는 함수입니다. 우선 들어온 정보들을 받아 일반 사용자를 만드는 함수인 create_user 에서 사용자 정보를 데이터베이스에 저장하고, "is_supseruser", "is_staff" 부분을 True로 만들어 데이터베이스에 저장합니다.

코드를 작성해보도록 하겠습니다. 우선 "shop_drf/authentication" 경로에 "managers.py" 파일을 만들어 아래 코드를 입력합니다.

# shop_drf > authentication > managers.py
from django.contrib.auth.models import BaseUserManager


class UserManager(BaseUserManager):

    # All user
    def create_user(self, username, email, password=None, **extra_fields):
    
        if username is None:
            raise TypeError('Users must have a username.')

        if email is None:
            raise TypeError('Users must have an email address.')

        if password is None:
            raise TypeError('Users must have a password.')
    
        user = self.model(
        username = username,
        # 중복 최소화를 위한 정규화
        email=self.normalize_email(email),
        **extra_fields
        )

        # django 에서 제공하는 password 설정 함수
        user.set_password(password)
        user.save()
        
        return user

    # admin user
    def create_superuser(self, username, email, password, **extra_fields):
        
        if password is None:
            raise TypeError('Superuser must have a password.')
        
        # "create_user"함수를 이용해 우선 사용자를 DB에 저장
        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'를 제외한 필드(Field)들을 의미합니다. 별도의 조건을 만족하게하고 싶은 경우 따로 명시해 받은 후 조건을 확인하면 됩니다.

User Model의 뼈대가 되는 BaseUserManager를 만들었으니 이제는 본격적으로 User Model을 만들어보도록 하겠습니다.

 


2. User Model 만들기(1) - TimestampedModel, UserModel

이번에는 User Model을 만들어보도록 하겠습니다. 우리는 "Email"을 ID로 두고, Login 할 때 "Email" 과 "Password"를 통해 접속할 수 있도록 하겠습니다.

또 사용자가 접속할 때 JWT(Json Web Token)를 이용해 인증할 수 있도록 하겠습니다. (JWT에 대해 궁금하신 분은 제가 걸어둔 링크로 접속하셔서 보시기 바랍니다.)

본격적으로 User Model을 만들기 전에 우리는 TimestampedModel을 만들어 User Model이 상속받을 수 있도록 하겠습니다. 이 모델은 사용자가 ID를 만든 시간과 마지막으로 로그인한 시간이 정의된 모델입니다. TimestampedModel을 따로 만들어 상속받아 사용하는 이유는 다른 Model들을 만들 때에도 객체의 생성 시간과 업데이트 된 시간이 기록되도록 하기 위함입니다. 즉, 다른 모델에서도 TimestampedModel을 사용하기 때문입니다.

먼저 재사용될 가능성이 있는 class, function들을 모아둘 'core' 폴더를 'shop_drf' 밑에 만들어 줍니다. 그리고 그 밑에 models.py 이라는 파일을 하나 만들어줍니다. 앞으로 이곳에는 각 앱의 model들이 공통적으로 사용하는 model을 위한 class 혹은 function들을 만들어 놓을 예정입니다.

지금까지 잘 따라오셨으면 폴더의 모양은 그림1과 같은 모양일 겁니다.

그림1

다음으로 models.py 파일에 아래 내용을 입력합니다.

# shop_drf > core > models.py
from django.db import models


class TimestampedModel(models.Model):
	
    # 생성된 날짜를 기록
    created_at = models.DateTimeField(auto_now_add=True)
    # 수정된 날짜를 기록
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        abstract = True
        ordering = ['-created_at', '-updated_at']

본격적으로 TimestampedModel을 상속받아 User 모델을 만들어보도록 하겠습니다.

 

# shop_drf > 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
from core.models import TimestampedModel


class User(AbstractBaseUser, PermissionsMixin, TimestampedModel):
    
    username = models.CharField(max_length=255, unique=True)
    email = models.EmailField(db_index=True, unique=True)
    phone_number = models.CharField(max_length=255)
    is_active = BooleanField(default=True)
    is_staff = BooleanField(default=False)
    
    USERNAME_FIELD = 'email'
    
    REQUIRED_FIELDS = [
        'username',
        'phone_number'
    ]
    
    objects = UserManager()
    
    def __str__(self):
        return self.email
    
    def get_full_name(self):
        return self.username
    
    def get_short_name(self):
        return self.username
  • AbstractBaseUser
    - AbstractBaseUser 모델을 상속한 User 커스텀 모델을 만들면 *로그인 ID로 이메일 주소를 사용(USERNAME_FIELD='email')하거나 Django 로그인 절차가 아닌 다른 인증 절차를 직접 구현할 수 있습니다.
  • PermissionsMixin
    - Django의 기본 그룹, 허가권 관리 기능을 재사용합니다.
    - PermissionsMixin 공식문서
  • USERNAME_FIELD
    - USERNAME_FIELD 로 지정된 값을 흔히 말하는 로그인 ID로 사용합니다.
  • REQUIRED_FILEDS
    - 필수로 값을 받아야 하는 필드를 명시합니다.
  • objects
    - 앞서 정의한 UserManager를 입력해줍니다.

3. User Model 만들기(2) - UserModel에 JWT 적용

JWT를 Model에 적용시키기 위해서는 우선 jwt 를 사용할 수 있도록 설치해야합니다.

jwt 설치  pip install pyjwt  

 

Welcome to PyJWT — PyJWT 2.3.0 documentation

© Copyright 2015, José Padilla Revision aabeb061.

pyjwt.readthedocs.io

 

jwt를 import 하고, 토큰 생성 로직에 사용될 datetime도 import 합니다.

#  shop_drf > authentication > models.py
+ import jwt
+ from datetime import datetime, timedelta

from django.conf import settings
+ from django.conf import settings
from django.db import models
. . .

다음으로 UserModel에 token을 만드는 함수를 입력합니다.

#  shop_drf > authentication > models.py
class User(AbstractBaseUser, PermissionsMixin, TimestampedModel):
    . . .

    def get_short_name(self):
        return self.username

    +@property
    +def token(self):
    +    return self._generate_jwt_token( )

    +def _generate_jwt_token(self):
    +    dt = datetime.now( ) + timedelta(days=60)

    +    token = jwt.encode({
    +        'id': self.pk,
    +        'exp': dt.utcfromtimestamp(dt.timestamp())
    +    }, settings.SECRET_KEY, algorithm='HS256')

    +return token

  • def token
    사용자의 token을 확인할 때 'user.generate_jwt_token( )'으로 확인하기 복잡하기 때문에 보다 간단하게 확인할 수 있도록 'user.token'을 정의하는 함수입니다. 별다른 값 없이 '_generate_jwt_token' 을 통해 return된 값을 반환합니다.
  • def _generate_jwt_token
    토큰을 발행하는 함수입니다. 안에 들어가는 setting 값들은 pyjwt 홈페이지에서 확인하시기 바랍니다. 참고로 dt에서 설정한 "days=60" 은 60일 뒤 token이 만료됨을 뜻합니다.

UserModel 만들기가 끝났습니다. settings.py 파일에 'authentication' 앱을 등록하고, 작성한 UserModel을 DB에 적용시키기 위해 miration을 진행하겠습니다.

먼저 settings.py 파일에 앱을 등록하도록 하겠습니다. 추가적으로 AUTH_USER_MODEL 을 settings.py 파일에 입력해서 이 홈페이지에서 UserModel로 사용할 모델이 우리가 지금 만든 'User' 모델이라는 것을 명시해줍니다. 저는 AUTH_USER_MODEL을 가장 settings.py 파일의 가장 하단에 입력했습니다.

# shop_drf > setting > settings.py
. . .

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    +'authentication',
    +'rest_framework',
]

. . .
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
+AUTH_USER_MODEL = 'authentication.User'

migration을 진행하겠습니다.

  python manage.py makemigrations  
  python manage.py migrate  

이 과정에서 django.db.utils.OperationalError: no such table: 오류가 발생하면 다음 명령어를 실행해주시기 바랍니다.

  python manage.py migrate --run-syncdb  

만약 위의 명령어를 실행해도 해결이 되지 않는다면 migration 폴더에 있는 파일들을 지우고 DB 파일인 db.sqlite3을 삭제하시고 진행해보시기 바랍니다.


4. CreateSuperUser

이제 Custom한 UserModel로 SuperUser를 만들어보도록 하겠습니다. 터미널에 다음 명령어를 입력해주세요.

  python manage.py createsuperuser  

그러면 그림3과 같이 정보를 입력하는 부분이 나옵니다. 입력해보세요.

그림3

이제 제대로 입력이 완료됐는지 shell을 통해 확인해보도록 하겠습니다. 우선 shell에서 모델들을 한번에 import 할 수 있는 기능을 사용하기 위해 'extensions'를 설치해주겠습니다.

  pip install django-extensions  

다음으로 settings.py 파일로 들어가서 INSTALLED_APPS 부분에 'extensions'를 등록해주겠습니다.

# shop_drf > setting > settings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
   
    +'django_extensions',
   
    'authentication',
    'rest_framework',
]

이제 터미널에 아래 명령어를 실행해 shell을 실행시켜보겠습니다.

  python manage.py shell_plus  

그림4와 같이 각종 기능들이 import 되는 것을 확인하실 수 있습니다.

그림4

이 중에서 저는 User 모델을 이용해 방금 등록한 Superuser의 정보를 확인해보도록 하겠습니다.

User모델의 모든 User를 검색합니다.
>>> User.objects.all()

<QuerySet [<User: admin@test.com>]>

Superuser를 'superuser'에 할당합니다.
>>> superuser = User.objects.get(email='admin@test.com') 
>>> superuser
<User: admin@test.com>

superuser의 token을 확인해보겠습니다.
>>> superuser.token
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MS. . .'

의도한 값들이 제대로 출력된 것을 확인할 수 있습니다.

다음시간에는 User를 등록하기 위한 Serializer와 View, Url을 만들어보도록 하겠습니다.

다음글

https://axce.tistory.com/108

 

[# DRF Project] 3. Registering New User(새로운 User 등록)

Django Rest Framework Part.3 지금까지 만든 User Model에 새로운 User를 등록하는 기능을 만들어보도록 하겠습니다. 목차 1. serializers.py 2. views.py 3. urls.py 4. renderers.py 5. views.py 1. serializer..

axce.tistory.com


참고자료

JWT(Json Web Token)

 

JWT.IO

JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.

jwt.io

기본 User Model DOC

 

django.contrib.auth | Django documentation | Django

Django The web framework for perfectionists with deadlines. Overview Download Documentation News Community Code Issues About ♥ Donate

docs.djangoproject.com

pyjwt

 

Welcome to PyJWT — PyJWT 2.3.0 documentation

© Copyright 2015, José Padilla Revision aabeb061.

pyjwt.readthedocs.io

 

반응형