사용자가 URL 주소를 보내게 되면 django에선 URL 패턴이 뷰를 호출하고, 뷰가 모델을 통해 데이터를 처리하고 템플릿을 렌더링하여 사용자에게 결과를 보여준다. 이러한 과정을 MTV(Model-Template-View)  아키텍처라고 한다. 오늘은 뷰와 템플릿에 대해서 작성해보고자 한다. 질문 목록(question)과 그에 따른 대답 목록(chioce)을 여러 방법으로 화면에 표시하는 예시를 들어서 작성할 예정이며, 파일 위치는 다음과 같다.

 


MTV 용어 정리

  1. URL:
    • 사용자가 웹 페이지를 요청할 때 Django는 URL을 통해 요청을 라우팅
    • URL은 Django 프로젝트의 URL 패턴들이 정의된 파일 (일반적으로 urls.py 파일)에서 관리
    • 각 URL 패턴은 특정한 뷰 함수를 호출하도록 매핑
  1. View:
    • URL 패턴에 의해 호출되는 뷰 함수는 사용자 요청을 처리하고 적절한 데이터를 준비
    • 뷰는 이 데이터를 데이터베이스로부터 검색하거나 다른 외부 소스로부터 가져와서 처리
    • 이후, 뷰는 템플릿을 렌더링하여 동적인 HTML 페이지를 생성하거나 RESTful API를 통해 JSON 데이터를 반환
  2. Template:
    • 뷰에서 준비한 데이터를 가지고 템플릿이 동적인 HTML 페이지를 생성
    • 템플릿은 Django 템플릿 언어를 사용하여 데이터를 표시하고 웹 페이지의 외관을 디자인
    • 템플릿은 사용자에게 보여지는 최종 결과물을 생성하는 역할
  3. Model:
    • 모델은 데이터베이스의 구조를 정의하고 데이터를 조작하는데 사용
    • 뷰에서는 모델을 통해 데이터를 검색하고 저장할 수 있으며, 이 데이터를 가지고 작업을 수행

 

HTTP:// ~~~/ polls 입력시 질문 목록 전체 출력하기

views.py

def index(request):
	lastest_question_list = Question.objects.order_by('-pub_date')[:5] 
    context = {'question': lastest_question_list}
    return render(request, 'polls/index.html', context)

여기서 주의깊게 봐야할 것은 render()이다. render() 함수는 Django에서 템플릿을 렌더링하여 HTTP 응답을 생성하는 데 사용되는 함수이다. 위의 코드에서 render() 함수는 index.html 템플릿을 렌더링하고, lastest_question_list 변수를 템플릿에 전달하여 동적으로 생성된 HTML을 클라이언트에게 반환해준다.

index.html

{% if questions %}
<ul>
    {% for question in questions %}
        <li>{{question}}</li>
    {% endfor %}
</ul>
{% else %}
<p>no questions</p>
{% endif %}

상세페이지(detail)와 링크 만들기

urls.py

app_name = 'polls'

urlpatterns = [   
	...
    path('<int:question_id>/', views.detail, name='detail'),     
]

이때, app_name을 지정함으로써 이후 app이 많아질 경우 발생할 URL충돌을 방지할 수 있다.

views.py

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

여기서 get(pk=question_id)는 Primary Key를 question_id로 사용함을 뜻한다.

detail.html

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

<a>태그를 통한 링크 구현


404 에러 처리

위의 코드에서 요청을 이상하게 보낼 경우, 404에러가 떠야한다. 하지만 현재는 500에러는 띄울것이다. 따라서 이번에는 잘못된 요청이 들어올 경우 404에러를 띄우게끔 직접 에러 처리를 진행해볼것이다.

views.py

from models.py import *
from django.http import HttpResponse
from django.http import Http404
from django.shortcuts import render , get_object_or_404

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

에러 처리를 할땐 try:except를 사용해도 좋지만 get_object_or_404를 사용하면 더욱 간단하게 구현가능하다.


Choice 투표 구현해보기

urls.py

    path('<int:question_id>/vote/', views.vote, name='vote'),

views.py

...
def vote(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    try:
        selected_choice = question.choice_set.get(pk=request.POST['choice'])
    except (KeyError, Choice.DoesNotExist):
        return render(request, 'polls/detail.html', {'question': question, 'error_message': '선택이 없습니다.'})
    else:
        selected_choice.votes += 1
        selected_choice.save()
        return HttpResponseRedirect(reverse('polls:index'))
  1. reverse() 함수는 URL 패턴의 이름을 기반으로 해당 URL을 생성한다.
  2. HttpResponseRedirect: 이 함수는 클라이언트를 다른 URL로 리디렉션한다. HttpResponseRedirect 객체를 생성할 때, 인자로는 리디렉션할 URL을 받는다.

detail.html

<form action="{% url 'questions:questions_vote' question.id %}" method="post">
    {% csrf_token %}
    <h1>{{question.question_text}}</h1>
    {% if Error_message %}
    <p><strong>{{ Error_message }}</strong></p>
    {% endif %}
    {% for choice in question.choice_set.all %}
        <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{choice.id}}">
        <label for="choice{{ forloop.counter }}">
            {{choice.choice_text}}
        </label>
        <br>
    {% endfor %}
<input type="submit" value="Vote">
</form>

 이때 주의해야할 부분은 {% csrf_token %} 이다. 이는 투표의 값을 받아올 수 있게끔하는 토큰값으로 해당 토큰이 없으면 오류가 발생하니 꼭 포함시켜주자.