TIL #38 http 통신 (요청/응답)-django

채록·2021년 1월 25일
0

Python & Django

목록 보기
12/34
post-thumbnail

들어가는 말

  • 이전에 django shell 을 통해 class 마다 일일이 create 해주었던 것을 views.py 를 통해 한번에 create 할수 있게 해본다.
  • http 통신에 기반해 request에 따른 데이터 처리 과정을 이해한다.

kew-word

  • views.py
  • http : request

flow

  • 함수 작성
  • 전체(프로젝트)의 url 생성
  • 중간(특정 app과 연관되는) url 생성




I. Client 준비

client에서 server로 HTTP request를 전송할 수 있는 것중 하나인 Httpie를 설치한다.
brew install httpie

brew install 은 프로그램을 설치하는 것 이므로 경로가 상관 없다. ex) mysql - 어느 가상환경에 설치하든 다 있다.
pip install 은 파이썬으로 작성된 패키지 소프트웨어를 설치하는 것이다. 가상환경 마다 설치가 필요하다 ex) django

까는 도중 에러가 발생했다 : link가 안되어있다!
이럴땐 brew link --overwrite@python=3.9를 입력한 뒤 다시 brew install httpie를 해준다.


1. http 요청 보내기

이전에 연습했던 프로젝트에서 진행하겠다.

연습용 pj 환경

  • 가상환경 : example
  • 위치 : MyProjects > wedia17
  • 플젝 이름 : wedia
  • db 이름 : beverage

틀어야 할 터미널

  • 요청 입력용 터미널
  • runserver용 터미널 (계속 틀어놓고 파일을 수정하면서 실시간 통신 상황을 살펴본다)

runserver를 한 상태에서 아래 코드를 입력해본다.

http -v POST 127.0.0.1:8000/products menu='음료' category='콜드브루' product:='{"name":"맛있는 콜드브루", "price":5400}'

다음과 같은 에러가 뜬다

Not Found: /product
[25/Jan/2021 15:31:27] "POST /product HTTP/1.1" 404 1966

이런 에러가 뜨는 이유는 요청을 받아 줄 서버가 없기 때문!!!

404 : Not Found
요청된 URI 가 존재하지 않는다는 의미

따라서 이번 학습은 해당 요청을 받아서 상품을 생성하는 application을 만드는 것이다.


연습중인 플젝의 models.py 상태

class Menu(models.Model):
        name            = models.CharField(max_length=50)

        class Meta:
                db_table='menu'

class Category(models.Model):
        name            = models.CharField(max_length=50)

        class Meta:
                db_table='categories'

class Product(models.Model):
        name            = models.CharField(max_length=50)
        description     = models.CharField(max_length=1000)
        category        = models.ForeignKey('Category', on_delete=models.CASCADE, null=True)
        is_new          = models.BooleanField(default=False)

        def __str__(self):
                return f'{self.name}'

        class Meta:
                db_table='products'

class Nutrition(models.Model):
        fat_g           = models.DecimalField(max_digits=4, decimal_places=1, null=True)
        calories_kcal   = models.DecimalField(max_digits=4, decimal_places=1, null=True)
        caffeine_mg     = models.DecimalField(max_digits=4, decimal_places=1, null=True)
        product         = models.OneToOneField('Product', on_delete=models.CASCADE, null=True)

        def __str__(self):
                return f'{self.product}'

        class Meta:
                db_table='nutritions'


+) 404 에러가 안 뜨는 상황

프로젝트 연습을 하면서 admin에 의해 자동으로 생성되는 기능들을 사용하지 않기 위해

app/settings.py 에서

  • IP 허용 : ALLOWED_HOST = ['*']
  • INSTALLED_APPS 에서 다음 두개 주석처리
    - django.contrib.admin
    - djang.contrib.auth
  • MIDDLEWARE 에서 다음 두개 주석처리
    - django.middleware.csrf.CsrfViewMiddleware
    - django.contrib.auth.middleware.AuthenticationMiddleware

