장고에서 View는 특정기능과 템플릿을 제공하는 웹페이지이자 모듈이다. polls 앱에서는 아래와 같은 4개의 뷰를 가지고 있다.
from django.shortcuts import HttpResponse
def index(request):
return HttpResponse("Hello, world. You're at the polls index.")
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)
이렇게 생성된 새로운 View들은 polls.urls 모듈로 연결시켜 준다.
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'),
]
urlpatterns 리스트 중 2번째 항목은 views.py 안에 있는 detail()을 통하여 url에 입력한 ID 값을 출력하게 되고, 해당 url을 통해 투표 서식을 보게 된다. 결과적으로 detail()엔 아래와 같은 값이 전달된다.
detail(request=<HttpRequest object>, question_id=34)
앞에서 작성한 View는 polls에 필요한 기능들을 정의하긴 했지만, 실제로 투표와 관련해서 별도의 기능을 구현하지는 않았다. 따라서 앞의 4개 View가 실제로 무엇가를 하도록 하려면 다음과 같이 코드를 보완하여야 한다.
우선, 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/views.py에 index.html를 직접 로드해서 HttpResponse 객체로 리턴해 줍니다.
polls/views
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))
하지만 이 내용을 보다 축약하기 위한 단축기능인 render()를 이용하여, 아래와 같이 코드를 다시 작성합니다.
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)
질문내역(ID)가 없는 경우에 404 에러를 예외처리하기 위해 detail() 함수 코드를 아래와 같이 정리합니다.
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})
하지만 앞써 index() 함수에서와 유사하게 예외처리 또한 get_object_or_404 메소드를 통해 보다 간결하게 코드를 작성할 수 있습니다.
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})
템플릿에 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>
템플릿 문법을 이용하여 하드코딩된 url를 제거하여 코드를 아래와 같이 다시 고칠 수 있습니다.
{% if latest_question_list %}
<ul>
{% for question in latest_question_list %}
<!-- <li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li> -->
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
{% endfor %}
</ul>
{% else %}
<p>No polls are available.</p>
{% endif %}
장고 프로젝트에는 앱이 몇 개라도 추가될 수 있습니다. 앱들마다 어떻게 url를 구별할 수 있을까요? 정답은 URLconf에 이름공간(namespace)를 추가하는 것입니다. polls/urls.py 파일에 app_name을 추가하여 어플리케이션의 이름공간을 설정할 수 있습니다.
polls/urls.py
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'),
path('<int:question_id>/vote/', views.vote, name='vote'),
]
이제 기존 polls/index.html 파일의 내용을 아래와 같이 이름공간으로 나눠진 상세 뷰를 가르키도록 수정한다.
polls/templates/polls/index.html
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>