Jello's development blog

Jello's development blog

Django 튜토리얼 따라하기 3

View, Url 추가

polls/views.py에 내용을 추가해보자.

polls/views.py

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들을 사용하기 위해서 url과 연결시켜준다.

polls/urls.py

from django.conf.urls import url

from . import views

app_name = 'polls'
urlpatterns = [
    # ex: /polls/
    url(r'^$', views.index, name='index'),
    
    # ex: /polls/5/
    url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
    
    # ex: /polls/5/results/
    url(r'^(?P<question_id>[0-9]+)/results/$', views.results, name='results'),
    
    # ex: /polls/5/vote/
    url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),
]

app_name의 역할은 잠시 뒤에 설명하도록 하겠다.

이제 polls/urls.py 안의 urlpatterns를 살펴보자. /polls/5/의 url은 어느 view와 연결될까? 바로 detail view이다.

정규표현식에 많은 것들이 담겨있는데, ?P<question_id>의 question_id는 괄호 안에 매치될 문자가 넣어질 변수 이름이다. 그리고 [0-9]+는 0부터 9까지의 수가 하나 이상이 존재한다는 정규 표현식이다. 따라서 /polls/5/의 경우, 이에 매치되는 5detail view 함수의 question_id라는 매개변수로 들어가게 된다.

결국 detail view는 You're looking at question 5.라는 결과를 내놓게 된다.

index view에서 database 읽어들이기

polls/views.py의 index를 수정해보자.

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)
    
# 다른 View들은 그대로 놔둔다.

Question 모델을 불러와서, index view에 요청이 들어왔을 때, pub_date의 역순(-)으로 5개 까지만 정렬한 뒤에 latest_question_list에 저장한다.

그 뒤에 ,으로 이어붙인 뒤에 출력한다.

template 추가해서 꾸미기

지금까지는 페이지에 들어가면 그저 흰 페이지에 검은 글씨만 나왔다. 이제 HTML을 이용하여 조금 더 예쁜 페이지를 만들어보자.

polls app 디렉토리에 templates 디렉토리를 만들고, 그 안에 polls 디렉토리를 또 만든 뒤 index.html 템플릿을 생성한다. Django는 템플릿을 찾아가는 과정에서, 다른 app에 같은 이름의 템플릿이 있다면 구별하지 못하기 때문에 polls 디렉토리를 한 번 더 만들도록 한다.

polls/templates/polls/index.html

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

그리고 index view도 수정하도록 하자.

polls/views.py

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)

아까 설명하지 못했던 app_name의 역할이 여기서 나온다. 템플릿의 url 'polls:detail' question.idpolls app 내의 detail view에 question.id의 값을 넣은 url을 반환해준다(question.id가 5일 경우, /polls/5를 반환). 여기서 app_name을 polls라고 설정해주었기 때문에 Django에서 구별할 수 있는 것이다. 지금은 polls:를 쓰지 않아도 무관하지만, 프로젝트가 커져서 이름이 중복될 경우를 미리 방지할 수 있다.

이제 view는 이전처럼 그대로 출력하지 않고, index.html이라는 템플릿으로 context 안의 내용을 전달하고 있다.

404 띄우기

detail view를 수정해서 특정 question을 읽어오도록 하고, 찾지 못하면 404페이지를 띄우게 해보자.

polls/views.py

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})

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>

polls/views.py에서 get_object_or_404로 해당 Question 모델을 찾고, 템플릿으로 전해주고있다.

polls/templates/polls/detail.html에서 question.choice_set.all은 Python 코드의 question.choice_set.all() 함수와 동일하며, for 문을 간편하게 쓰기 위해 존재한다. 이는 해당 Question 모델을 foreign key로 갖는 Choice 모델을 for문으로 쓸 수 있게(iterable) 반환한다.