2월 10, 2022

Django Tutorial : View

 4개의 뷰를 만듭니다.

각각은 다음과 같습니다.

색인(Index) - 최근의 질문 표시

세부(Detail) - 질문 내용, 투표할 수 있는 서식 제공 

결과(Result) - 특정 질문에 대한 결과 제공 

투표(vote) - 특정 질문에 대해 선택할 수 있는 기능 제공


urls.py의 path()호출로 view들을 polls.url로 연결합니다. 

이때, view에 페이지의 디자인이 하드코딩 돼 있다면, 페이지를 바꾸기 위해 python코드를 편집해야만 합니다. 따라서, 코드와 디자인을 분리하기 위하여 view에서 사용할 수 있는 템플릿을 작성해줍니다. 

polls디렉토리 내에 templates라는 디렉토리를 만들고, 그 안에 polls 디렉토리를 또 만들고, 그 안에 index.html을 만듭니다. 즉, 다음과 같은 구조입니다. 

polls/templates/polls/index.html

이는 DjangoTemplates가 각 설치된 app의 디렉토리의 templates하위 디렉토리를 탐색하기 때문입니다. ... 무슨 말일까요 이게?


mysite내에 app1 , app2 두개의 앱이 있다고 가정하면 장고가 인식하는 template폴더는 다음과 같습니다. 

  1. app1/templates
  2. app2/templates
  3. mysite/templates
1번 폴더에 없으면 2번폴더로, 2번에도 없으면 3번으로 이동하며 장고는 템플릿을 찾습니다.
근데 만약 찾으려는 파일이 app1/templates 안에도 있고, app2/templates안에도 있다면 원하는 파일을 찾아오지 못 할 수 있다는 문제가 생깁니다. 따라서 templates폴더 내부에 하위 폴더로 app이름의 폴더를 또 하나 만들어서 관리해주는 것이 편리한 것입니다. 

app1/templates/app1/test.html

이런 식으로 디렉토리를 만들어서 관리하게 되면, html파일 앞에 app 경로가 추가되기 때문에, 중복되어 로딩될 수 없습니다.

이러한 경로로 작성된 템플릿을 이용하여, view를 업데이트 할 수 있습니다. 

polls/templates/polls/index.html의 코드입니다

1
2
3
4
5
6
7
8
9
{% 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 %}

템플릿을 이용하여 index view를 업데이트 하면 다음과 같습니다. 이때 render()라는 구문이 사용됩니다. 이는 context(템플릿 변수명과 python 객체를 연결하는 딕셔너리 값) 를 채워넣어 표현한 결과를 HttpResponse 객체와 함께 돌려주는 구문입니다. 


1
2
3
4
5
6
7
8
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)
 
이번에는 detail() view를 업데이트 해줍니다.

get_object_or_404()를 사용하는데, get()을 사용하여 Http404 예외를 발생시키는 단축 기능입니다. 


1
2
3
4
5
6
7
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})
get_object_or_404의 첫 인자는 Django 모델이고, pk를 키워드 인수로 넘깁니다. 만약 객체가 존재하지 않는다면, Http404 예외가 발생합니다.

detail 템플릿을 확인해봅시다.
1
2
3
4
5
6
<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
    <li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>
{{ question.question_text }} 구문에서 django는 question 객체에 대해 dictionary형으로 탐색합니다, 만약 탐색에 실패하게 되면 속성값으로 탐색합니다. 속성 탐색에도 실패한다면, 리스트 인덱스 탐색을 시도합니다.

{% for %} 구문에서 question.choice_set.all()코드로 해석되는 question.choice_set.all은 Choice객체의 반복자를 반환합니다. 이를{% for %} 구문에서 반복자로 사용합니다.

앞서 index.html을 보면, 하드코딩(데이터를 코드 내부에 직접 입력함) 된 부분이 있습니다.
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
polls.urls 의 path()함수에서 인수의 이름을 정의했기에, 

{% url %} 템플릿 태그를 사용하여 url 설정에 정의된 특정 URL 경로들의 의존성을 제거합니다. 
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>




django는 URLconf에 namespace(이름공간)을 추가함으로써 여러가지 앱들의 URL을 구분합니다. polls/urls.py 파일에 app_name을 추가하여 어플리케이션의 이름공간을 설정합니다. 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
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'),
]
app_name = 'polls'를 추가해줍니다 
그 이후, 템플릿의 기존 내용에 namespace로 상세 view를 가리키도록 변경해줍니다.
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>

여기서 url 'detail'을 url 'polls:detail'로 바꿔주면 됩니다.