TIL 32. Django - 1:M 관계 Model과 View 작성하기 (주인과 강아지)

문승준·2021년 9월 28일
0

Django

목록 보기
4/7
post-thumbnail

HTTP Client(Httpie, Chrome, Postman 등)를 사용해서 Server에 요청을 보내고 Django Application 을 통해 요청을 분석(URLconf) 하고 요청을 처리하기 위한 로직(View)을 실행시켜 데이터베이스와 통신(Model) 하여 데이터 작업을 수행하고 요청에 맞는 응답을 보낼 수 있는 Backend API를 구현해봅시다.


1. HTTP Request Message 만들기


1. Server에 HTTP request 할 수 있는 Httpie 설치하기

#Ubuntu
sudo apt install httpie

#Mac
brew install httpie

2. Body에 담아서 보낼 Data 만들기 (httpie는 string type을 기본으로 전송)

{
	"menu" : "음료",
	"category" : "에스프레소",
	"product" : "카페 라떼"
}

3. Http POST 요청 보내는 방법

$ http -v POST 127.0.0.1:8000/product menu='음료' category='에스프레소' product='카페 라떼'

# 또는

$ echo '{ "menu" : "음료", "category" : "에스프레소", "product" : "카페라떼"}' | http -v POST 127.0.0.1:8000/products

2. owners app 기능 구현하기

  • 주인과 강아지를 DB에 등록하고 각각의 리스트를 불러올 수 있는 View를 작성해보자.

  • 주인과 강아지는 일대다 관계이다.
    한명의 주인은 강아지 여러 마리를 가질 수 있지만, 하나의 강아지는 한명의 주인만 가진다.

2-1. Model 작성

# owners.models.py

from django.db import models

class Owner(models.Model):
    name  = models.CharField(max_length=20)
    age   = models.IntegerField()
    email = models.EmailField(max_length=40)

    class Meta:
        db_table = 'owners'

class Dog(models.Model):
    name  = models.CharField(max_length=20)
    age   = models.IntegerField()
    owner = models.ForeignKey("Owner", on_delete=models.CASCADE)

    class Meta:
        db_table = 'dogs'
  • Dog class의 owner 필드는 Owner class로 외래키를 설정해주고, 부모 테이블 값이 삭제되면 자식 테이블에도 그 값이 자동 삭제되도록 on_delete=models.CASCADE 옵션을 지정해준다.

  • 주인 owners (부모 테이블)


  • 강아지 dogs (자식 테이블) -> 외래키는 owner_id이다.



2-2. OwnerView 작성 (create & get)

# owners.views.py

import json

from django.http   import JsonResponse
from django.views  import View

from owners.models import Owner
  • JSON 타입으로 요청과 응답을 주고 받는다.

OwnerView - create 기능 구현

class OwnerView(View):
    def post(self, request):
        try:
            data = json.loads(request.body)
            Owner.objects.create(
                name = data['owner_name'],
                email = data['owner_email'],
                age = data['owner_age'],
            )
            return JsonResponse({'message': 'CREATED'}, status=201)
        
        except KeyError:
            return JsonResponse({'message' : 'KEY_ERROR'},status=400)
  • OwnerView로 오는 HTTP POST 요청을 받아들여서 주인의 이름, 이메일, 나이 정보를 DB에 저장한다.

  • json.loads() 메소드는 JSON 문자열을 파이썬 딕셔너리 형태로 전환시켜준다. 즉, 파이썬 객체를 반환한다.

  • data 객체는 딕셔너리 타입으로 [key] 로 접근할 수 있으며 get() 메소드도 사용 가능하다.

    → 이때, key가 없다면 KeyError가 일어나고 get()을 사용하면 None만 반환된다.

  • try, except 문으로 KeyError 상황을 예외처리 해주고 적절한 에러를 반환한다.

OwnerView - get 기능 구현

class OwnerView(View):
    ...
		def get(self, request):
        result = []
        owners = Owner.objects.all()

        for owner in owners:
            dog_list = []
            dogs = owner.dog_set.all()  # 역참조 활용
            for dog in dogs:
                dog_info = {
                    "name" : dog.name,
                    "age"  : dog.age
                }
                dog_list.append(dog_info)
            owner_info = {
                "name"      : owner.name,
                "email"     : owner.email,
                "age"       : owner.age,
                "dog_list"  : dog_list
            }
            result.append(owner_info)
        return JsonResponse({'result': result}, status=200)
  • OwnerView로 오는 HTTP GET 요청을 받아들여서 강아지 주인 리스트를 응답한다.

    이때 포함하는 정보는 주인의 이름과 이메일, 나이, 강아지 리스트(이름과 나이)이다.

  • 모든 주인에 대하여 아래의 로직을 반복한다.

  • 모든 주인 정보와 각 주인마다 보유한 강아지 정보가 필요하다.

  • 각 주인마다 보유한 강아지 정보를 알기 위해 dogs 테이블로 접근한다.
    -> owners 테이블에서 접근하려면 역참조 (외래키는 dogs 테이블에 있으니까)

  • 해당 강아지가 여러 마리일 수 있으니 for문으로 순회하고 배열에 담는다.

  • 해당 주인의 정보와 강아지 리스트를 최종 result 배열에 담는다.

2-3. DogView 작성 (create & get)

# owners.views.py

class OwnerView(View):
	...

class DogView(View):
    def post(self, request):
        data = json.loads(request.body)
        # 에러핸들링
        if not Owner.objects.filter(id=data['owner_id']).exists():
            return JsonResponse({'MESSAGE': "Owner Does Not Exist"}, status=404)
        
        Dog.objects.create(
                name  = data['dog_name'],
                age   = data['dog_age'],
                owner_id = data.owner.id # 정참조 활용
        )
        return JsonResponse({'MESSAGE': 'CREATED'}, status=201)

    def get(self, request):
        result = []
        dogs   = Dog.objects.all()
        for dog in dogs:
            dog_info = {
                "name"      : dog.name,
                "age"       : dog.age,
                "owner_name": dog.owner.name # 정참조 활용
            }
            result.append(dog_info)
        return JsonResponse({'result': result}, status=200)
  • POST 요청으로 강아지 등록시 이름, 나이, 주인id가 필요하다.

  • GET 요청으로 강아지 리스트를 요청하면 dogs table에서 FK를 이용해 정참조로 주인 이름을 포함시킨다.

  • if문과 exitsts() 메소드를 이용해 요청된 데이터의 주인id가 없으면 에러 메시지를 반환한다.


2-4. URLconf 작성

# owners.urls.py

from django.urls import path

from owners.views import OwnerView, DogView

urlpatterns = [
    path('/owner', OwnerView.as_view()),
    path('/dog', DogView.as_view())
]
  • Owner 폴더에 urls.py 파일을 작성한다.
# project.urls.py

from django.urls import path, include

urlpatterns = [
    path('owners', include("owners.urls")),
]
  • 프로젝트 폴더의 urls.py 에서 연결하고자 하는 View를 바라보게 해준다.

    여기선 url 참조를 위해 include 함수를 이용한다.

  • HTTP 요청시 localhost/owners/owner(or dog) 주소로 보낸다.


- GET 요청 테스트 사진

profile
개발자가 될 팔자

0개의 댓글