Django 시작하기 #2 - View, Template

2021. 4. 11. 18:48Dev/Django

#View 만들기

View는 Django에서 Model 등을 관리하고 API를 만들고 사용하는 곳이다. NodeJS의 src/api 라고 생각할 수 있을 것 같다. 이곳에서 찾는 모델 또는 모듈을 불러오고, 해당 데이터를 처리한 후 return해주면 응답을 할 수 있다.

응답은 주로 HttpResponse를 통해 HTML로 반환하고, 요청은 HttpRequest를 통해 한다. 요청은 view의 클래스 또는 함수의 첫 번째 인자인 request를 통해 받을 수 있고, 이 request 정보를 바탕으로 Django는 데이터를 처리한 후 response를 해 줄 수 있다.

polls/views.py

#각각의 view가 호출되면, 웹페이지에 HttpResponse의 인자가 출력된다.
#즉, 라우팅을 통해 연결된 view의 내용이 return HttpResponse를 통해 출력된다.

def detail(request, question_id):
    return HttpResponse("You're looking at question %s." % question_id)

def results(request, question_id):
    response = "You're looking at the results of question %s."
    return HttpResponse(response % question_id)

def vote(request, question_id):
    return HttpResponse("You're voting on question %s." % question_id)

실제로 프로젝트를 진행하면서는 Generic View(추후에 개념 등장)를 더 많이 사용할테지만, Django의 라우팅 방식과 호출되는 함수를 이해하기 위해 HttpResponse를 통해 출력해 보고, 동작 원리를 파악하고 있다.

이후 작성한 view를 라우팅 하기 위해, polls/urls.py에서 path를 수정한다.

from django.urls import path

from . import views

app_name = 'polls' #이것은 프로젝트의 규모가 커졌을 때, 어떤 템플릿을 사용할 지를 결정하는 이름이다.
urlpatterns = [
    path('', views.index, name='index'),
    path('<int:question_id>/', views.detail, name='detail'),
    path('<int:question_id>/results/', views.results, name='results'),
    
    #예를 들어, localhost:포트/polls/3/vote/로 접속하면, 방금 만들었던 views.py의 vote의 HttpResponse가 출력된다.
    path('<int:question_id>/vote/', views.vote, name='vote'),
]

여기까지가 View의 기본적인 생성 방법 및 라우팅이다.

 

#View에 기능 추가하기

그럼 실제로 view가 비즈니스 로직을 처리하도록 해 보자.

http://localhost:8000/polls/ 에 접속했을 때, 가장 최신의 질문 리스트발행일에 대한 내림차순으로 5개만 출력해보도록 한다.

polls/views.py에서 index를 다음과 같이 수정한다.

from django.http import HttpResponse

from .models import Question


def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    output = ', '.join([q.question_text for q in latest_question_list])
    return HttpResponse(output)

다시 http://localhost:8000/polls/ 에 접속한다면 정상적으로 5개가 내림차순으로 출력되는 것을 볼 수 있는데, 질문 5개의 타이틀이 전부 오른쪽으로 붙어서 알아보기 힘들게 출력됨을 알 수 있다.

물론 프론트 페이지를 따로 만들어서 구현하면 보기 좋게 나올 수 있게 할 수 있지만,  Django는 기본적으로 template을 지원하여 마치 프론트 페이지처럼 script를 통해 페이지에 표시할 수 있다.

template을 생성하기 위해 polls에 templates 폴더를 만들자. 이후 안에 polls라는 폴더를 하나 더 만들고 index.html 파일을 만든다.

polls/templates/polls/index.html

{% if latest_question_list %}
    <ul>
    {% for question in latest_question_list %}
        <li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
    {% endfor %}
    </ul>
{% else %}
    <p>No polls are available.</p>
{% endif %}

이 템플릿은 latest_question_list가 존재한다면, 리스트 내의 질문들의 question_text를 리스트로 나타내주고, 해당 질문들의 question_text를 클릭 시 href를 통해 detail한 정보를 표시해주는 페이지로 이동하게 해준다.

다시 polls/views.py로 돌아가서 index를 수정한다

from django.http import HttpResponse

#template을 사용하기 위한 모듈 load를import
from django.template import loader

from .models import Question


