[Django] Database table 생성 & C.R.U.D 실습 1 (feat. QuerySet API)

이태권 (Taekwon Lee)·2022년 6월 6일
0

Django

목록 보기
1/3
post-thumbnail

오늘 해 볼 것은
1. Django Model로 Database table 생성
2. Django Shell에서 C.R.U.D (생성, 조회, 수정, 삭제)
이다.

그 전에 C.R.U.D가 무엇인지 살펴 보고 가자

✍️ C.R.U.D

  • C.R.U.D, 대부분의 컴퓨터 소프트웨어가 갖는 기본적인 데이터 기능을 묶어서 일컫는 말이다.
  • 여기서 기본적인 데이터 기능은 Create, Read, Update, Delete (생성, 조회, 수정, 삭제)이다.
  • 위의 그림에서 View에서 Model로 가는 과정이다.

Create

원하는 데이터를 새로 생성하고자 할 때 쓰인다.

class Size(models.Model):
	name				= models.CharField(max_length=40)
	size_ml				= models.CharField(max_length=40, null=True)
	size_fluid_ounce	= models.CharField(max_length=40, null=True)

	class Meta:
		db_table = 'sizes'
Size.objects.create(name='Tall(톨)', size_ml=355, size_fluid_ounce=12)
mysql> select * from sizes;
+----+-------------------+---------+------------------+
| id | name              | size_ml | size_fluid_ounce |
+----+-------------------+---------+------------------+
|  1 | Tall(톨)          | 355     | 12               |
+----+-------------------+---------+------------------+
  • objects는 Model class를 통하여 Database에 Data를 C.R.U.D 작업을 제공하는 manager class이다.
  • create는 manager class의 method로, 해당 method가 호출되면 Database에 Query 작업을 수행한다.

즉, 파이썬 객체의 형태로 Database에 Data를 C.R.U.D하는 것이다.

Read

원하는 데이터만 찾고자 할 때 쓰인다.

Product.objects.get(id=1)				# id가 1인 것만 찾기
<Product: Product object (1)>			# 해당 결과

Product.objects.filter(allergy=1)												# product 중에서 allergy가 1(대두)인 것만 찾기
<QuerySet [<Product: Product object (1)>, <Product: Product object (12)>]>		# 해당 결과

Update

원하는 데이터를 수정하고자 할 때 쓰인다.

  • Tall(톨) 사이즈를 먼저 입력하고 Short(숏) 사이즈를 이후에 입력하여 id 순서가 Tall(톨)이 먼저 와버렸다.
mysql> select * from sizes;
+----+-------------------+---------+------------------+
| id | name              | size_ml | size_fluid_ounce |
+----+-------------------+---------+------------------+
|  1 | Tall(톨)          | 355     | 12               |
|  2 | Short(숏)         | 237     | 8                |
+----+-------------------+---------+------------------+
  • 이를 update를 사용하여 둘의 정보를 바꿔 보기 편하게 바꾸었다.
Size.objects.filter(id=1).update(name='Short(숏)', size_ml=237, size_fluid_ounce=8)
Size.objects.filter(id=2).update(name='Tall(톨)', size_ml=355, size_fluid_ounce=12)
mysql> select * from sizes;
+----+-------------------+---------+------------------+
| id | name              | size_ml | size_fluid_ounce |
+----+-------------------+---------+------------------+
|  1 | Short(숏)         | 237     | 8                |
|  2 | Tall(톨)          | 355     | 12               |
+----+-------------------+---------+------------------+

Delete

원하는 데이터를 삭제하고자 할 때 쓰인다.

  • 위의 Update 상황 대신, Delete를 사용하여 데이터를 삭제하고 다시 입력할 수도 있다.
  • 대신, 지워진 id를 덮어 씌우는 것이 아니라 다음 번호로 저장된다.
Size.objects.filter(id=1).delete()

🖥 실습(Django Shell)

0. 들어가기 전에

Django Shell을 편하고 깔끔하게 이용하고자 ipython을 이용하고자 한다.
일단 iptyhon을 설치해 보자.

pip install ipython

이후 database의 application 생성 한다

python manage.py startapp <만들 app의 이름> # 여기서는 products

그 다음 settings.pyapp 이름 추가

# products 디렉토리의 settings.py에 들어간다

INSTALLED_APPS = [
	...
	'products',
]

마지막으로 서버 실행(실패 시 오류 검증)

python manage.py

1. models.py에 Class로 Database Table 생성하기

models.py에서 ERD를 코드로 구현해야 한다.
구글링을 열심히 하고 저번에 다룬 스타벅스 ERD와 modeling을 참고하여
아래와 같이 models.py를 만들었다.
(자세한 건 GitHub 코드 확인)

from django.db import models

