본문 바로가기

Django

Django - models.py

장고 내에 app을 만들면 models.py, views.py 등이 들어있는 app 폴더가 생긴다.

Web 2.0 기준이라면 이 app을 기능마다 만들겠지만 3.0은 entity를 기준으로 한다. 데이터의 성격을 보고 이게 상품 관련인지, 사용자에 관련된 건지에 따라서 묶인 단위가 entity이다.

 

models.py에 쓰는 class는 곧 DB의 table이다.

class 안에 <column명> = models.<field method> 형태로 column을 넣는다.

 

field로 줄 수 있는 것들 중 하나가 ForeignKey인데, column명으로 준 거에 장고가 자동으로 _id를 붙인다. 그리고 참조할 테이블의 class와 참조했던 게 사라졌을 때 어떻게 처리할 건지를 attribute로 줘야 된다. 참조할 테이블의 class명은 따옴표를 붙여 string으로 주는 게 좋다. 따옴표 없이 그냥 쓰면 class 선언 순서에 영향을 받는다. string으로 주면 코드를 다 읽고 나서 메모리 상에서 일치하는 걸 맵핑한다.

 

참조했던 게 사라지면 이 테이블에 있는 연관 데이터도 삭제 -> on_delete=models.CASCADE

참조했던 게 사라지면 이 테이블에서 해당 값을 null로 변경 -> on_delete=models.SET_NULL, null=True

null을 허용하지 않는게 DB의 기본 성질이라 null을 넣으려면 null=True로 허용해줘야 된다.

 

one to one -> models.OneToOneField(<참조할 테이블의 class명>, on_delete=<옵션>)

many to many -> 중간 테이블 class를 만들어서 거기에 두개 테이블을 참조하는 foreign key 2개를 만든다.

 

이대로 만들면 테이블의 이름이 <app 이름>_<class 이름> 이렇게 영어 소문자로 된다.

테이블명을 지정하려면 각 class 안에 아래와 같은 코드를 넣어줘야 된다.

class Meta:
    db_table = '<테이블명>'

 

스타벅스 데이터 모델링했던 걸 기반으로 만든 models.py는 아래와 같다.

from django.db import models
from django.db.models.deletion import CASCADE

class Menu(models.Model):
    name = models.CharField(max_length=5)

    class Meta:
        db_table = 'menus'


class Category(models.Model):
    name = models.CharField(max_length=10)
    menu = models.ForeignKey('Menu', on_delete=models.CASCADE)

    class Meta:
        db_table = 'categories'


class Drink(models.Model):
    name = models.CharField(max_length=20)
    category = models.ForeignKey('Category', on_delete=models.CASCADE)
    english_name = models.CharField(max_length=50)
    description = models.CharField(max_length=100, null=True)
    is_new = models.BooleanField()

    class Meta:
        db_table = 'drinks'


class Image(models.Model):
    url = models.CharField(max_length=1000)
    drink = models.ForeignKey('Drink', on_delete=models.CASCADE)

    class Meta:
        db_table = 'images'


class Nutrition(models.Model):
    drink = models.ForeignKey('Drink', on_delete=models.CASCADE)
    calories = models.CharField(max_length=10)
    saturated_fat = models.CharField(max_length=10)
    protein = models.CharField(max_length=10)
    sodium = models.CharField(max_length=10)
    total_carbs = models.CharField(max_length=10)
    caffeine = models.CharField(max_length=10)

    class Meta:
        db_table = 'nutritions'


class Allergy(models.Model):
    allergen = models.CharField(max_length=10)

    class Meta:
        db_table = 'allergies'


class DrinkAllergy(models.Model):
    drink = models.ForeignKey('Drink', on_delete=models.CASCADE)
    allergy = models.ForeignKey('Allergy', on_delete=models.DO_NOTHING)

    class Meta:
        db_table = 'drinks_allergies'

 

models.py를 저장하고 나서

makemigrations 해서 변경 사항을 확인 한 뒤 migration 파일을 만들고

migrate하면 migration 파일을 DB에 반영한다.

 

명령을 실행할 때 뒤에 app명을 붙이면 해당 app에 대해서만 실행한다. 아무것도 주지 않으면 INSTALLED_APPS 전부를 훑는다.

migrate 할 때 특정 마이그레이션 파일만 지정해 그것만 적용할 수도 있다.

$ python manage.py makemigrations    # 마이그레이션 파일 만들기

$ python manage.py showmigrations    # 마이그레이션 파일, 반영 상태 확인

$ python manage.py migrate    # 마이그레이션 파일 반영

$ python manage.py sqlmigrate <app명> <마이그레이션 파일 번호>    # raw query로는 어떻게 보이는지 알려줌

 

반영된 migration 상태와 mysql에 있는 걸 일치시키는 게 중요하다. dbshell에서는 직접 건드리지 말고,

$ python manage.py shell

이 장고 쉘에서 CRUD 하는 편이 안전하다. 마이그레이션 파일을 함부로 편집하거나 삭제하면 에러가 더 생길 수도 있다. 만일 migration을 초기화할 일이 생긴다면 혹시 모를 상황에 대비해 DB를 백업해두고, 참고 링크를 보며 조심스레 접근하자.

 

DB를 확인하려면,

$ python manage.py dbshell

또는 mysql에 로그인한다.

$ mysql -u root -p    # 로그인

mysql > show databases;

mysql > use <DB명>;