app/urls.py 에서

  • urlpatterns = [ ] 괄호 안 내용 지움

을 설정해주었었다. 위 설정을 적용한 채로 runserver 뒤 http를 요청하면 200OK가 뜬다..





II. Create 하기

data를 create 하는것은 프론트로부터 받은 요청을 백에서 받아 database에 저장/수정 하는 것이다! 프론트의 관점으로 http 의 문법이 지칭되기 때문에 이런 경우 POST 하는 것이다. 따라서 create 할때에는 views.py 에서 post메소드를 사용한다.

1. app/view.py 작성 (함수 작성)

resource(자원)을 생성할 때, post Method를 사용한다!! (http method 중의 하나 / get과 반대 의미)

로그인, 회원가입 등 중요한 정보들도 POST method로 데이터를 request의 body에 담아서 client에서 server로 요청한다.

또한 models.py에서는 class를 만들고 그 속에 attribute(속성)을 넣었는데, views.py에서는 class를 만들고 그 안에 메소드(클래스 속의 함수 지칭), 그리고 그 메소드 안에 사용될 변수들을 생성해준다.

// vi products/views.py
// 원래 상태
from django.shortcuts import render

여기에 코드를 추가해 준다.

import json
from django.http     import JsonResponse

from django.views    import View
from products.models import Menu, Category, Product

class ProductsView(View):

    def post(self, request):
		    data = json.loads(request.body)
        menu     = Menu.objects.create(name=data['menu'])
        category = Category.objects.create(
                name=data['category'],
                menu=menu
        )
        product = Product.objects.create(
                name=data['product']['name'],
                category=category
        )
        return JsonResponse({'MESSAGE':'SUCCESS'}, status=201)

JSON ?
각 언어마다 지칭이 다르다! ex) 파이썬의 딕셔너리가 자바스크립트에서는 객체라고 불리운다..
때문에 통신을 할때 언어별로 지칭이 다르더라도 약속된 형식으로 일하기로 약속했는데 그것이 JSON 이며, text 형식으로 작동한다.! (통신을 할 때 key와 value로 이뤄져 있고 다 text 이다.. 사람이 알아볼수 없는 언어임)

따라서 이걸 사람이 알아볼 수 있도록 JSON 폼 데이터를 Python 객체로 읽게 하는 것이 json.loads() 이다.

2. pj-config/urls.py 작성 (전체 url)

// vi wedia/urls.py
//원래 상태
from django.urls import path

urlpatterns = [
]

다음의 코드를 추가한다.

from django.urls import path, include

urlpatterns = [
    path('products', include('products.urls'))
]

include도 함께 import 해주기!!!

3. app/urls.py 새로 작성하기 (중간 url)

app dir에는 원래 urls.py가 없다! 그래서 그냥 만들어 준다

새로 urls.py 만드는 법

  • touch urls.py touch는 파일을 새로 만들거나 수정하는 명령어이다.
  • vi urls.py vi로 urls.py 안에 들어감과 동시에 존재하지 않았던 파일이라면 새로 만든다. 저장하면 파일이 새로 저장되는 격!
//in products/urls.py
from django.urls    import path
from products.views import ProductsView

urlpatterns = [
    path('', ProductsView.as_view())
]

4. runserver로 확인

http -v POST 127.0.0.1:8000/products menu='음료' category='콜드브루' product:='{"name":"맛있는 콜드브루", "price":5400}'

1) 실패

결과

[25/Jan/2021 16:17:09] "POST /products HTTP/1.1" 500 82761

😃 ................ 왜...그러는거야 대체..................
에러 위에를 보니 다음과 같은 문구가 있다

TypeError: Category() got an unexpected keyword argument 'menu'

카테고리에 메뉴가 연결이 안되있다고?? 엇;; 얼른 products/models.py 에 FK로 연결시켜 줬다;;

2) 성공!



👏👏👏👏👏👏👏 박수짝짝




III. Read 하기

프론트의 관점에서 Read 하는것은 서버에 저장되어 있는 정보를 얻어오는 것이다! 때문에 이때 사용되는 것은 GET메소드 이다.

