TIL31 - Starbucks with Django(3/3)

Kiyong Lee·2021년 9월 12일
0

Django

목록 보기
7/35

Starbucks with Django

2편에 이어.. 이제 마지막 챕터이다.

데이터 CRUD 작업을 할건데, Shell을 켜도록 하자


Shell

터미널 환경에서 OS와 유저 사이를 이어주는 역할을 하는 프로그램이다.

1. Data Insert


1-1. 참조를 이용하지 않은 INSERT

참조를 이용하지 않는다는 것은, 외부 테이블의 pk값을 가져오는 게 없이
값을 저장한다는 의미이다.

해당 테이블 : menus, allergy

우선 models.py에서 데이터베이스에 접근하기 때문에,
해당 파일을 import 해준다.

그리고 우선 가장 상위 카테고리인 메뉴만 만들 예정이다.

from products.models import Menu, Allergy

#Menu, allergy 테이블 조회
Menu.obejects.all()
Allergy.objects.all()

#빈 쿼리 셋 출력
QuerySet[]

#Menu 데이터 입력
#스타벅스의 홈페이지가면 '음료', '보도자료', '푸드', '상품', '카드'가 있다.
Menu.objects.create(name='음료')
Menu.objects.create(name='보도자료')
Menu.objects.create(name='푸드'
Menu.objects.create(name='상품')
Menu.objects.create(name='카드')

#Menu 테이블 조회 use SQL
select * from menus;

#Menu 테이블 조회 use ORM
Menu.objects.all()

#쿼리셋 출력(괄호안의 숫자는 id값이다)
<QuerySet [<Menu: Menu object (1)>, <Menu: Menu object (2)>, <Menu: Menu object (3)>, <Menu: Menu object (4)>, <Menu: Menu object (5)>]>


1-2. 참조를 이용하는 INSERT

메뉴와 카테고리를 보면,
메뉴에서 음료를 눌러야 음료 카테고리가 나온다

그러므로 카테고리는 메뉴의 PK값을 참조하고 있다.

그리고 음료와 카테고리를 OneToMany로 설정했는데,
메뉴를 '음료'로 선택하면 선택할 수 있는 카테고리는 10개

그러면 카테고리 테이블의 menu_id는 메뉴 테이블에서 불러와야 한다.

이럴 때, 메뉴의 키값이자 카테고리 테이블의 참조키를 변수에 넣어줘서 가져온다.

#귀찮아서 그냥 다 import
from products.models import *

#메뉴에서 음료의 키값을 변수에 입력하기 위한 과정
m_id = Menu.objects.get(id=1)
m_id.save()	
m_id

#결과
<Menu: Menu object (1)>


#카테고리에 Insert / 괄호안은 실제 DB의 컬럼명을 넣어준다
Category.objects.create(menu_id = m_id.id, name='콜드브루커피')
Category.objects.create(menu_id = m_id.id, name='브루드커피')
Category.objects.create(menu_id = m_id.id, name='에스프레스')
Category.objects.create(menu_id = m_id.id, name='푸라푸치노')
Category.objects.create(menu_id = m_id.id, name='블렌디드')

menu_id = 메뉴 중 음료의 키 값이므로 이렇게 참조되어 들어가진다

이렇게 해서 나머지 테이블에 값을 넣어준다


2. Data select

데이터를 조회하기 위한 QuerySet은 어떤 게 있을까?


2-1. all()

>> Product.objects.all()

조회 시, id가 1~5인 5개의 데이터가 있음을 알 수 있다.

이건 쿼리 조회 결과


2-2. get()

이 데이터 중 내가 얻고 싶은 특정 데이터를 지목할 때 사용한다.

>> Product.object.get(id=2)

get()의 가장 큰 특징은 데이터를 단 하나의 행만 반환한다는 것!

get(id=2)나 get(kor_name__contains='나이트')를 했을 때

get() returned more than one Product -- it returned 3!

당신이 사용한 get() 메소드의 결과는 1개 이상의 값이 리턴되었다라고 말한 뒤,
몇 개 리턴하는지까지 친절히 써주는 것도 볼 수 있다.


2-3. filter()

get()이 1개만을 반환하면, 이걸 보완하기 위한 것이 filter()이다.

>> Product.objects.filter(kor_name__contains='나이트')

그러면 id=1,2,5인 쿼리셋이 나오는 게 확인된다

테이블 조회 결과, 한국 이름에 '나이트'가 음료는 1,2,5번 확인


2-4. exclude()

내가 조회 할 때, 돌체콜드브루를 제외하고 찾고 싶다면?
이 때 사용하는 것이 exclude() 이다.

# 조회조건은 간편하게 id로 함(kor_name='돌체콜드브루' 이렇게 해도 됨)
>> Product.objects.exclude(id=3)

나는 처음에 이거랑 filter()랑 대조돼서 포함하지 않는 것만 나오는 줄 알았는데
그게 아니었다.


엄연히 따지면 get ≒ exclude 인 셈..

찾아보니 and, or, not 은 Q객체를 사용한다고 하는데 그 때 배울듯;


2-5. values()

객체의 값을 볼 때 사용한다.

보다시피 형태는 딕셔너리이며 전체 값을 볼 수도 있고, 필터링도 가능

# 전체 값 추출
>> Product.objects.values()

# 전체 값 중 필터링
>> Product.objects.values().filter(id_lt=3)

values()를 통해 전체 값 보기


전체 값 중 원하는 거 필터링


2-6. values_list()

values() 함수와 달리 튜플로 값을 반환한다.

#전체 데이터 추출
>> Product.objects.values_list()

#전체 데이터 중 id<3인것만 추출
>> Product.objects.values_list()

#전체 데이터 중 한글이름이 '나이트'가 들어가는 음료의 한글명만 추출
>> Product.objects.filter(kor_name__contais('나이트').values_list('kor_name')


2-7. 반복문을 이용한 데이터 출력

>> for category in Category.objects.filter(menu__name='음료') :
....    print(category.name)
....

마지막에 엔터를 한 번 더 눌러야 실행된다;;


menu__name에 대해 추가 설명

이전에 쿼리셋을 작성할 때는 아래의 테이블 필드명을 이용해서 사용했었다.

그런데 해당 반복문은 파이썬의 문법이므로,
파이썬 models.py에서 작성한 걸 기준으로 해야한다.

menu는 Menu 클래스의 pk를 참조하기 때문에,
메뉴 테이블에서 id가 n인 것의 이름을 가져오게 되는 것이다.


3. Data Update

음료 데이터에 5개가 있는데, 돌체콜드브루에 대한 변경사항이 생겼다

이름 : 돌체콜드브루 -> 따뜻한콜드브루
설명 : 무더운 여름철 -> 신규 메뉴로 재런칭되었습니다

#이름과 설명 변경
>> Product.objects.filter(name__contains='돌체').update(kor_name='따뜻한콜드브루', desc='신규메뉴로 재런칭되었습니다')
>> 1 #변경된 개수가 아웃풋으로 나옴

그리고 메뉴에 대한 정보가 바뀜에 따라,
영양정보 또한 일부 조정될 예정이다.


4. Data Delete

바뀐 따뜻한콜드브루의 영양정보 값은 3번이었다.

그런데, 담당자의 실수로 3번을 바꾸면 되는 걸 3번을 삭제하고 6번으로 변경해버렸다.

이 때 담당자가 한 실수 과정은 다음과 같다.

1. 영양정보 테이블에 신규 행 삽입(id=6)

  1. 해당 테이블에서 3번 삭제
>> Nut.objects.filter(id=3).delete()
(3, {'products.allergyProducts': 1, 'products.Product': 1, 'products.Nut': 1})

그러면 음료/알러지/영양정보에서 한 개씩 삭제되어 3개가 삭제됐다고 나온다.

왜 그런가 봤더니

class Nut(models.Model) :
    one_serving_kcal = models.DecimalField(max_digits=6,decimal_places=2)
    sodium_mg = models.DecimalField(max_digits=6,decimal_places=2)
    saturated_fat_g = models.DecimalField(max_digits=6,decimal_places=2)
    sugars_g = models.DecimalField(max_digits=6,decimal_places=2)
    protein_g = models.DecimalField(max_digits=6,decimal_places=2)
    caffeine_mg = models.DecimalField(max_digits=6,decimal_places=2)
    size_ml = models.CharField(max_length=20)
    size_fluid_ounce = models.CharField(max_length=20)
    

class Product(models.Model) :
    category = models.ForeignKey('Category', on_delete=models.CASCADE)
    kor_name = models.CharField(max_length=20)
    eng_name = models.CharField(max_length=20)
    desc = models.TextField()
    nutr = models.OneToOneField('Nut', on_delete=models.CASCADE)
    


class allergyProducts(models.Model) :
    allergy = models.OneToOneField('Allergy', on_delete=models.CASCADE)
    product = models.OneToOneField('Product', on_delete=models.CASCADE)

영양정보의 PK값을 음료에서 1대1로 참조하여,
영양정보가 삭제되면 음료의 영양정보가 삭제되는데,

그랬더니 음료의 그 행이 삭제가 됐다.

그런데 음료의 PK값을 알러지가 가져오니 얘도 삭제됨;;

이 세개의 특징이 모두 1대1 관계로 이루어졌다는 점이다.

그러면 수정할 값을 미리 수정해주고 그 때 없애야 한다는 걸 깨달았다

그래서 음료와 영양정보에 한 개씩 추가했고,

다음과 같이 작업을 해볼 예정이다.

1. id=6인 음료의 nutr_id = 7로 변경
2. nutrtions에서 id=6 인 것 삭제

get()으로 수정하면 에러가 나고, filter()로 수정하면 된다.

이게 왜 그런가 찾아보니

get()으로 받아와서 업데이트 하는 건, pk가 단일컬럼으로 잡혀있는경우만 가능합니다

라고 하는데, 이게 무슨 말이냐면

products 테이블은 얼핏 보면, PK가 id하나 같지만 nutr_id가 Unique key라서
얘도 PK범주에 들어서 수정이 안되는 것이다.

여튼 수정 후 nutrition 테이블에서 id=6인 걸 삭제한 뒤 조회해보면
products 테이블에 관계없이, nutrition에서만 삭제된 걸 알 수 있다.


4-1. 그러면 oneToMany 관계는 어떨까?

음료와 이미지는가 이 관계에 해당된다.

현재 image 테이블에는 1번 음료의 이미지가 들어가있다.

#이미지 테이블 데이터 삭제
>>> Images.objects.filter(product_id=1).delete()

그 결과 음료 테이블의 데이터만 삭제된 걸 알 수 있다.

관계 설정에 따른 데이터 변경 시, 잘 확인해봐야 겠다.

profile
ISTJ인 K-개발자

0개의 댓글