Django User Model 설계

손성수·2023년 7월 5일
0
post-thumbnail

User Model 소개

사용 목적

  1. 서비스의 회원 정보 저장
  2. 서비스를 이용하기 위한 로그인 기능 제공
  3. 다른 모델과 각각의 관계를 맺어 서비스 연결

ERD 설계


로그인 관련 필드

  • email : 사용자를 구분, 및 로그인을 위한 필드이므로 unique 설정

  • password : 서비스에 가입한 사용자가 로그인을 하기위한 비밀 번호

  • login_type : 복잡한 절차를 생략하고 사용자 편의성을 위한
    손 쉬운 가입을 위한 소셜 로그인 구현,
    kakao,google,naver 소셜 계정이 있으며, 일반 계정으로 구분합니다.
    소셜 로그인 유저는 이메일,비밀번호 필드를 수정할 수 없습니다.

  • login_attempts_count : 보완성을 강화하기 위하여 로그인 실패 횟수를 기록하여 일정 횟수 이상의 로그인 실패가 기록될 경우
    로그인을 할 수 없습니다.

  • last_login : AbstractBaseUser를 상속받아 사용할 수 있게된 필드입니다. 사용자의 마지막 로그인 기록이 언제인지 확인할 수 있습니다.

프로필 관련 필드

  • nickname : 서비스내에서 타 유저를 확인하고, 자신을 소개할 별명을 사용하기위한 필드

  • profile_image : 서비스에서 타 유저를 확인하고, 자신을 소개할 이미지 필드

  • introduction : 커뮤니티 사이트 구축 확장성을 고려하여
    사용자의 소개 메시지를 다른 사용자에게 보여주기 위한 필드 입니다.

개인 정보 관련 필드

  • customs_code : 통관 번호 필드, 추후 확장성을 고려하여 실 판매 행위가 이루어진다면 해외 직구 사이트의 특성상 필요한 데이터입니다.

권한 필드

  • is_admin : True or False, 둘 중 하나의 값을 갖는 Boolean 필드입니다. 위 필드를 통하여 사용자가 관리자 계정인지, 아닌지 판단할 수 있습니다.

  • is_active : True or False, 둘 중 하나의 값을 갖는 Boolean 필드입니다. 위 필드를 통하여 사용자가 활성화된 계정인지, 비 활성화된 계정인지 확인할 수 있습니다.

  • is_seller : True or False, 둘 중 하나의 값을 갖는 Boolean 필드입니다. 위 필드를 통하여 사용자가 판매자 활동 권한을 갖는 계정인지 판단할 수 있습니다.

관계형 필드

  • product_wish_list : 상품 찜 기능 구현을 위한 필드
    Product Model과 Many - To - Many

  • review_like : 리뷰 좋아요 기능 구현을 위한 필드
    Review Model과 Many - To - Many

  • Follower : 판매자 팔로우 기능 구현을 위한 필드
    User Model과 Many - To - Many

상속

  • AbstractBaseUser을 상속 받아
    last_login, is_authenticated, set_password 등등의
    다양한 편리한 기능을 제공받아 사용하엿다.

공통 상속 Model

  • 생성일과 수정일 필드는 모든 모델에서 사용하므로 공통 모델을 지정하였다.
    User Model의 경우, 다중 상속을 통해 추가 필드를 제공 받았다.

  • created_at : object의 생성 시간 저장

  • updated_at : object의 수정 시간 저장

로그인 필드 설정

  • USERNAME_FIELD를 email로 지정하여 로그인에 필요한 필드 지정
  • REQUIRED_FIELD를 추가로 지정하여 오브젝트 생성시 필수로 받아야할 필드를 지정 했다.
USERNAME_FIELD = "email"
REQUIRED_FIELDS = ["nickname", "password"]

모델 설계

class User(AbstractBaseUser, CommonModel):
    """
    Base User model 정의
    """

    LOGIN_TYPES = [
        ("normal", "일반"),
        ("kakao", "카카오"),
        ("google", "구글"),
        ("naver", "네이버"),
    ]
    
    # 로그인 관련 필드
    email = models.EmailField("이메일 주소", max_length=100, unique=True)
    password = models.CharField("비밀번호", max_length=128)
    login_type = models.CharField("로그인유형", max_length=20, choices=LOGIN_TYPES, default="normal")
    login_attempts_count = models.PositiveIntegerField("로그인 시도 횟수", default=0)
    
    # 프로필 관련 필드
    nickname = models.CharField("사용자 이름", max_length=20)
    profile_image = models.ImageField("프로필 이미지", upload_to=img_upload_to, blank=True, null=True)
    introduction = models.CharField("소개", max_length=50, blank=True, null=True, default="아직 소개글이 없습니다.")

	# 개인 정보 관련 필드
    customs_code = models.CharField("통관번호", max_length=100, blank=True, null=True)

	# 관계형 관련 필드
    product_wish_list = models.ManyToManyField("products.Product", symmetrical=False, related_name="wish_lists", blank=True)
    review_like = models.ManyToManyField("products.Review", symmetrical=False, related_name="review_liking_people", blank=True)
    follower = models.ManyToManyField('self', symmetrical=False, related_name="followings", blank=True)

	# 권한 관련 필드
    is_admin = models.BooleanField(default=False)
    is_active = models.BooleanField(default=False)
    is_seller = models.BooleanField(default=False)  # 판매자 신청 후 관리자 승인하 에 판매 권한 획득

	
    USERNAME_FIELD = "email"
    REQUIRED_FIELDS = ["nickname", "password"]
    objects = UserManager()

    def __str__(self):
    	"""
        오브젝트의 이름을 email로 지정
        """
        return self.email

    def has_perm(self, perm, obj=None):
        return True

    def has_module_perms(self, app_label):
        return True

    @property
    def is_staff(self):
        return self.is_admin