get 메소드

  • in django : table의 row를 불러온다!
  • in http : 프론트가 백으로부터 데이터를 갖고 올 때
    +) http의 post 메소드 : 프론트에서 백으로 데이터를 보낼 때 / 그 데이터가 db에서 등록/수정 되는 것.

1. app/views.py 작성

// vi products/views.py
class ProductsView(View):
		def get(self, request):
				products = Product.objects.all()
				results = []
				for product in products:
						results.append(
							{
									"menu":product.category.menu.name,
									"category":product.category.name,
									"product" :product.name
							}
				)
        return JsonResponse({'results':results}, status=200)

아까 Create 를 위해 작성했던 class 에 get 메소드만 추가한다
공백 간격 주의... 탭 or space 둘중 하나만!!!!! 섞어쓰면 에러뜸
TabError: inconsistent use of tabs and spaces in indentation

위 파일 작성 후 runserver 터미널 상황

2. app/urls.py & pj-config/urls.py

아까 create 하면서 설정한 것만 있으면 된다!

3. httpie(clien)로써 server에 요청 보내기

http -v GET 127.0.0.1:8000/products

1) 실패

AttributeError: 'NoneType' object has no attribute 'menu'
[25/Jan/2021 17:06:44] "GET /products HTTP/1.1" 500 67729

😃 ...... 한번에 되는게 없네
아까 급하게 category table에 menu를 넣으면서 null=True로 설정했다. 이 경우 불러올 menu의 값이 없기 때문에 위와 같은 에러가 뜬다... 값.. 넣고온다..그래도 안된다!

... products에서! menu 값이 없다는 뜻이었나???!!!! products table에 menu_id column 만들고 온다.

만들어도 안된다!!! 답은.. products table에서 category_id의 값이 NULL 이었기 때문에 NoneType 에서 읽어들일 수 없다고 뜬 것이었다..
그래서 django shell 을 이용해 빠르게 update 시켜주었다. (장고 쉘 이용 안하려구 이거 배우는 건데 ㅎㅎ;;) 그리고 !! 자나깨나 오타조심!!

2) 성공!

{
    "results": [
        {
            "category": "콜드 브루",
            "menu": "음료",
            "product": "나이트로 바닐라 크림"
        },
        {
            "category": "콜드 브루",
            "menu": "음료",
            "product": "나이트로 콜드 브루"
        },
        {
            "category": "프라푸치노",
            "menu": "음료",
            "product": "자바 칩 프라푸치노"
        },
        {
            "category": "프라푸치노",
            "menu": "음료",
            "product": "화이트 딸기 크림 프라푸치노"
        },
        {
            "category": "블렌디드",
            "menu": "음료",
            "product": "자몽 셔벗 블렌디드"
        },
        {
            "category": "블렌디드",
            "menu": "음료",
            "product": "딸기 요거트 블렌디드"
        },
        {
            "category": "에스프레소",
            "menu": "음료",
            "product": "아이스 커피"
        },
        {
            "category": "에스프레소",
            "menu": "음료",
            "product": "오늘의 커피"
        },
        {
            "category": "에스프레소",
            "menu": "음료",
            "product": "더블 에스프레소 크림 라떼"
        },
        {
            "category": "브루드 커피",
            "menu": "음료",
            "product": "제주 샤이닝 바나나 라떼"
        },
        {
            "category": "콜드브루",
            "menu": "음료",
            "product": "맛있는 콜드브루"
        }
    ]
}

참고로 위 방법으로 읽히는 것은 models.py 의 __str__함수와는 전혀 상관 없다!!



🐱 C.R 마무리

오늘은 연습하면서 client도 됐다가... server도 됐다가 해봤다..
이런식으로 소통하는구나!!! 를 느낄 수 있었다!

따라치는것 이제 끝내고 스스로 쳐보면서 익히도록 한다.

U.D 는 열심히 googling 하면서 익히도록!

profile
🍎 🍊 🍋 🍏 🍇

0개의 댓글