Django 다양한 응답의 함수 기반 뷰

2022. 1. 6. 15:27강의 정리/Django Views

반응형

출처 : https://www.inflearn.com/course/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%9E%A5%EA%B3%A0-%EC%9B%B9%EC%84%9C%EB%B9%84%EC%8A%A4/dashboard

 

장고(Django)를 배우기 시작한 입문자이시거나, 또는 배우고 싶은 생각이 있으신 분은 위 출처의 강의를 적극 추천드립니다!!!

 

 

 


장고의 작동 흐름

  • 웹 브라우저에서 어떤 이벤트가 발생한다고 하자.
    (여기서 이벤트는 특정 url을 클릭한다던지, 폼에 데이터를 입력해 보내는 등의 액션을 뜻한다)
    이벤트가 발생하면 장고 서버로 request(이벤트를 처리해달라는)가 들어오게 된다.
  • 장고 서버로 들어온 이벤트에 대해 URL 디스패처가 URL을 분석해서, 적합한 VIEW로 이 요청을 보낸다.
  • VIEW는 사용자 요청을 받아 데이터 베이스 어디에 접근해서 어떤 데이터를 가공할것인지 MODEL에게 알려준다.
  • MODEL은 DB와 커넥션을 해서 필요한 DB 연산을 처리한다.
  • DB가 다시 모델로 결과값을 보내주면 모델이 이것을 뷰로 전달한다.
    뷰는 우리에게 보내줄 데이터를 다시 TEMPLATE에게 전달해준다.
  • TEMPLATE는 .js나 .html과 같은 페이지를 만들어서 웹브라우저에게 넘겨준다.

https://eunhyejung.github.io/python/2018/07/31/django-basic-concept.html

 

Django 기본 개념 이해하기

   

eunhyejung.github.io

 

 

즉 request란 예를 들어서 검색창에 dongbaek이라고 검색하면 dongbaek이라는 값이 requset 인자로 받아진다.

 

 

#instagram/views.py 

from django.shortcuts import render
from .models import Post


def post_list(request):
    # QuerySet을 의미함.
    qs = Post.objects.all()
    
    # 전달 받은 인자 중에서 'q'라는 이름의 인자를 가져오겠다는 의미
    # 두번째 매개변수는 만약 'q'라는 인자가 없을시 반환할 값을 의미
    q = request.GET.get('q', '')
    
    # 'q'라는 인자가 있었을 경우
    if q:
        #  filter를 통해서 message 필드 중 q인 필드들만 qs에 저장함.
        qs = qs.filter(message__icontains=q)
    
    # render를 통해서 HTML 응답을 쉽게 만들 수 있다.
    # render의 첫번째 인자는 view 함수의 인자를 넘겨받는다.
    # 두번째 인자는 app이름을 써주고 뒤에 원하는 템플릿(HTML) 이름을 써준다.
    # 세번쨰 인자로 템플릿 내에서 참조할 것과 참조할 이름을 적어준다.
    
    # 위치는 instagram(app)/templates/instagram/post_list.html
    return render(request, 'instagram/post_list.html', {
        'post_list': qs,
        'q' : q,
    })
  • render는 httpRespose 객체를 반환하는 함수로 template을 context와 엮어 httpResponse로 쉽게 반환해 주는 함수임
장고에서 view의 역할은 쉽게 설명하면 models로부터 데이터 테이블을 받아서,  가공한다음 HTML 템플릿에 가공한 인자들을 넘겨주고, 최종적으로 url에 render된 HTML를 인자로 갖는 함수를 return해준다.  .

 


 

View

- 1개의 HTTP 요청에 대해 -> 1개의 뷰가 호출

# HTTP는 Hyper Text Transfer Protocol의 두문자어로, 인터넷에서 데이터를 주고받을 수 있는 프로토콜이다.

 

- urls.py / urlpatterns 리스트에 매핑된 호출 가능한 객체

urlpatterns = [
    path('', views.post_list),

]
  • 함수도 "호출 가능한 객체" 중의 하나

 

