이번 글의 주제는 model입니다.
일단은 저번 글에서 ERD, 테이블 명세서, API 명세서 작업을 하였고 추가로 프론트분들과 함께 요구사항 정의서 작업을 했습니다.(API 명세서는 추가 수정이 필요 하지만...)
Model생성의 경우 ERD와 테이블 명세서를 토대로 만들면 끝입니다.
기간상의 문제로 Ask를 제외한 다른 작업들을 진행하였고" Ask는 시간이 된다면 진행하는 방식으로 프론트와 백엔드 서로 회의 후 결정하였습니다.(결론 = Ask는 Model생성을 진행하지 않았습니다.)
Django Form 모델은 설문 조사나 양식을 관리하기 위한 핵심 구조를 정의합니다. 이 모델은 사용자 정의 설문지를 생성하고 관리하는 데 필요한 다양한 필드와 관계를 포함하고 있습니다. 각 필드에 대해 자세히 살펴보겠습니다.
user: CustomUser 모델과의 외래 키 관계를 설정합니다. 이는 각 Form이 특정 사용자에 의해 생성되었음을 나타냅니다.
title: 설문지의 제목을 저장하는 CharField입니다. 최대 255자까지 입력 가능합니다.
tag: 설문지를 분류하거나 검색하기 쉽게 만드는 태그 정보를 저장합니다.
create_at: 설문지 생성 날짜를 자동으로 저장하는 DateField입니다. auto_now_add=True 옵션으로 생성 시 자동 설정됩니다.
end_at: 설문 종료 날짜를 지정하는 DateField입니다.
is_closed: 설문지의 상태를 나타내는 CharField입니다. 'TEMP', 'OPEN', 'CLOSED' 중 하나의 값을 가집니다.
access_code: 비공개 설문시 사용할 수 있는 입장 코드를 저장합니다. null과 blank가 True로 설정되어 선택적 필드입니다.
subtitle: 부제목을 저장하는 CharField입니다.
form_description: 설문지에 대한 상세 설명을 저장합니다.
uuid: 고유 식별자로 사용되는 UUIDField입니다. default로 uuid4를 사용하여 자동 생성됩니다.
target_count: 목표 응답자 수를 저장하는 IntegerField입니다. 기본값은 0입니다.
is_private: 설문지의 공개/비공개 여부를 나타내는 BooleanField입니다. 기본값은 False(공개)입니다.
is_bookmark: 즐겨찾기 여부를 나타내는 BooleanField입니다. 기본값은 False입니다.
db_table: 'form'으로 설정되어 있어, 데이터베이스에서 이 테이블 이름으로 저장됩니다.
유연한 상태 관리: STATUS_CHOICES를 통해 설문지의 상태를 효과적으로 관리할 수 있습니다.
보안성: uuid 필드를 사용하여 고유하고 예측 불가능한 식별자를 제공합니다.
사용자 정의 가능: is_private, is_bookmark 등의 필드를 통해 다양한 기능을 구현할 수 있습니다.
관계 설정: ForeignKey를 사용하여 사용자와의 관계를 명확히 정의합니다.
데이터 무결성: CharField의 max_length 설정, DateField의 자동 생성 등을 통해 데이터 무결성을 유지합니다.
class Form(models.Model):
STATUS_CHOICES = [ #상태
('TEMP', 'TEMP'),
('OPEN', 'OPEN'),
('CLOSED', 'CLOSED'),
]
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
title = models.CharField(max_length=255, verbose_name="제목")
tag = models.CharField(max_length=255, verbose_name="태그")
create_at = models.DateField(auto_now_add=True)
end_at = models.DateField()
is_closed = models.CharField(choices=STATUS_CHOICES,max_length=20, help_text="상태") #ENUM값으로 변경해야함
access_code = models.CharField(max_length=255, verbose_name="입장코드", null=True, blank=True)
subtitle = models.CharField(max_length=255, verbose_name="부 제목")
form_description = models.CharField(max_length=255, help_text="상세 내용")
uuid = models.UUIDField(default=uuid_lib.uuid4, unique=True)
target_count = models.IntegerField(default=0)
is_private = models.BooleanField(default=False) # 기본 값 false = 공개
is_bookmark = models.BooleanField(default=False) # 기본 값 false = 즐겨찾기 X인 상태
def __str__(self):
return self.title
class Meta:
db_table = 'form'
# Form참여 인원
class Respondent(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
form = models.ForeignKey(Form, on_delete=models.CASCADE)
is_complete = models.BooleanField(default=False)
class Meta:
db_table = 'respondent'
# 질문
class Questions(models.Model):
LAYOUT_CHOICES = [
('SHORT_TYPE', 'SHORT_TYPE'),
('LONG_TYPE', 'LONG_TYPE'),
('CHECKBOX_TYPE', 'CHECKBOX_TYPE'),
('RADIO_TYPE', 'RADIO_TYPE'),
('DROPDOWN_TYPE', 'DROPDOWN_TYPE'),
('RANGE_TYPE', 'RANGE_TYPE'),
('STAR_RATING_TYPE', 'STAR_RATING_TYPE'),
('IMAGE_SELECT_TYPE', 'IMAGE_SELECT_TYPE'),
('NUMBER_TYPE', 'NUMBER_TYPE'),
('DATE_TYPE', 'DATE_TYPE'),
('EMAIL_TYPE', 'EMAIL_TYPE'),
('FILE_UPLOAD_TYPE', 'FILE_UPLOAD_TYPE')
]
form = models.ForeignKey(Form, on_delete=models.CASCADE)
layout_type = models.CharField(choices=LAYOUT_CHOICES, max_length=255)
question = models.TextField()
question_order = models.IntegerField()
is_required = models.BooleanField(default=False)
class Meta:
db_table = 'questions'
# 보기
class OptionsOfQuestions(models.Model):
question = models.ForeignKey(Questions, on_delete=models.CASCADE)
# 아래 예시와 같이 받기 위해 수정
option_number = models.CharField(max_length=2, default=0)
option_context = models.CharField(max_length=255)
# [
# {
# 'option_number': 1, 'option_context': 'asdflkjsadfj',
# 'option_number': 2, 'option_context': 'asdflkjsadfj',
# 'option_number': 3, 'option_context': 'asdflkjsadfj',
# 'option_number': 4, 'option_context': 'asdflkjsadfj',
# }
# ]
class Meta:
db_table = 'options_of_questions'
# 객관식
class MultipleAnswers(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
options_of_question = models.ForeignKey(OptionsOfQuestions, on_delete=models.CASCADE)
class Meta:
db_table = 'multiple_answers'
# 주관식
class SubjectiveAnswers(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
question = models.ForeignKey(Questions, on_delete=models.CASCADE)
response = models.TextField()
class Meta:
db_table = 'subjective_answers'
# 객관식 통계
class Statistics(models.Model):
options_of_question = models.ForeignKey(OptionsOfQuestions, on_delete=models.CASCADE)
count = models.IntegerField()
class Meta:
db_table = 'statistics'
'CustomUserManager'는 'BaseUserManager'를 상속받아 사용자 생성 로직을 커스터마이즈했습니다.```
class CustomUserManager(BaseUserManager):
def create_user(self, email, password, extra_fields):
if not email:
raise ValueError("이메일은 필수 입력 사항입니다.")
email = self.normalize_email(email)
user = self.model(email=email, extra_fields)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, password, **extra_fields):
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', True)
extra_fields.setdefault('is_active', True)
return self.create_user(email, password, **extra_fields)
'CustomUser' model은 'AbstractUser'를 상속받고 기본 User 모델을 확장하였습니다.
'AbstractUser'에 django가 기본적으로 제공하는 field에서 저희 팀이 사용하지 않는 필드가 많았기에 사용하지 않는 필드는 입력하지 않거나 None값을 지정해주고 추가 필드를 작성했었습니다.```
class CustomUser(AbstractUser):
username = models.CharField(max_length=150, null=True, blank=True)
email = models.EmailField(unique=True, verbose_name="이메일")
first_name = None
last_name = None
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
date_joined = models.DateTimeField(default=timezone.now)
name = models.CharField(max_length=100, blank=True, verbose_name="이름")
profile = models.CharField(max_length=255, blank=True, verbose_name="프로필")
age = models.IntegerField(blank=True, null=True, verbose_name="나이")
refresh_token = models.CharField(max_length=255, blank=True, verbose_name="리프레시 토큰")
uuid = models.UUIDField(unique=True, default=uuid_lib.uuid4, editable=False, verbose_name="UUID")
objects = CustomUserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
class Meta:
verbose_name = "사용자"
verbose_name_plural = "사용자들"
def __str__(self):
return self.email
기본 필드 수정
* 'username': 선택적 필드로 변경 * 'email': 고유 식별자로 설정 * 'first_name', 'last_name': None값으로 지정해서 제거
추가필드
* 'name': 사용자 전체 이름 * 'profile': 사용자 프로필 정보 * 'age': 사용자 나이 * 'refresh_token': JWT인증을 위한 리플레시 토큰 * 'uuid': 고유 식별자로 UUID 사용
인증 설정
* 'USERNAME_FIELD': 'email' = 이메일을 인증 식별자로 사용 * 'REQUIRED_FIELD': [] = 추가 필수 필드 없음
Meta class
* 모델의 복수형 이름 설정
문자열 표현
* 사용자의 이메일 반환
Model을 생성하는 과정에서 이미 알고 있던 내용도 있었고, 새롭게 알게 된 내용도 있었는데 처음에는 '모델 정도는 이제 껌이지!' 하고 생각했다가 팀원들과 상의를 하면서 하다보니 팀원들에게서 여러가지로 많은 것을 배우게 되었습니다.
확실히 혼자서 진행하는 것 보다는 팀 단위로 같이 프로젝트를 진행하는 게 공부도 되는 좋은 시간이였습니다.
앞으로도 갈등 없이 이렇게 사이좋게 프로젝트를 마무리했으면 좋겠습니다. 홀홀