# Create your models here.
class Menu(models.Model):
	name = models.CharField(max_length=40)

	class Meta:
		db_table = 'menus'


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

	class Meta:
		db_table = 'categories'


class Product(models.Model):
	korean_name		= models.CharField(max_length=100)
	english_name	= models.CharField(max_length=100)
	description		= models.TextField(max_length=300)
	category		= models.ForeignKey('Category', on_delete=models.CASCADE)
	allergy			= models.ManyToManyField('Allergy', through='AllergyProduct')

	class Meta:
		db_table = 'products'

.
.
.

2. 생성한 Table을 migration하기

  • migration은 아래와 같이 2단계를 밟아 진행하면 된다.
  • 처음에 하든, 이후에 추가로 Table을 생성한 이후든 상관 없다.
python manage.py makemigrations
python manage.py migrate

3. Database에서 Table 확인

mysql 명령어를 이용하여 사용할 데이터베이스 westarbucks를 찾아서 세부 테이블을 확인한다.

원하는 데이터베이스 확인

mysql> show databases;
+------------------------+
| Database               |
+------------------------+
| information_schema     |
| mysql                  |
| performance_schema     |
| sys                    |
| westarbucks            |
+------------------------+

원하는 데이터베이스 사용

mysql> use westarbucks;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

아래와 같이 django를 통해 table이 생성되었을 것이다.

mysql> show tables;
+-----------------------+
| Tables_in_westarbucks |
+-----------------------+
| allergies             |
| allergies_products    |
| categories            |
| django_content_type   |
| django_migrations     |
| django_session        |
| images                |
| menus                 |
| nutritions            |
| products              |
| sizes                 |
+-----------------------+

django로 시작하는 것은 무시하자, 여기서는 다룰 내용이 아니다.

4. ipython으로 C.R.U.D하고, mysql로 확인하기

  • products app에서 수정하고 싶은 Table(Menu, Category, ...)를 import 해야 한다.
  • 하지만 여기서는 편의상 models에 해당하는 모든 것(*)을 import 하여 수고로움을 덜겠다.
  • C.R.U.D할 내용이 너무 많아 대표적인 한두 개의 코드만 남겨놓고 ...로 생략하였다.
# 원래는 C.R.U.D 할 Table만 해아 한다.
from products.models import Menu

# 편의상 아래와 같이
from products.models import *
Menu.objects.create(name='음료')
...
mysql> select * from menus;
+----+--------+
| id | name   |
+----+--------+
|  1 | 음료   |
|  6 | 푸드   |
+----+--------+

이후에 Category(mysql에서는 categories)에 해당하는 걸 다 넣어 보자

Category

Category.objects.create(name='콜드 브루', menu=drinks)
...
mysql> select * from categories;
+----+--------------------------------+---------+
| id | name                           | menu_id |
+----+--------------------------------+---------+
|  1 | 콜드 브루						|         1 |
|  2 | 브루드 커피                  	    |       1 |
+----+--------------------------------+---------+
cold_brewed = Category.objects.get(id=1)
brewed_coffee = Category.objects.get(id=2)
...

Product

 Product.objects.create(korean_name='롤린 민트 초코 콜드 브루', english_name='Rollin Mint Choco Cold Brew', description='스타벅스의 콜드 브루와 은은한 민트 초코 베이스로 누구나 즐길 수 있는 여름 음료. 손목의 스냅을 춤을 추듯 가볍게 돌려 음료를 섞어서 빨대 없이 즐겨 보세요.', category=cold_brewed)
 ...
mysql> select * from products;
+----+---------------------------------------------+---------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+
| id | korean_name                                 | english_name                          | description                                                                                                                                                                                                             | category_id |
+----+---------------------------------------------+---------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+
|  1 | 롤린 민트 초코 콜드 브루                    | Rollin Mint Choco Cold Brew           | 스타벅스의 콜드 브루와 은은한 민트 초코 베이스로 누구나 즐길 수 있는 여름 음료. 손목의 스냅을 춤을 추듯 가볍게 돌려 음료를 섞어서 빨대 없이 즐겨 보세요.                                                                |           1 |
|  2 | 나이트로 바닐라 크림                        | Nitro Vanilla Cream                   | 부드러운 목넘김의 나이트로 커피와 바닐라 크림의 매력을 한번에 느껴보세요!                                                                                                                                               |           1 |
+----+---------------------------------------------+---------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+
  • Product 같이 table의 개수가 많으면 보는 게 힘들다
  • 그럴 때는 select *를 쓰기 보다는
  • 아래와 같이 필요한 부분만 보는 것도 좋다.
mysql> select id, korean_name, category_id from products;
+----+---------------------------------------------+-------------+
| id | korean_name                                 | category_id |
+----+---------------------------------------------+-------------+
|  1 | 롤린 민트 초코 콜드 브루                         |           1 |
|  2 | 나이트로 바닐라 크림                             |           1 |
+----+---------------------------------------------+-------------+