- 웹 클라이언트(안드로이드, IOS app)로부터 HTTP 요청을 처리

 

 

크게 2가지 형태의 뷰

- 함수 기반 뷰 (Function Based View) : 장고 뷰의 기본.

  • 호출 가능한 객체, 그 자체
  • 장고를 배우기 시작한 초반에는 클래스 기반 뷰가 어렵게 느껴지기 때문에, 함수 기반 뷰로 시작하는 것을 추천함.

 

- 클래스 기반 뷰 (Class Based View) 

  • 클래스.as_view 를 통해 호출가능한 객체를 생성 / 리턴
from django.views.generic import ListView

post_list = ListView.as_view(model=Post)
from django.views.generic import ListView

Class ItemListView(ListView): # ListView 상속
	model = Item

post_list = ListView.as_view()

 


 

View 호출 시 인자 # HttpRequest 객체 및 URL Captured Values

1번째 인자 : HttpRequest 객체

  • 현재 요청에 대한 모든 내역을 담고 있다.
def post_list(request):

# 즉 model로부터 넘겨받은 데이터베이스를 흔히 첫번째 HttpRequest 객체 로 사용한다.

 

 

2번째~ 인자 : 현재 요청의 URL로부터 Capture된 문자열

def post_detail(request, pk):
    pass

# 위 코드에서 pk인자가 URL Captured Values 이다.

 

 

urlpatterns = [
    path('', views.post_list),
    path('1/', views.post_detail),
    path('2/', views.post_detail),
    path('3/', views.post_detail),
    path('4/', views.post_detail),
    ]

+ url / urlpatterns에서 아래 코드처럼 작성할 시 위 코드의 번거러움을 해결할 수 있다.

urlpatterns = [
    path('', views.post_list),
    path('<int:pk>/', views.post_detail),
    # 숫자 타입이 나오고, /로 끝날경우 뒤 함수를 호출하겠다는 의미
]

 

  •  url / re_path 를 통한 처리에서는 -> 모든 인자는 str 타입으로 전달 # path에서는 타입에 맞게 인자가 전달이 된다.
re_path(r'(?P<pk>\d+)/$', views.post_detail)

path('<int:pk>/', views.post_detail)

두 코드는 서로 같은 의미
  •  path를 통한 처리에서는 -> 매핑된 Converterto_python에 맞게 변환된 값이 인자로 전달

 Converter는 아래 코드 부분을 칭한다.

'<int:pk>/'

 

 

 


 

View 호출에 대한 리턴값 # HttpRequest 객체

필히 무조건!!! HttpResponse 객체를 리턴해야 함.

def post_list(request):
    qs = Post.objects.all()
    q = request.GET.get('q', '')
    if q:
        qs = qs.filter(message__icontains=q)
    return render(request, 'instagram/post_list.html', {
        'post_list': qs,
        'q' : q,
    })
  • 장고 Middleware에서는 뷰에서 HttpResponse 객체리턴하기를 기대한다. -> 다른 타입을 리턴하면 Middleware에서 처리 오류
  • django.shortcuts.render 함수는 템플릿 응답을 위한 shortcut 함수

 

파일 like 객체 혹은 str / bytes 타입의 응답 지원

# 이 말은 아래 코드와 같은 방식으로도 HttpResponse 객체를 리턴할 수 있다는 말이다.

def post_detail(request, pk):
    response = HttpResponse()
    response.wrire("Hello World")
    return response
    response = HttpResponse( 파일like객체 또는 str객체 또는 byte객체)
    response.wrire(str객체 또는 bytes객체)
  • 장고에서 모든 문자열은 자동으로 utf-8로 인코딩 해준다.

 

 


 

HttpRequest와 HttpResponse 예시

from django.http import HttpRequest, HttpResponse

def index(request: HttpRequest) -> HttpResponse: # View 함수
    # 주요 request 속성
    request.method # 'GET', 'POST', etc...
    request.META
    request.GET, request.POST, request.FILES, request.body

    content = '''
    <html>...</html>
    '''
    # 문자열 혹은 이미지, 각종 파일 등

    response = HttpResponse()
    response.write(content) # response -> file-like object
    return response

