C.R.U.D란 컴퓨터 소프트웨어가 가지는 기본적인 데이터 처리 기능인
를 뜻한다.
여기선 C, R을 한번 Review해보고자 한다.
기존에 작성하였던 글을 참조하여 아래와 같이 테스트용 프로젝트를 생성하였다.
test
가상환경 생성
django_project
프로젝트 생성
practice/pet
브랜치 생성
cats
App 생성
주인 - 고양이 관계를 생성하여, 두세사람이 여러마리의 고양이를 가졌다고 가정한 후 해당 DB를 통해 아래 기능을 구현할 예정이다.
POST
이용GET
이용어떠한 데이터가 들어갈 것인지 정했으므로, 데이터가 들어갈 Model을 생성하면 된다.
우리는 django_project
이름의 프로젝트를 만들었고, cats
라는 앱을 만들었다.
이제 cats
앱 안에 생성된 models.py
파일에 모델을 정의해주면 된다.
처음 models.py
로 들어가보면 아래와 같다.
이제 모델을 아래와 같이 구현하면 된다.
# cats/models.py
from django.db import models
class Owner(models.Model):
name = models.CharField(max_length=45)
email = models.CharField(max_length=300)
age = models.IntegerField()
class Meta:
db_table = 'owners'
class Cat(models.Model):
owner = models.ForeignKey('Owner', on_delete=models.CASCADE)
name = models.CharField(max_length=45)
age = models.IntegerField()
class Meta:
db_table = 'cats'
이제 각 줄의 의미를 알아보자.
class Owner(models.Model):
→ 모델(객체)을 정의
class
: 객체를 정의할것이라고 선언Owner
: 모델 이름models
: Owner
이 장고 모델임을 알려줌name = models.CharField(max_length=45)
→ 각 필드의 데이터타입 지정
(당연하겠지만 aquery에 지정한 row대로 설정해주면 된다.)
owner = models.ForeignKey('Owner', on_delete=models.CASCADE)
→ 다른 모델의 FK인 경우 이와 같이 어떤 모델의 FK인지 선언해줘야 한다.
(on_delete
는 개체 삭제시 수행할 동작이고, models.CASCADE
는 FK를 포함하는 행도 함께 삭제한다는 의미이다.)
class Meta: db_table
: MySQL의 table 이름 지정
최종적으로는 아래와 같이 모델이 구현된다.
(위에 생성된 모델이랑 우리가 만들었던 아래의 Aquery 테이블을 비교해보자)
"Aquery에는 owner_id
라고 해줬는데 왜 모델에는 owner
라고만 적었냐?"
라고 생각할 수 있다.
해답은 꽤나 간단한데, 나중에 mySQL에서 FK같은 경우는 자기가 알아서 뒤에 _id
를 붙여주기 때문이다.
완성 후 요청을 보내보면 아래와 같이 나온다.
(owner_id라고 써있는게 보인다!!)
models.py의 기능을 이해할 필요가 있다.
Django는 models.py를 통해 데이터에 접근하고 관리한다.
즉, Django에서 기본적으로 제공하는 db.sqlite3와 연동해서 models.py에서 정의한 models 클래스의 데이터들을 DB형태로 관리하게 된다.
이제 아래의 두가지 방법을 거쳐 모델을 database에 적용시켜야한다.
위 models.py
에 python code를 이용하여 작성한 모델을 database에 적용하기 위해 migration 파일(설계도)을 만든다.
python manage.py makemigrations app이름
makemigration으로 생성한 migration 파일(설계도)을 database에 적용
python manage.py migrate
실제로 shell에서 코드를 치면 아래와 같이 적용된다.
POST
를 이용하여 Create)데이터가 들어갈 모델을 작성했으니 이제 View를 이용하면 된다.
이제 POST
를 이용해서 데이터를 집어넣는 로직을 짜보자.
처음 views.py를 들어가면 아래와 같다.
이제 아래와 같이 구현하면 된다.
# cats/views.py
import json
from django.db.models.fields import EmailField
from django.shortcuts import render
from django.http import JsonResponse
from django.views import View
from cats.models import Owner, Cat
class OwnersView(View):
def post(self, request):
data = json.loads(request.body)
owner = Owner.objects.create(
name = data['owner_name'],
email = data['email'],
age = data['owner_age']
)
return JsonResponse({'MESSAGE':'Owner Info Created!'}, status=201)
class CatsView(View):
def post(self, request):
data = json.loads(request.body)
owner = Owner.objects.get(name=data['owner_name'])
cat = Cat.objects.create(
owner = owner,
name = data['cat_name'],
age = data['cat_age'],
)
return JsonResponse({'MESSAGE':'Cat Info Created!'}, status=201)
views.py
에서는 보통 파이썬 문법을 활용하여 여러 함수들을 생성하게 되며, 이 함수들을 이용하여 자신이 원하는 형태로 데이터를 처리한 뒤, 어떠한 html로 데이터를 보내게 되는 것이다.
서버에 HTTP Request를 가능하게 해주는 httpie를 설치하면 자체적으로 확인이 가능하다.
brew install httpie
view를 만들었으면 이제 클라이언트의 요청을 받아 적절한 view를 매핑해주는 urls.py
를 작성하면 된다.
우리는 cats
앱을 만들었으므로, 해당 위치에 urls.py를 생성 해주자.
또는 아래와 같이 shell에서 바로 작성해주어도 무방하다.
touch urls.py # urls.py 생성
vi urls.py # urls.py로 이동
이제 urls.py에 아래와 같이 입력해주자.
# cats/urls.py
from django.urls import path
from cats.views import OwnersView, CatsView
urlpatterns = [
path('owners', OwnersView.as_view()),
path('cats', CatsView.as_view()),
]
우리는 위에서 두개의 view class를 정의했다.
각각의 view class에 대해, 클라이언트가 입력한 정보를 어떻게 처리하여 보여줄(view)것인가를 urls.py
가 도와주는 개념이다.
path
에 있는 owners
, cats
를 address/main_url_path 뒤에 추가하게 되면 views.py
에서 정의했던 각각의 class와 연결이 된다.
이제 클라이언트로부터 제일 처음 요청을 받기 위해 메인 urls.py
를 작성해주면 된다.
처음 모양은 아래와 같다.
이제 아래와 같이 추가하면 된다.
# django_training/urls.py
from django.urls import path, include
urlpatterns = [
path('pets/', include('cats.urls')),
]
VSCode로 보면,
이제 domain/pets/owners 혹은 domain/pets/cats와 같이 url이 연결된다.
앞서 httpie를 설치하였다.
이제 위에 작성한 POST
를 이용하여 서버에 데이터를 Create해보자!
먼저 Owner 데이터를 입력해보자.
위 views.py
에서 정의한 json 데이터 형식의 key와 value를 아래와 같이 임의로 지정해줘봤다.
(key라고 하는게 맞는지 모르겠다. python의 딕셔너리 형식과 비슷해서 일단 이렇게 생각하고 있다.)
http -v POST 127.0.0.1:8000/pets/owners owner_name='김장고' email='django@django.com' owner_age='21'
위를 보면 알겠지만, mail URL인 pets 뒤에 앱의 url인 owners로 간다고 했고,
위에서 언급했던것처럼 그럼 우리가 views.py
에서 선언했던 Owners
Class를 이용하는 것이다.
테이블이 생성되지 않았다고 한다.
여기엔 언급되지 않았지만 views.py
에서 객체를 import하는 과정에서 오타가 있어서 수정을 했는데, 그래서 migrate을 다시 해야했던것 같다.
그래서 migrate을 다시 진행했다.
근데 아니란다.
내가 migrate을 한 다음 db_table의 이름을 바꿨는데 그게 적용 안돼서 그랬던거 같다.
아무튼 이제 다시 제대로 위와 같이 데이러를 Create 해보자.
잘 됐다. (이게 뭐라고 눈물이 난다.)
그럼 이제 고양이 정보도 잘 create 되는지 해보자.
잘 된다.
사실 여기에는 우여곡절이 있었는데 오류 코드는 아래와 같다.
# cats/views.py
class CatsView(View):
def post(self, request):
data = json.loads(request.body)
cat = Cat.objects.create(
owner = Owner.objects.get(name=data['owner_name']),
name = data['cat_name'],
age = data['cat_age'],
)
return JsonResponse({'MESSAGE':'Cat Info Created!'}, status=201)
참 멍청했다. 아래 aquery 테이블을 보자
FK인 owner_id
를 설정해줘야 하는데, 그걸 바보같이 Cat 객체 안에서 지정을 해준 것이다.
당연히 owner_id는 바깥으로 빼서 지정해줘야하고,
cat 내부의 owner은 그냥 변수로 같다고만 해줘도 된다.
# cats/views.py
class CatsView(View):
def post(self, request):
data = json.loads(request.body)
owner = Owner.objects.get(name=data['owner_name'])
cat = Cat.objects.create(
owner = owner,
name = data['cat_name'],
age = data['cat_age'],
)
return JsonResponse({'MESSAGE':'Cat Info Created!'}, status=201)
아래와 같이 DB를 만들었다.
이제 아래 데이터들을 불러오는 get
함수를 만들어볼 것이다.
아래와 같이 만들어보자.
cats/views.py
import json
from typing import AsyncGenerator
from django.db.models.fields import EmailField
from django.shortcuts import render
from django.http import JsonResponse
from django.views import View
from cats.models import Owner, Cat
class OwnersView(View):
# POST
def post(self, request):
data = json.loads(request.body)
owner = Owner.objects.create(
name = data['owner_name'],
email = data['email'],
age = data['owner_age']
)
return JsonResponse({'MESSAGE':'Owner Info Created!'}, status=201)
# GET
def get(self, request):
owners = Owner.objects.all()
results = []
for owner in owners:
results.append(
{
"Owner\'s name" : owner.name,
"e-mail address" : owner.email,
'Owner\'s age' : owner.age,
}
)
return JsonResponse({'Result!':results}, status=200)
class CatsView(View):
# POST
def post(self, request):
data = json.loads(request.body)
owner = Owner.objects.get(name=data['owner_name'])
cat = Cat.objects.create(
owner = owner,
name = data['cat_name'],
age = data['cat_age'],
)
return JsonResponse({'MESSAGE':'Cat Info Created!'}, status=201)
# GET
def get(self,request):
cats = Cat.objects.all()
results = []
for cat in cats:
results.append(
{
'Cat\'s name' : cat.name,
'Cat\'s age' : cat.age,
'Owner\'s name' : owner.name
}
)
return JsonResponse({'Result!':results}, status=200)
Owner
정보를 GET 해보자
http -v GET 127.0.0.1:8000/pets/owners
잘 나왔다.
이제 Cat
정보를 불러보자.
http -v GET 127.0.0.1:8000/pets/cats
에러가 났다.
에러를 알아보기 전에 Owner
정보는 잘 나왔으니 어떻게 저렇게 짰나 생각을 해보자.
cats
라는 변수를 지정하고, Cat
class의 모든 객체를 불러온다.results
생성for
문을 만들고, list안에 dictionary 형식으로 정보를 불러온다. (어떤 정보냐면 아래의 정보다.)이런식으로 진행이 된다고 보면 된다.
기본적으로 for
문으로다가 하나씩 돌려서 정보를 뽑아내는 형식이다.
근데 NameError
가 떴다.
왜이럴까?
당연하다.
CatsView
class에서, for
문에 cat에 대해서만 돌렸는데, 갑자기 owner
이 나와버리니 owner
가 정의되지 않았다고 뜨는 것이다.
아래와 같이 고쳐보자.
잘 불러져왔다!!!!
위에서는 동명이인의 경우를 생각하지 않았지만, 동명이인이 존재하는 경우 위 예시처럼 하면 안된다.
만약 위 정보로만 하게된다면, 이름이 아닌 이메일 이름으로 구분을 해야할 듯 하다.
이렇듯 다뤄야하는 정보가 많아지고, 생각해야하는 관계가 많아지면 정보간의 관계를 잘 조율하여야할 듯 하다
위 관계는 cat은 owner를 참조하고 있다.
그래서, cat에서 owner를 불러오는것은 가능한데 역으로 owner가 cat 정보를 불러오는(역참조)는 위 방법으론 불가능하다.
이제 역참조가 어떻게 가능한지를 알아봐야겠다.
참고
https://tutorial.djangogirls.org/ko/django_models/
https://076923.github.io/posts/Python-Django-11/
https://livetodaykono.tistory.com/42
https://velog.io/@magnoliarfsit/ReDjango-3.-GET-POST-메소드-차이점-및-api-설계
https://opentutorials.org/module/4034/24661