[PlanTo # 3] User Model 커스텀

이원진·2023년 7월 13일
0

Planto

목록 보기
3/9
post-thumbnail

목차


  1. 서론

  2. 커스텀 User Model

  3. Django Admin User

0. 서론


이번 글에서는 Django에서 제공하는 기본 User Model을 상속해 커스텀 User Model을 구현하고, Django Admin User를 생성해 admin 페이지를 사용해보겠습니다.

1. 커스텀 User Model


username 대신 email을 ID로 사용해 로그인하고, JWT(JSON Web Token)을 사용해 인증하기 위해 커스텀 User Model을 구현해보겠습니다.

Django에서 제공하는 기본 User Model을 사용하다가 향후에 변경하려고 할 경우 DB 스키마를 변경해야하고, 이에 따라 코드도 수정해야하기 때문에 프로젝트 초기에 설정하는 것이 좋습니다.

app 생성

  • python manage.py startapp authentication: 사용자를 인증하는 기능의 app 생성

  • projectname/settings.py

    INSTALLED_APPS = [
        ...
        'authentication.apps.AuthenticationConfig',
    ]
    • settings.py에 authentication app 등록

UserManager 구현

커스텀 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 구현

커스텀 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
    • PermissionsMixin: 그룹과 사용자의 권한을 관리하는 역할

커스텀 User Model에 JWT 적용

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 불러오기

커스텀 User Model 구현 이후에는 외부 app의 models.py에서 외래 키로 해당 모델을 호출해 사용합니다.

이때 크게 두 가지 방법을 사용할 수 있는데, 하나는 커스텀 User Model을 직접 지정하는 것이고, 다른 하나는 Django settings.py에 AUTH_USER_MODEL을 설정해 사용하는 것입니다.

  1. 모델 직접 지정

    class Task(models.Model):
        ...
    
        owner = models.ForeignKey("authentication.User", ...)

  2. 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에서만 수정하면 되기 때문에 유연성과 유지보수성이 높아서 두 번째 방법을 사용하는 것을 권장합니다.


2. Django Admin User


이제 커스텀 User Model을 기반으로 Admin User를 생성하고, DB에 데이터를 쉽게 CRUD할 수 있는 admin 페이지를 활용해보겠습니다.

  • python manage.py createsuperuser: Admin User 생성

    • ID, PW 설정

  • 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를 소유하는 사용자

      • 한 명의 사용자가 여러 개의 Task를 가질 수 있으며, 사용자 삭제 시 Task도 삭제

    • null = True: DB에 NULL값이 저장되는 것을 허용

    • blank = True: 입력 폼이 빈 칸으로 넘어오는 것을 허용

    • due_date의 경우, timezone 모듈을 사용해 오늘 날짜를 default로 지정


0개의 댓글