이번 실습에서 집중한 것은 데이터 테이블 생성, Foreign key 사용하기, 데이터 신규 입력, 추가, 삭제다. 먼저 아래처럼 Aquery로 모델링했다. models.py에서 어떤 클래스를 생성할지, 어느 테이블을 Foreign key로 연결할지 구조를 잡아둔 뒤 코드를 작성하면 어떤 클래스에 어떤 필드명으로 만들어야 할지 훨씬 명확해서 시행착오를 줄여준다.
데이터 테이블 구조 짜기, Foreign key 사용하기
해당하는 앱의 models.py 파일에서 데이터 테이블 이름, 열(필드) 이름, 필드값이 될 데이터 타입과 속성 등을 정의해준다.
class Menu(models.Model):
name = models.CharField(max_length=45)
# name이라는 이름으로 열을 만드는데, 열에 들어갈 데이터는 문자열 타입, 최대 길이는 영문자 기준 45자라는 뜻이다.
class Meta:
db_table = 'menu'
# 데이터 테이블 이름을 지정한다. 따로 정해주지 않으면 장고에서 '앱이름_클래스이름'으로 만든다.
class Category(models.Model):
menu = models.ForeignKey(Menu, on_delete=models.CASCADE)
# menu열은 Menu라는 클래스에서 생성한 menu 테이블 데이터를 끌어다 쓴다.
# 모든 데이터는 고유 id값을 가지고 있기 때문에 그것을 기준으로 데이터를 매칭해준다.
name = models.CharField(max_length=45)
class Meta:
db_table = 'category'
다대다 관계인 drink 테이블과 allergy 테이블은 중간 테이블인 allergy_drink 테이블로 이어준다.
class Drink(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE)
name = models.CharField(max_length=45)
nutrition = models.ForeignKey(Nutrition, on_delete=models.SET_NULL, null=True)
size = models.ForeignKey(Size, on_delete=models.SET_NULL, null=True)
class Meta:
db_table = 'drink'
class Allergy(models.Model):
name = models.CharField(max_length=45)
class Meta:
db_table = 'allergy'
class AllergyDrink(models.Model):
allergy = models.ForeignKey(Allergy, on_delete=models.CASCADE)
drink = models.ForeignKey(Drink, on_delete=models.CASCADE)
class Meta:
db_table = 'allergy_drink'
데이터 테이블 생성하기
manage.py 파일을 이용할 것이므로 manage.py 파일이 있는 위치에서 실행해준다.
# 변경 사항 감지해서 migrations 디렉토리 내에 initial 파일 생성
python manage.py makemigrations
# makemigtaions를 통해 감지한 변경 사항을 반영해 DB에 테이블을 새로 생성하거나 테이블 이름, 열 이름 등을 변경함
python manage.py migrate
데이터 신규 입력
장고에 기본으로 내장된 python shell을 사용했다. 데이터를 밀어넣기 위해 models.py 파일 안 클래스들을 최초 1회 import 해야한다.
from drinks.models import Menu, Category, 기타 클래스들 추가추가
# menu 테이블에 '음료' 데이터 입력. 이 테이블에 첫 번째로 입력된 데이터이므로 id값은 1이 된다.
>>> Menu.objects.create(name='음료')
# category 테이블의 menu 열 항목은 menu 테이블을 참조하므로 Foreign key로 끌어온다.
# '음료' 메뉴 안 '콜드 브루' 카테고리이므로 menu 테이블에서 id값이 1인 객체를 가져와서 menu 열에 할당해준다.
>>> Category.objects.create(menu=Menu.objects.filter(id=1)[0], name='콜드 브루')
입력한 데이터 확인
입력한 데이터는 장고 내장 DB인 sqlite3를 이용했다. 표 형태로 보기 위해서 사전에 아래 명령어를 입력해줬다.
.headers on
.mode column
# migrate로 생성한 모든 테이블 목록
.tables
# 결과값
django_admin_log django_content_type
auth_group django_migrations
auth_group_permissions django_session
auth_permission auth_user
auth_user_groups auth_user_user_permissions
menu category
# menu 테이블 안 모든 데이터 호출
select * from menu;
# 결과값
id name
---------- ----------
1 음료
# category 테이블 안 모든 데이터 호출
select * from category;
# 결과값
id name menu_id
---------- ---------- ----------
1 콜드 브루 1
기존 데이터 행에 새로운 데이터 추가하기
drink 테이블 열에 fk 값을 추가해 size 테이블 정보를 끌어오려고 한다. 우선 models.py 파일에서 Drink 클래스를 수정한다. 참조할 테이블은 참조하는 테이블보다 반드시 위에 있어야 한다. 그 반대라면 fk로 끌어올 값이 없으므로 에러가 발생한다.
# size 테이블 추가
class Size(models.Model):
name = models.CharField(max_length=45)
size_ml = models.IntegerField(default=0)
size_oz = models.IntegerField(default=0)
class Meta:
db_table = 'size'
# 기존 열 category, name, nutrition에 size 열 추가
class Drink(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE)
name = models.CharField(max_length=45)
nutrition = models.ForeignKey(Nutrition, on_delete=models.SET_NULL, null=True)
size = models.ForeignKey(Size, on_delete=models.SET_NULL, null=True)
class Meta:
db_table = 'drink'
# drink 테이블에서 id값이 1인 객체 불러오기
>>> drink1 = Drink.objects.get(id=1)
# drink 테이블의 size열에 size 테이블에서 가져온 데이터 추가하기
>>> drink1.size = Size.objects.filter(id=1)[0]
# 테이블에 데이터 저장
>>> drink1.save()
저장한 데이터를 다시 sqlite3에서 확인해보면 이렇게 출력된다.
select * from drink;
# 결과값
id name category_id size_id nutrition_id
---------- ----------- ----------- ---------- ------------
1 나이트로 바닐라 크림 1 1 1
참고로 size 테이블의 데이터는 아래와 같이 생성했다.
select * from size;
id name size_ml size_oz
---------- ---------- ---------- ----------
1 Tall(톨) 355 12
2 Grande(그란데 473 16
이미 저장한 데이터 삭제하기
filter 명령어를 통해 객체를 특정한 뒤 삭제해준다. 만약 size 테이블에서 그란데 사이즈 데이터를 삭제하고 싶다면, id값이 2인 객체를 찾아 삭제하거나 name값이 ‘Grande(그란데)‘인 객체를 찾아 삭제하면 된다.
>>> Size.objects.filter(id=2).delete()
# 또는
>>> Size.objects.filter(name='Grande(그란데)').delete()
특정 필드값만 삭제하기
null값을 허용한 필드에서만 삭제가 가능하다.
# drink 테이블에서 id값이 1인 데이터의 size 값만 삭제
Drink.objects.filter(id=1).update(size=None)
# 결과값
id name category_id size_id nutrition_id
---------- ----------- ----------- ---------- ------------
1 나이트로 바닐라 크림 1 1
2 나이트로 쇼콜라 클라 1 1 2
3 아이스 커피 2 1 3
번외 - 삭제한 열과 동일한 id값으로 다시 데이터 저장하기
size 테이블의 첫 번째 열(id값 1)을 삭제한 뒤 다시 id값이 1인 데이터를 입력하는 방법이다. 위에서 썼던 filter
와 update
로 입력하려고 했으나 id=1
인 데이터 자체가 없으므로 필터에서 걸리지 않아 실패. 아예 size 테이블에 id=1
인 열을 만들어주고 데이터를 밀어넣었다.
# size 테이블에서 id값이 1인 열 전체 삭제
>>> Size.objects.filter(id=1).delete()
# 사이즈 테이블에 id값이 1인 열 생성
>>> Size().id = 1
>>> Size().save()
# 사이즈 테이블에서 id값이 1인 열에 데이터 저장
>>> Size.objects.filter(id=1).update(name='Tall(톨)', size_ml=355, size_oz=12)
어려웠던 점
Aquery로 구조를 미리 짜놓은 덕분에 models.py에서 클래스 생성하는 것은 어렵지 않았다. 다만 데이터를 신규로 입력할 때 fk 부분에서 많이 헤맸다. Query set 형태 말고 객체 형태로 넣어줘야 한다는 것을 수 번 시행착오를 겪은 뒤에 깨달았다.