Allergy

Allergy.objects.create(name='대두')
...
mysql> mysql> * from allergies;
+----+--------------+
| id | name         |
+----+--------------+
|  1 | 대두         |
|  2 | 우유         |
+----+--------------+

AllergyProduct

AllergyProduct.objects.create(allergy_id=1, product_id=1)
AllergyProduct.objects.create(allergy_id=2, product_id=2)
...
mysql> select * from allergies_products;
+----+------------+------------+
| id | allergy_id | product_id |
+----+------------+------------+
|  1 |          2 |          1 |
|  2 |          1 |          1 |
|  3 |          2 |          2 |
+----+------------+------------+

Nutrition

 Nutrition.objects.create(calories_kcal=210, saturated_fats_g=4.5, protein_g=6, sodium_mg=115, sugars_g=28, caffeine_mg=131, product=Product.objects.get(id=1))
mysql> select * from nutritions;
+----+---------------+------------------+-----------+-----------+----------+-------------+------------+---------+
| id | calories_kcal | saturated_fats_g | protein_g | sodium_mg | sugars_g | caffeine_mg | product_id | size_id |
+----+---------------+------------------+-----------+-----------+----------+-------------+------------+---------+
|  1 |        210.00 |             4.50 |      6.00 |    115.00 |    28.00 |      131.00 |          1 |       2 |
|  2 |         80.00 |             2.00 |      1.00 |     40.00 |    10.00 |      232.00 |          2 |       2 |
+----+---------------+------------------+-----------+-----------+----------+-------------+------------+---------+

Image

URL 주소는 Chrome의 개발자 도구를 이용하여 구했다.
1. 단축키(Ctrl/command + shift + i 또는 F12) 또는 좌클릭 후 검사 눌러 개발자 도구 실행
2. 개발자 도구 좌측 상단에 마우스 클릭으로 해당 html 코드를 볼 수 있는 기능이 있다.(단축키: command + shift + c)
3. 원하는 이미지를 클릭하면 html 문서에서 해당 이미지의 URL 주소를 볼 수 있다.

Image.objects.create(image_url='https://image.istarbucks.co.kr/upload/store/skuimg/2022/04/[9200000003988]_20220406113215431.jpg', product_id=1)
...
mysql> select * from images;
+----+--------------------------------------------------------------------------------------------------+------------+
| id | image_url                                                                                        | product_id |
+----+--------------------------------------------------------------------------------------------------+------------+
|  1 | https://image.istarbucks.co.kr/upload/store/skuimg/2022/04/[9200000003988]_20220406113215431.jpg |          1 |
+----+--------------------------------------------------------------------------------------------------+------------+

Size

Size.objects.create(name='Tall(톨)', size_ml=355, size_fluid_ounce=12)
mysql> mysql> * from sizes;
+----+-------------------+---------+------------------+
| id | name              | size_ml | size_fluid_ounce |
+----+-------------------+---------+------------------+
|  1 | Tall(톨)          | 355     | 12               |
+----+-------------------+---------+------------------+

📝 느낀 점

1. 반드시 큰 틀(table)을 모두 짜는 걸 완료하고 migration 하기

  • 중간에 Table를 수정하려다가 심각한 오류(?)가 발생하여 크게 애를 먹었다.
  • 결국 에러를 해결할 줄을 몰라서 싹 다 갈아 엎고 처음부터 다시 시작했다.😭

에러 내용

  • 테이블을 몇 개만 만들고 C.R.U.D를 하고 나서 다시 여러 테이블을 넣으려고 했는데 다음과 같은 에러가 발생하였다.
You are trying to add a non-nullable field 'name' to allergy without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
2) Quit, and let me add a default in models.py
Select an option: 2
  • 구글링을 여러 번 해서 이것저것 시도해봤지만 오류로부터 탈출을 하지 못하여 결국 다 갈아 엎었다😢
  • 다음에는 ERDModeling을 확실히 해놓고 migration을 하자.

2. 일일이 다 Create를 하려니 힘들다...

  • 테이블이 한 두개도 아니고 중간 테이블 포함 8개이다.
  • 심지어 ProductNutrition처럼 정보량이 테이블에 데이터를 담을 때 너무 힘들었다.😡
  • 동기들이랑 얘기를 해보니 크롤링 과정을 통해서 훨씬 쉽게 데이터를 가져올 수 있다는데... 지금은 CRUD 기초밖에 모르니 어쩔 수 없다.
  • 결국 명령어를 반복하여 치려니 힘들다. 다행히 ipython으로 이전 명령어를 불러올 수 있어 수고를 덜은 정도?

🔖 참고 자료

profile
(Backend Dev.) One step at a time

0개의 댓글