[Django] tutorial #3-view

view 추가하기


뷰를 추가하기 위해 polls/views.py를 열어 다음 코드를 작성해준다.

- polls/views.py

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)

여기서 명심해야 할 것은
뷰에서는 클라이언트로 부터 request를 받게 되고, 다시 response를 반환 해준다는 것이다.

이때 request에는 많은 정보들이 담겨있다.
response를 해주기 전에 뷰에서는 데이터를 추출할 수도 있고 저장할 수 도 있으며, 웹에 맞는 다양한 처리를 해줄 수 있다.


추가해준 뷰를 호출하기 위해 polls/urls.py를 열어 url 코드를 작성해준다.

- 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/ 주소로 index 뷰가 호출이 되고,
호출 된 index 뷰가 "Hello, world. You're at the polls index." 라는 respose를 클라이언트에게 반환해주는 것이다.

다음은 장고에서 지원하는 url 패턴이다.
-<int:question_id>/
-<int:question_id>/results/
-<int:question_id>/vote/


여기서 주의해야할 점은
url 코드에 명시된 question_id는 뷰에 있는 파라미터 중 question_id와 일치해야한다는 점이다.



view가 실제로 뭔가를 하도록 만들기


현재까지는 index 뷰를 호출했을 때
"Hello, world. You're at the polls index."라는 str을 반환했다면,

이젠 시스템에 저장된 최소한 5개의 투표질문이 콤마로 분리되어, 발행일에 따라 출력되도록 해볼 것이다.

- 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

이제 index 뷰가 호출되면 클라이언트로 부터 request를 받아,
Question 데이터 중 출판일자(pub-date)를 정렬하여 5개까지 가져오고, 이를 콤마(,)로 연결하여 str으로 반환해주게 된다.



templates 분리하기


지금까지는 클라이언트에게 보여지는 페이지가 뷰 내에 있었다. 데이터를 추출하여 바로 클라이언트에게 반환해주는 방식인 것이다.

하지만 이러한 방식은 디자인 수정시 클라이언트에게 보여지는 페이지 수정이 매우 복잡해진다.
디자인을 수정하기 위해 뷰 내부를 수정해야하기 때문이다

따라서 내부로직 담당인 뷰와 디자인 담당인 템플릿을 분리시켜줘야 한다 !


우선 polls 디렉토리에 templates라는 디렉토리를 만들어준다.

이때 templates 디렉토리 내에 앱 이름(polls) 으로 디렉토리를 또하나 만들어 html파일을 관리해줘야 한다.

그 이유는 앱이름의 디렉토리를 또하나 만들어 주지 않으면 장고가 다른 앱의 템플릿과 구분하지 못하기 때문이다.

polls/
    migrations/
    templates/polls
    	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 %}

만들어준 템플릿을 이용하여 polls/views.pyindex 뷰를 업데이트 해준다

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

코드를 살펴보자면,
teplateload해서 respose를 반환해주는 것이다.

이때 context를 통해서 템플릿의 데이터를 전달해준다.
latest_question_list 데이터를 템플릿에 전달해주면 템플릿에서 해당 데이터를 사용하게 되는 것이다.

템플릿은 뷰로부터 데이터(latest_question_list)를 받아 리스트를 만들어서 클라이언트에게 보여준다 !



render()로 코드량 줄이기


render함수를 사용하게 되면 코드량을 줄일 수 있다.
장고에서는 정형화된 작업의 소스코드를 줄이기 위해 간단한 함수로 표현할 수 있도로 단축 기능(shortcuts)를 제공한다.

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



404 예외 일으키기


프로그래밍을 할때 에러를 얼마나 잘 처리하는 가는 중요한 부분이다.

detail 뷰에 에러를 발생시켜보려고 한다.

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

- polls/templates/polls/detail.html

{{ question }}

코드를 작성해준 뒤 없는 데이터를 조회하면 에러가 나타나면서 예외 메시지인 "Question does not exist"를 반환해준다



get_object_or_404()


데이터가 존재하지 않을때 get()을 사용하여 Http404 예외를 발생시키는 것은 자주 사용한다. 장고는 이 기능에 대한 단축 기능(shortcuts)을 제공한다.

방금 전 작성한 detail 뷰를 단축기능을 사용하면 다음과 같이 수정할 수 있다.

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



템플릿 시스템 사용하기


detail 뷰도 context 변수인 question이 주어졌을 때, polls/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>

이때 코드 해석을 해보자면, 다음과 같다

  • <h1>{{ question.question_text }}</h1> : 템플릿은 question을 넘겨받고 question 데이터 내의 question_text
    를 제목으로 보여준다
  • {% for choice in question.choice_set.all %} : question을 외래키(ForeignKey)로 받는 choice들을 모두 가져온다
  • <li>{{ choice.choice_text }}</li> : 가져온 choice를 리스트에다
    하나씩 넣어준다

코드 수정 후 서버를 확인해보면 question_text가 제목이 되고,
그 아래 choice의 모든 목록들이 보이는 것을 확인 할 수 있다.


템플릿에서 하드코딩된 url 제거하기


현재 index.html 파일의 url부분이 하드코딩 되어있다.

- polls/templates/polls/index.html

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

이렇게 될 경우 url 변경 시, 템플릿에 있는 url들도 일일히 변경해줘야 된다.

이를 개선하기 위해서
장고에서는 url마다 name을 명시해줄 수 있다.

url 코드에 name을 명시해주고, 템플릿에 그 name을 직접 써주게 되면
수정이 있어도 url의 고유네임이 있기 때문에 템플릿 내의 소스코드를 변경해줄 필요가 없게 되는 것이다 !


따라서 하드코딩된 url을 제거하기 위해 다음과 같이 코드를 수정해준다.

- polls/templates/polls/index.html

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

이때 해당 url의 고유네임은 polls/ulrs.py 파일에서 확인할 수 있다.

- polls/ulrs.py

...
# the 'name' value as called by the {% url %} template tag
path('<int:question_id>/', views.detail, name='detail'),
...



url의 이름공간 정하기


지금까지는 polls라는 앱 하나만 있었지만,
실제 프로젝트를 하면 앱이 여러개가 있을 수 있다.

polls 앱에서 detail이라는 뷰를 가지고 있고, 예를 들어 또 다른 blog 앱에서도 동일하게detail이라는 뷰를 가질 수 있다.

이때 방금 위에서 했던 것처럼
템플릿에서의 url 태그를 {% url %} 이렇게 해준다면, 장고는 어떤 앱의 뷰에서 생성된 url인지 구분할 수 없다.

따라서 장고가 앱마다의 url을 구분할 수 있도록,
다음과 같이 해당 앱에서 사용하는 url에 대해서 앱 이름을 명시해줘야 한다.

- 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'),
]

이름을 명시해준뒤 템플릿에도 추가해준다.

- polls/templates/polls/index.html

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



🔎 참고
디장고 공식문서
디장고 공식문서 강의자료

좋은 웹페이지 즐겨찾기