Django tutorial project - 3

minch·2021년 7월 15일
1

Django

목록 보기
4/16
post-thumbnail

Django tutorial

Write views that actually do something

polls app에 다음 4가지 view를 추가하기 위해,

질문 《index》 페이지 – 최근의 질문들을 표시
질문 《detail》 페이지 – 질문 내용과, 투표할 수 있는 서식을 표시
질문 《results》 페이지 – 특정 질문에 대한 결과를 표시
투표 기능 《vote》 – 특정 질문에 대해 특정 선택을 할 수 있는 투표 기능을 제공

일단 아래와 같이 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를 polls.urls 모듈과 연결해준다.

polls/urls.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'),
]

이렇게되면, 서버를 실행시키고 《/polls/34/》 이 주소에 접속하면
You're looking at question 34.
detail() 함수를 호출하여 URL 에 입력한 ID 를 출력한다.

《/polls/34/results/》 와 《/polls/34/vote/》 도 마찬가지로,
You're looking at the results of question 34.
You're voting on question 34.
라는 화면이 각각 출력된다.

예를 들어 《/polls/34/》를 요청했다고 하면, 출력까지 다음과 같은 과정을 거친다.

  1. Django는 <mysite.urls> 파이썬 모듈을 불러오게 된다.
    ROOT_URLCONF 설정에 의해 해당 모듈을 바라보도록 지정되어 있기 때문이다.

  2. <mysite.urls>에서 urlpatterns라는 변수를 찾고, 순서대로 패턴을 따라간다.

  3. polls/를 찾은 후엔, 일치하는 텍스트("polls/")를 버리고, 남은 텍스트인 "34/"〈polls.urls〉 URLconf로 전달하여 남은 처리를 진행한다.

  4. 거기에 '<int:question_id>/'와 일치하여, 결과적으로 detail() 뷰 함수가 호출된다.

views.py를 조금 수정하여, 실제로 polls app의 역할을 하게 만들어보았다.

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

작성된 Question들 중에 작성 날짜를 내림차순으로 정렬하여 5개를 불러오는
즉, 최근 작성된 5개를 순서대로 출력하는 것이다.

하지만 이렇게 되면, 웹 페이지의 디자인을 바꾸기 위해서는 views.py의 index를 직접 수정해야하는 번거러움이 있다.

이를 해결하기 위해 Template를 사용할 수 있는데,
일단 아래와 같은 디렉토리에 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 %}

그리고 views.py를 다음과 같이 수정해주면,

polls/views.py

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

작성한 Template를 불러와서 답해주는 구조이다.

latest_question_list를 만들어서 그 데이터를 index.html Template에 전달을 하면, 이 데이터를 사용하여 그 형태로 값을 전달해 주는 것이다.

실제로 확인해보면,
전에 저장해놓았던 id=1 이고, 'What's up?'이라는 Question에 링크를 걸어서
<li><a href="/polls/1/">What's up?</a></li>
와 같이 출력되는 것을 확인 할 수 있다.

render( )

위와 같이 Template에 context를 채워넣어 표현한 결과를 HttpResponse 객체와 함께 돌려주는 구문은 자주 쓰는 용법이다.
따라서 Django는 이런 표현을 쉽게 표현할 수 있도록 단축 기능(shortcuts)을 제공하고 있다.

index() 뷰를 단축 기능으로 작성하면 다음과 같다.

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)

render( ) 함수는 request 객체를 첫번째 인수로 받고, Template 이름을 두번째 인수로 받으며, context 사전형 객체를 세번째 선택적(optional) 인수로 받는다.
인수로 지정된 context로 표현된 Template의 HttpResponse 객체가 반환된다.

Rasing a 404 error

Question의 detail에 질문과 답변 내용을 보여주게 만들어보자.

일단 detail 페이지의 template을 만들기 위해 아래와 같이 추가한다.

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의 text가 제목<h1></h1>으로 나오고
그에 해당하는 Choice의 text들이 <li></li>로 출력된다.

그리고 views.py 아래와 같이 내용을 수정해주면,

polls/views.py

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

datail.html template에 question을 지정해주고,
만약 요청된 질문의 ID가 존재하지 않을 경우 Http404 예외를 발생시킨다.

Http404 예외를 render()로 표현해줄 수 있는데,

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

이와 같이 작성하여도 정상적으로 Http404가 실행되는 것을 볼 수 있다.

get_object_or_404() 함수는 Django 모델을 첫번째 인자로 받고,
몇개의 키워드 인수를 모델 관리자의 get() 함수에 넘긴다.
만약 객체가 존재하지 않을 경우, Http404 예외가 발생하게 된다.

Removing hardcoded URLs

index.html template를 보면,

polls/index.html

<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>

이런식으로 하드코딩이 되어있는 것을 확인 할 수 있다.

url을 Template에 하드코딩 하게 되면 url 변경 시 Template에 있는 코드들도 변경해 주어야 한다.
이는 url 수정에 대한 어려움이 늘어나는 것으로 크게 도움이 되지 않는다.
Django에서는 url 마다 urls.py에 있는 name 을 명시하여 이를 해결하는데 도움을 준다.

polls/index.html

<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>

이처럼 url 코드에 name 을 명시해서 Template에 그 네임을 직접 써주면,
하드코딩 된 url이 변경되더라도 고유의 name을 갖고 있기 때문에
Template 안에있는 url의 변경을 할 필요가 없게 된다.

하지만 지금처럼 app이 polls 하나인 경우가 아니라,
여러 개의 app들에서 동일한 name의 url을 가지는 경우에는
{% url %} 이 어떤 앱의 뷰에서 URL을 명시하는지 알 수 없다.

이를 해결하기 위해 polls/urls.py에 app의 이름을 지정하고,

polls/urls.py

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'),
    path('<int:question_id>/vote/', views.vote, name='vote'),
]

기존 Template의 내용을 다음과 같이 수정하면 'polls:detail'과 같이,
특정한 이름을 가진 app의 name의 지정이 가능해진다.

polls/templates/polls/index.html

<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>

참조
(https://docs.djangoproject.com/ko/3.1/intro/tutorial03/)

1개의 댓글

comment-user-thumbnail
2021년 7월 16일

포스팅 감사합니다~!

답글 달기