관리 메뉴

한다 공부

[Django] Model, ERD 본문

Dev/Python

[Django] Model, ERD

사과당근 2022. 8. 8. 22:37

장고는 MTV 패턴을 가지고 있다.

Model, Template, View 인데 쉽게 이야기하면

모델은 데이터, 템플릿은 화면, 뷰는 데이터 처리와 같은 로직을 담당하고 있다.

 

우선 Model을 작성해야 하는데,

이러한 데이터 구조와 데이터간의 관계를 쉽게 나타내기 위해

ERD (Entity Relationship Diagram) 를 작성해보자

 

ERDCloud

 

ERDCloud

Draw ERD with your team members. All states are shared in real time. And it's FREE. Database modeling tool.

www.erdcloud.com

여기에서 작성하였다.

ERD는 아래와 같다. 추후 수정될 여지가 있다..

(여력이 된다면 좋아요 기능, 커뮤니티 기능을 추가할 것이기 때문에)

우선 사용자는 자신이 다른 사용자를 도울 수 있는 요일과 시간대에 대한 데이터를 가지고 있어야 한다.

그리고 지역과 관련된 데이터, 도울 수 있는 능력에 관련된 데이터를 가지고 있어야 한다.

이러한 데이터는 별개의 모델로 추출해 냈으며, User와는 1:1 관계를 가지고 있고,

Detail을 저장할 모델과는 1:n 관계를 가지고 있다.

 

사용자에 관련된 User 모델에는, 개개인의 봉사시간과 누적포인트, 개인정보 그리고 sms 수신 여부를 담았다.

 

Post는, 도움을 요청하기 위해 작성하는 게시글에 대한 모델이다.

제목, 내용, 도움에 걸리는 예상 소요시간, 도움 해결 여부, 도움 요청 날짜, 도움이 필요한 날짜-시간-지역, 도움과 관련된 능력에 대해 저장해야 한다.

Day, Address, Ability와 Post 모델을 처음에는 연결을 시켰는데

개발을 하다보니, 연결되었을 때의 문제점들이 발생하여 결국 Post 내 별개의 필드로 두었다.

 

Django 프로젝트 내에서 Models.py를 작성해보았다.

우선 아래는 사용자 User 모델이다.

장고에서 제공해주는 User 모델로는 담고싶은 정보를 다 저장할 수가 없어서 커스터마이징을 했다.

from django.db import models
from django.contrib.auth.models import (AbstractBaseUser, BaseUserManager, PermissionsMixin)
from django.core.validators import MinValueValidator, MaxValueValidator


class UserManager(BaseUserManager):
    use_in_migrations = True

    def create_user(self, username, nickname, phone, password, email=None):
        if not username:
            raise ValueError('아이디는 필수 항목 입니다.')
        if not nickname:
            raise ValueError('이름은 필수 항목 입니다.')
        if not phone:
            raise ValueError('전화번호는 필수 항목 입니다.')
        if not password:
            raise ValueError('비밀번호는 필수 항목 입니다.')

        user = self.model(
            username=username,
            nickname=nickname,
            email=self.normalize_email(email),
            phone=phone
        )
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(self, username, nickname, email=None, phone=None, password=None):
        user = self.create_user(
            username=username,
            nickname=nickname,
            email=self.normalize_email(email),
            phone=phone,
            password=password
        )
        user.is_admin = True
        user.is_superuser = True
        user.is_staff = True
        user.save(using=self._db)
        return user


class User(AbstractBaseUser, PermissionsMixin):

    objects = UserManager()

    username = models.CharField(max_length=20, unique=True)
    nickname = models.CharField(max_length=20)
    email = models.EmailField(max_length=128, null=True)
    phone = models.CharField(max_length=11, unique=True)
    alarm = models.BooleanField(default=False)
    hours = models.IntegerField(default=0)
    point = models.IntegerField(default=0)

    is_admin = models.BooleanField(default=False)
    is_superuser = models.BooleanField(default=False)
    is_staff = models.BooleanField(default=False)
    is_active = models.BooleanField(default=True)

    USERNAME_FIELD = 'username'
    REQUIRED_FIELDS = ['phone', 'nickname']

    def __str__(self):
        return self.username

아래는 선택지들이다.

아래와 같이 선택지들을 설정해 둔다면, dropdown 방식으로 데이터를 선택할 수 있다. choice를 통해 누군가는 거주지를 '서울' 이라고 입력하고, 누군가는 '서울특별시'라고 저장할 경우를 대비할 수 있다.

 

able_choice는 능력에 대한 큰 카테고리를 선택할 수 있는 선택지인데, 아직 정확하게 카테고리를 나누지 않아서 추후 수정할 예정이다.

