[Django] 뷰(View) 시작하기 - 투표 앱 만들기(Django tutorial)
목차
- View 작성해 보기
- 멋진 동작을 하는 View로 바꿔보기
- 간결한 표현과 Exception 응답 작성
- 결론
Django에서 콘텐츠나 웹 페이지는 View로 전달됩니다. 각각의 View는 Python 함수로 되어있고 (때에 따라 클래스로 되어있음) URL 요청에 맞게 적절히 사용자에게 노출됩니다. 이번 포스팅에서는 이전에 만든 투표 모델을 View를 통해 사용자에게 전달하는 방법을 이해해 보겠습니다.
실습 전 python manage.py runserver
명령을 통해 개발 서버를 실행해 주세요.
1. View 작성해 보기
단순 View 작성
우선 polls/views.py
에 아래의 코드를 작성해 보도록 하겠습니다. 각각은 큰 의미를 가지기보다 단순하게 question_id를 입력으로 받아 그와 관련한 정보를 단순 출력해 주는 것입니다.
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)
URL 경로 생성
View를 보려면 URL이 있어야겠죠? polls/url.py
를 아래와 같이 수정해 봅시다
from django.urls import path
from . import views
urlpatterns = [
# ex: /polls/
path("", views.index, name="index"),
# ex: /polls/5/
path("<int:question_id>/", views.detail, name="detail"),
# ex: /polls/5/results/
path("<int:question_id>/results/", views.results, name="results"),
# ex: /polls/5/vote/
path("<int:question_id>/vote/", views.vote, name="vote"),
]
URL을 간략하게 설명해 보겠습니다. 만약에 /polls/34/
를 호출하는 상황을 가정해 보면, Django는 detail() 함수를 호출합니다. /polls/34/results/
and /polls/34/vote/
역시 마찬가지로 각각의 함수를 호출합니다. 이후에 그에 맞는 View, 즉, 웹 페이지를 보여주죠. URL 설정 중 <int:question_id>
부분이 있습니다. 이는 :
을 기준으로 왼쪽이 입력 형식이고 오른쪽이 입력 변수 이름입니다. 즉, question_id라는 이름을 가진 int 형 변수를 입력으로 받는다는 이야기입니다.
2. 멋진 동작을 하는 View로 바꿔보기
Django에서 각각의 View는 2 가지 동작 중 하나를 해야 합니다. HttpResponse를 반환하거나 Exception을 반환해야 합니다. 나머지 동작은 여러분이 작성하기 나름입니다. DB에 저장된 Question을 화면에 표시하도록 View를 구현해 보겠습니다.
View에서 Question 가져오기
앞서 모델은 Python API를 제공한다고 말씀드렸습니다. 이제 그 Python API를 통해 View에서 모델에 대한 정보를 알아보겠습니다. polls/views.py
에서 아래 코드를 수정해 보겠습니다.
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)
# Leave the rest of the views (detail, results, vote) unchanged
템플릿 작성
이전 포스팅을 충실히(?) 따라 실습하신 분이라면 하얀 화면에 무슨일이야?
라는 질문이 보이실 것입니다. 다른 것은 아무것도 추가하지 않았기 때문에 1 가지 투표에 관해서 보이는 것입니다.
그런데, 너무 하얀 느낌이죠? 화면, 즉, View가 polls/views.py
에 hard-coded(하드코딩) 되어 있기 때문에 그렇습니다. 이제 하드코딩 된 부분을 수정해 보겠습니다. 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 %}
그런데, 왜 하필 polls/templates/polls/index.html
경로일까요? 사실 우리는 Django 어디에도 이 경로가 View라는 것을 입력한 적이 없습니다. mysite/settings.py
를 보면 의문이 좀 풀릴 수 있습니다. setting에 TEMPLATES라는 변수가 있는데 이 TEMPLATES가 템플릿 즉, 우리가 만든 html을 관리하는 부분입니다. TEMPLATES의 APP_DIRS 옵션이 True로 설정되어 있는 것을 확인할 수 있습니다. 이 옵션이 INSTALLED_APPS 변수에 추가된 앱의 하위 디렉터리 중 templates 디렉터리를 자동으로 인식해 추가해 주는 옵션입니다. 따라서, 우리가 polls/templates를 추가하면 자동으로 Django가 인식할 수 있는 것이죠.
템플릿을 이용한 View 화면 출력
그럼 이제 하드코딩된 부분을 고쳐보겠습니다
from django.http import HttpResponse
from django.template import loader
from .models import Question
def index(request):
latest_question_list = Question.objects.order_by("-pub_date")[:5]
template = loader.get_template("polls/index.html")
context = {
"latest_question_list": latest_question_list,
}
return HttpResponse(template.render(context, request))
조금은 달라진 모습을 확인하실 수 있습니다.
3. 간결한 표현과 Exception 응답 작성
render() 함수를 이용한 간결한 구현
template을 통해 View를 작성하는 실습을 해봤습니다. 하지만 앞선 코드를 더 간결하게 표현할 수도 있습니다. Django가 제공하는 render 기능을 이용하면 더 간단하게 작성이 가능합니다. render() 함수는 내부적으로 template과 입력 파라미터를 통해 HttpResponse를 반환합니다.
from django.shortcuts import render
from .models import Question
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)
Exception 응답 구현
한 가지 마음에 걸리는 점이 있습니다. 바로 Django의 View는 2가지 응답 즉, HttpResponse 또는 Exception 중 하나를 반환해야 하는데 우리는 HttpResponse만 작성했습니다. Exception이 없을 리가 없는데 말이죠. 이제 Exception에 대한 응답을 작성해 보겠습니다.
아래의 코드는 question_id를 전달받았을 때 Question이 없다면 404 에러, 즉 너무나 유명한 404 NOT FOUND 코드를 반환하는 코드입니다. Question이 있다면 Question을 보여주고 아니면 404를 전달하도록 작성하였습니다.
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})
polls/detail.html 파일은 현재 없습니다. 간단하게 polls/templates/polls/detail.html
을 생성하여 아래의 코드를 입력해 봅시다.
{{ question }}
이후에 http://127.0.0.1:8000/polls/1/
와 같이 없는 id(저는 question을 하나만 입력하여 1번이 없습니다)를 파라미터로 전달하면 404 에러 페이지가 나오는 것을 확인할 수 있습니다.
get_object_or_404() 함수를 이용한 간결한 404 응답 구현
404도 간단하게 작성할 수 있습니다. 아래의 코드에서 get_object_or_404() 함수를 이용하여 DB에서 Object를 가져오거나 아니면 404 에러를 반환할 수 있습니다. 이를 통해 Exception을 조금 더 간단한 방식으로 구현할 수 있습니다.
from django.shortcuts import get_object_or_404, render
from .models import Question
# ...
def detail(request, question_id):
question = get_object_or_404(Question, pk=question_id)
return render(request, "polls/detail.html", {"question": question})
결론
이번 포스팅에서는 View를 작성하는 것을 실습했습니다. 이전에 만들었던 모델을 화면에 표시하는 기능을 구현하였고, 템플릿을 사용하여 화면을 동적으로 꾸미는 실습을 진행하였습니다. 이후에는 Django의 함수를 이용하여 조금 더 간단하게 구현하는 실습들을 진행하였습니다. 이러한 실습을 통해 여러분만의 화면, 기능을 구현할 수 있습니다.
도움이 되셨다면 공감 부탁드리겠습니다. 여러분의 공감이 정말 큰 힘이 됩니다.
감사합니다!