장고는 request와 response 객체로 상태를 서버와 클라이언트가 주고 받습니다. 이를 위해 장고는 django.http 모듈에서 HttpRequest와 HttpResponse API를 제공합니다.

서버-클라이언트 통신 시 아래와 같은 절차로 데이터가 오고 갑니다.

 

1) 특정 페이지가 요청(request)되면, 장고는 요청 시 메타데이터를 포함하는 HttpRequest 객체를 생성
2) 장고는 urls.py에서 정의한 특정 View 클래스/함수에 첫 번째 인자로 해당 객체(request)를 전달
3) 해당 View는 결과값을 HttpResponse 혹은 JsonResponse 객체에 담아 전달

https://velog.io/@jcinsh/Django-request-response

 

 


 

다양한 타입의 HttpResponse

Excel 파일 다운로드 응답

def response_excel(request):
    filepath = '/other/path/excel.xls'
    filename = os.path.basename(filepath)
    with open(filepath, 'rb') as f:
        response = HttpResponse(f, content_type='application/vnd.ms-excel')
         # 파일like객체를 지정하면, 내부적으로 읽기를 시도하므로 굳이 f.read()를 호출할 필요 X
        
        encoded_filename = quote(filename)
        response['Content-Disposition'] = "attachment; filename*=utf-8 '' {}".format(encoded_filename)
        # 파일 첨부를 파일 다운로드 다이얼로그 형식으로 브라우저에 띄울려면 응답 헤더에 'Content-Disposition'
    return response

 

Pandas를 통한 CSV 응답 생성

import pandas as pd
from io import StringIO

def response_csb(request):
    df = pd.DataFrame([
        [100, 110, 120],
        [200, 210, 220],
        [300, 310, 320],
    ])

    io = StringIO()
    df.to_csv(io)
    io.seek(0) # 끝에 있는 파일 커서를 처음으로 이동

    response = HttpResponse(io, content_type='text/csv')
    response['Content-Disposition'] = "attachment; filename*=utf-8 '' {}".format(encoded_filename)
            
    return response

 

Pandas를 통한 엑셀 응답 생성

import pandas as pd
from io import StringIO

def response_csb(request):
    df = pd.DataFrame([
        [100, 110, 120],
        [200, 210, 220],
        [300, 310, 320],
    ])

    io = StringIO()
    df.to_excel(io)
    io.seek(0) # 끝에 있는 파일 커서를 처음으로 이동

    response = HttpResponse(io, content_type='application/vnd.ms-excel')
    response['Content-Disposition'] = "attachment; filename*=utf-8 '' {}".format(encoded_filename)
            
    return response

 

 

Pillow를 통한 이미지 응답 생성

import requests
from io import BytesIO
from PIL import Image, ImageDraw, ImageFont

def response_pillow_image(request):
    ttf_path = 'C:Windows/Fonts/Malgun.ttf' # 윈도우의 맑은고딕 폰트 경로

    # 이미지 파일 다운로드 혹은 로컬 디스크 상의 이미지 직접 열기
    image_url = ''
    res = requests.get(image_url) # 서버로 HTTP GET 요청하여, 응답 획득
    io = BytesIO(res.content) # 응답의 Raw Body 메모리 파일 객체 BytesIO 인스턴스 생성
    io.seek(0) # 파일의 처음으로 커서를 이동

    canvas = Image.open(io).convert('RGBA') # 이미지 파일을 열고, RGBA 모드로 변환
    font = ImageFont.truetype(ttf_path, 40) # 지정 경로의 TureType 폰트, 폰트 크기 40
    draw = ImageDraw.Draw(canvas) # canvas에 대한 ImageDraw 객체 획득

    response = HttpResponse(io, content_type='image/png')
    canvas.save(response, format= 'PNG') # HttpResponse의 file-like 특성 활용

    return response
반응형