CITY_CHOICE = [
    ('서울특별시', '서울특별시'),
    ('경기도', '경기도'),
    ('강원도', '강원도'),
    ('충청북도', '충청북도'),
    ('충청남도', '충청남도'),
    ('전라북도 ', '전라북도'),
    ('전라남도 ', '전라남도'),
    ('경상북도', '경상북도'),
    ('경상남도', '경상남도'),
    ('부산광역시', '부산광역시'),
    ('인천광역시', '인천광역시'),
    ('대구광역시', '대구광역시'),
    ('대전광역시 ', '대전광역시'),
    ('광주광역시 ', '광주광역시'),
    ('울산광역시', '울산광역시'),
    ('세종특별자치시', '세종특별자치시'),
    ('제주특별자치도', '제주특별자치도'),
]

# Coming Soon..
ABLE_CHOICE = [
    ('생활', '생활')
]

아래는

도움이 필요한 날짜, 시간 /

도움이 필요한 지역, 지역 디테일 /

요청한 도움과 관련된 능력, 능력 디테일 /

그리고 도움 요청 게시글 에 대한 모델이다.

특이한 점이 있다면, 시간대를 나타내고자 할 때 0~24 까지 나타내고 싶어서 validator를 통해 범위를 지정했다.

class Day(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    day_week = models.CharField(max_length=20, choices=DAY_CHOICE)

    def __str__(self):
        return '{}. {} : {}'.format(self.pk, self.user, self.day_week)


class Time(models.Model):
    day = models.ForeignKey(Day, on_delete=models.CASCADE)
    star_time = models.IntegerField(validators=[MinValueValidator(0), MaxValueValidator(24)])
    end_time = models.IntegerField(validators=[MinValueValidator(0), MaxValueValidator(24)])


class Address(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    city = models.CharField(max_length=20, choices=CITY_CHOICE)

    def __str__(self):
        return '{}. {} : {}'.format(self.pk, self.user, self.city)


class AddressDetail(models.Model):
    address = models.ForeignKey(Address, on_delete=models.CASCADE)
    si_gun_gu = models.CharField(max_length=20)
    addr_detail = models.CharField(max_length=100, null=True, blank=True)


class Ability(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    able_category = models.CharField(max_length=20, choices=ABLE_CHOICE)

    def __str__(self):
        return '{}. {} : {}'.format(self.pk, self.user, self.able_category)


class AbilityDetail(models.Model):
    ability = models.ForeignKey(Ability, on_delete=models.CASCADE)
    able_detail = models.CharField(max_length=100, null=True, blank=True)


class Post(models.Model):
    author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='author')
    helper = models.ForeignKey(User, on_delete=models.CASCADE, related_name='helper')
    title = models.CharField(max_length=100)
    content = models.CharField(max_length=1000)
    hour = models.IntegerField()  # 소요 시간
    solved_flag = models.BooleanField(default=False)
    created_date = models.DateTimeField(auto_now_add=True)

    help_day_week = models.CharField(max_length=20, choices=DAY_CHOICE)
    help_star_time = models.IntegerField(validators=[MinValueValidator(0), MaxValueValidator(24)])  # 시작 시간
    help_city = models.CharField(max_length=20, choices=CITY_CHOICE)
    help_si_gun_gu = models.CharField(max_length=20)
    help_addr_detail = models.CharField(max_length=100, null=True, blank=True)
    help_able_category = models.CharField(max_length=20, choices=ABLE_CHOICE)
    help_able_detail = models.CharField(max_length=100, null=True, blank=True)

    def __str__(self):
        return '{}. {} : {}'.format(self.pk, self.author, self.title)

 

 

장고에서는 Admin 기능을 지원해주는데 이때 모델마다 원하는 형식으로 데이터를 보여줄 수 있다. 이를 def __str__(self) : 를 통해 지정했다.

 

모델은 개발에서 정말 중요한 역할을 하지만, 기획만 했을 때에는 생각하지 못했던 실수가 개발을 하면서 발생하기 때문에 추후 모델을 수정할 가능성이 있다. 그리고 나중에 좋아요 기능과 커뮤니티 기능을 추가하고자 하는데, 이를 위한 모델을 작성해두지 않았기 때문에 추후 수정될 것이 틀림없다!

그리고 현재는 장고에서 기본으로 지원해주는 sqlite3를 사용하는데, 나중에 AWS RDS로 변경할 것이다. 이 과정에서 DB가 초기화되므로 중요한 데이터는 아직 저장해두지 않을 것이다.. ㅋㅋㅋ

 

장고에서 모델을 적용할 때는

python manage.py makemigrations
python manage.py migrate

이것을 실행시켜야 적용이 되므로 잊지말고 실행하자