관리자 모델 커스텀

BaseUserManager 이해하기

  • Django에서 제공하는 사용자 관리 클래스에 상속받는다.
class UserManager(BaseUserManager):
    """
    커스텀 유저 매니저
    """

오버라이딩 메서드

  • create_user
    일반 사용자를 생성하고 저장하는 메서드
    필수 필드를 인자값으로 지정한다.

  • create_superuser
    관리자 사용자를 생성하고 저장하는 메서드

관리자 모델 생성시, 비밀번호, 닉네임 검증 절차를 커스텀 하였으며
UserModel에서는 is_active를 기본값을 False로 설정하여
관리자 계정 생성시 is_admin을 True로 설정할 필요가 있다.

  • 관리자 계정 생성 메서드
    def create_superuser(self, email, nickname, password=None):
        """관리자 계정 생성 커스텀"""
        user = self.create_user(
            email,
            password=password,
            nickname=nickname,
        )
        user.is_admin = True
        user.is_active = True
        user.save(using=self._db)
        return user

터미널 명령어로 관리자 계정 생성시
is_admin과 is_active를 True로 설정하도록 로직을 구현했다.

  • 오브젝트 생성 메서드
    def create_user(self, email, nickname, password=None):
        """관리자 계정 생성"""
        validated_result = ValidatedData.validated_user_data(email=email, nickname=nickname, password=password)
        if validated_result is not True:
            raise ValueError(validated_result[1])
        user = self.model(
            email=self.normalize_email(email),
            nickname=nickname,
        )
        user.set_password(password)
        user.save(using=self._db)
        return user

오브젝트 생성시 필요한 검증 절차를 커스텀 하였다.
여기서 사용된 ValidatedData 메서드는 직접 구현한 검증 모듈이므로
이메일과 비밀번호, 닉네임 데이터의 유효성이 올바른지 검증하는 용도로 사용 된다.

  • ValidatedData.validated_user_data
    커스텀 벨리데이션
    직접 작성한 데이터 유효성 검증 모듈
    ValidatedData클래스에서 추상 클래스로 정의된
    validated_user_data를 호출하였다.
    @classmethod
    def validated_user_data(cls, **kwargs):
        if not cls.validated_email(kwargs.get('email')):
            return [False, '이메일 정보가 올바르지 않습니다.']
        elif not cls.validated_nickname(kwargs.get('nickname')):
            return [False, '닉네임 정보가 올바르지 않습니다.']
        elif not cls.validated_password(kwargs.get('password')):
            return [False, '비밀번호가 올바르지 않습니다.']
        return True
  • 데이터의 유효성을 검증하는 과정은 위에 제시한 링크를 확인하자
  • 위 코드는 유효성에 대한 결과 처리를 하는 곳으로 작성하였다.
    각각의 검증 데이터에 따라 유효성 검사가 실패할 경우
    False와 특정한 메시지를 전송하게 구현하였고.
validated_result = ValidatedData.validated_user_data(email=email, nickname=nickname, password=password)
	if validated_result is not True:
		raise ValueError(validated_result[1])
  • validated_result 라는 변수에 반환값을 저장한다.
  • 분기문을 통하여 True값이 아닐경우 ValuError를 반환하고
    변수에 저장된 메시지를 출력한다.

전체 코드

class UserManager(BaseUserManager):
    """
    커스텀 유저 매니저
    """

    def create_user(self, email, nickname, password=None):
        """관리자 계정 생성"""

        validated_result = ValidatedData.validated_user_data(email=email, nickname=nickname, password=password)
        if validated_result is not True:
            raise ValueError(validated_result[1])
        user = self.model(
            email=self.normalize_email(email),
            nickname=nickname,
        )
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(self, email, nickname, password=None):
        """관리자 계정 생성 커스텀"""
        user = self.create_user(
            email,
            password=password,
            nickname=nickname,
        )
        user.is_admin = True
        user.is_active = True
        user.save(using=self._db)
        return user
profile
더 노력하겠습니다

0개의 댓글