def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    
    #template을 선언 후, 방금 만든 polls/index.html을 불러온다.
    template = loader.get_template('polls/index.html')
    #이후 index.html의 latest_question_list 값에 latest_question_list값을 넣고
    context = {
        'latest_question_list': latest_question_list,
    }
    #render하여 응답한다.
    return HttpResponse(template.render(context, request))

위처럼 만들 수도 있지만, render()를 사용하면 보다 쉽게 만들 수도 있다.

#render를 사용하기 위핸 render import
from django.shortcuts import render

from .models import Question

 #위와 동일하지만, template을 따로 선언하고 load하지 않아도 된다.
 
 #render는 3개의 parameter를 갖는데(3번째는 옵션이라 넣지 않아도 됨),
 #첫번째는 request,
 #두번쨰는 template의 이름,
 #세번째는 context 사전형 객체

def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    context = {
    'latest_question_list': latest_question_list
    }
    return render(request, 'polls/index.html', context)
    
    #물론 context를 굳이 선언하지 않고 아래와 같이 써도 무방하다.
    #return render(request, 'polls/index.html', {'latest_question_list': latest_question_list })

 

#View에 Error 처리하기

error 발생 시, error 처리를 해보자.

예를 들어,  detail한 view를 보고싶은데 해당하는 question_id를 가진 질문이 없을 때 404에러를 일으키도록 하자.

#404에러를 발생시키는 모듈 import
from django.http import Http404
from django.shortcuts import render

from .models import Question

# ...

def detail(request, question_id):
    try:
        question = Question.objects.get(pk=question_id)
    except Question.DoesNotExist:
        raise Http404("Question does not exist")
    return render(request, 'polls/detail.html', {'question': question})

더 간단하게 작성하고 싶다면, Django에서 제공하는 get_object_or_404()함수를 쓰면 된다.

from django.shortcuts import get_object_or_404, render

from .models import Question

# ...

#get_object_or_404()함수는 함수 이름에서도 알 수 있듯이, Question model에서 pk값이 question_id인 데이터를 가져오고, 없으면 404 에러를 리턴한다.
#첫 번째 인자 : model
#두 번째 인자 : 키워드, 즉 찾고자 하는 옵션

def detail(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    return render(request, 'polls/detail.html', {'question': question})

물론 나는 아직 polls/detail.html을 만들지 않았기 때문에 간단하게 만들어보도록 하자

polls/templates/polls/detail.html

<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
    <li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>

question의 내용을 제목으로 표시하고, 모든 choice들을 리스트의 형태로 표시한다.

 

#View에 연결된 URL 하드코딩 제거

실제로 프로젝트의 규모가 커진다면 여러 view가 있을 수 있고, 그 중 이름이 같은 view들이 있을 수 있다. 그런데 내가 템플릿의 URL에 하드코딩을 했고, 유지 보수를 생각한다면 그 템플릿들을 수정하고 변경했을 때 잘못된 view들이 호출될 수 있어 고생 좀 할 것이다.

이를 방지하기 위해, polls/urls.py에서 app_name이라는 값을 polls로 설정했다.

polls/templates/polls/index.html을 다음과 같이 변경하자

#기존의 내용을
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>

#아래와 같이 변경한다
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>

위 코드는 urlpatternsname = "detail"인 view에 question.id값을 넣어 url로 이동하는 코드인데 , 만약 동일한 이름을 가진 view가 다른 곳에 있다면 까다로워질 것이다. 따라서 urls.pyapp_name을 넣어 그 중 polls라는 app_name을 가진 urlpatterns의 name = "detail"인 url로 이동하게 할 수 있다. 이렇게 작성한다면 유지보수 및 개발 환경에서 편해질 것이다.

다음에는 Generic View를 이용 및 그 밖의 기능에 대해 공부해보자.

출처 : docs.djangoproject.com/ko/3.1/intro/tutorial03/

 

첫 번째 장고 앱 작성하기, part 3 | Django 문서 | Django

Django The web framework for perfectionists with deadlines. Overview Download Documentation News Community Code Issues About ♥ Donate

docs.djangoproject.com

 

'Dev > Django' 카테고리의 다른 글

Django REST Framework(DRF)로 회원가입/로그인/로그아웃 하기  (0) 2021.05.17
Django User DB 커스텀하기  (0) 2021.05.15
Django 시작하기 #1 - App, DBMS 설정  (0) 2021.04.05
Django란?  (0) 2021.03.30