뷰는 장고 앱의 로직을 담당함 데이터 저장, 다운로드 라든지 하는 것을 처리하기 위해 코드를 작성하게 된다.
색인(index) 세부(detail) 결과(result) 투표(vote) 기능이라는 네가지 뷰를 작성해 보자.
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)
명심해야 될 점이 있다. 뷰에서는 Request 라는 인자를 받고 HttpResponse라는 함수를 이제 리턴하게 되는데,
클라이언트로부터 Request를 받게 되면 이때 Request 에는 여러가지 정보들이 담겨 있을 것이고 다시 Response를 해준다 라는 것만 명심해 주면 되겠다.
리스폰스를 해 주기 전에 뷰에서는 데이터를 추출하게 될 것이고 데이터를 저장하게 될 것이고 또는 파일을 다운로드를 하기 위한 로직을 작성도 하게 될 것이고 이런 코드들이 중간에 이렇게 들어가는 것이지,
기본 개념은 클라이언트로부터 리퀘스트를 받아가지고 리스폰스 해준다
라고 알고 있으면 되겠다.
뷰를 호출하기 위해 poll/urls.py 코드를 작성하자.
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 라고 호출하게 되면 이제 패턴대로 뒤에 아무것도 없으니까 views.index 라는 view 를 호출하게 된다.
그러면 이 해당 주소로 인덱스 뷰가 호출이 되고 이 해당 주소로 (/polls/) 호출이 되고 views.py 에서 보이듯 이제 이 뷰가 호출이 되어서 헬로 월드 라는 문자열이 화면에 보이게 된다.
그밑에 이어서 보면 퀘스천 아이디로 5를 전달해 줬다 그러면 상세페이지 에 대한 뷰를 호출하게 되고.
“you are looking at question %s.” % question_id 5번 해가지고 이러한 스트링을 리스폰스 해주게 된다.
리스폰스 준다는 것은 클라이언트에게 전달해준다는 것을 의미한다.
클라이언트는 이제 그러면 이 문자를 화면으로 볼 수 있다.
마지막 아래를 보면 퀘스천 아이디 5번/result 이렇게 호출하게 되면 result view 를 호출하게 되고 vote 까지 명시해 주면 view 에 있는 vote 를 호출 할 수 있게 된다.
int:question_id 이런거는 장고에서 지원하는 url 패턴이다.
명심할 것이 저 패턴에 걸리게 되면 바로 뒤에 views 를 호출을 하겠다를 의미한다. 그리고 이 네임 디테일이 명시되어 있는 내용은 조금 이따 템플릿을 작성하게 될텐데 url 패턴을 명시해 주는 대신 디테일 이라고 작성을 해주면 된다. 뒤에서 좀더 자세히 다룰예정.
한 가지 짚고 넘어갈 게 퀘스천 아이디라 하는 건 뷰에잇는 파라미터 중 퀘스천 아이디와 일치가 되어야 겠다.
urls.py 에서 question_id 5번을 받으면 view 에서는 question_id 5번에 받아가지고 작업을 처리 해 준다.
각 뷰는 두 가지 중 하나를 하도록 되어 있습니다. 요청된 페이지의 내용이 담긴 HttpResponse 객체를 반환하거나, 혹은 Http404 같은 예외를 발생하게 해야합니다. 나머지는 당신에게 달렸습니다. 라고 장고 튜토리얼 3에 쓰여 있는데 아무튼.
이 두 가지는 반드시 리턴되게 되어 있다. 반환하거나 예외가 발생 하거나.
지금은 httpresponse 해가지고 스트링만 출력이 되는데 여기 이미 views.py 에 명시해준 스트링이 있다. hello world 어쩌고 저쩌고..
헬로 월드 대신 Question 을 직접 가지고 와서 Question 을 출력 하도록 하자.
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
polls.py 를 수정하는데..
코드 내용은 Question 데이터 중에서 출판일자를 정렬하여 다섯개까지만 데이터를 가지고 오게 되고 그리고 이 5개를 output 에 보듯이 콤마로 연결하겠다 라는 의미. 콤마로 연결해서 스트링으로 만들게 되면 문자열로 만들게 되고 이 문자열을 다시 리스폰스 하겠다 라는 의미.
코드를 작성 했으니 서버를 구동해서 확인을 해보도록 하자.
로컬호스트 8000/polls/ 를 들어가게 되면 what’s up 이렇게 퀘스천이 하나 나오게 된다.
polls 해가지고 클라이언트가 서버한테 요청을 했고(urls.py)
요청받은게 인덱스 뷰 (views.index) 로 전달되었다.
views.py 에 있는 index 에서는 데이터를 뽑아내서 리스폰스하게 되면 클라이언트는 What's up? 이라는 이렇게 Question 문구를 볼 수 있게 되었다.
지금까지는 클라이언트 에게 보여주는 페이지가 뷰 내에 있었다. 데이터를 추출해서 바로 클라이언트에게 보여주었다.
하지만 디자인 수정 시 클라이언트에게 보여지는 페이지 수정이 이렇게 되면 너무 어렵다.
뭐가 어렵냐면, 디자인을 수정하기 위해서는 views.py 의 내부 index 를 수정해야 되기 때문이다. 그래서 내부 로직 담당인 view 와 디자인 담당인 templates 를 분리해야 된다. 앞으로 코드를 작성할 때 염두에 두어야 할 게 어떻게 하면 수정 영향 범위를 줄일 수 있을까 고민해 봐야 한단다.
그럼 템플릿을 작성해보고 views.py 소스도 수정하여 템플릿과 연결해 보도록 하겠다.
템플릿 디렉토리 위치는 polls 안에 템플릿 디렉토리 위치를 만들어 주면 되는데 템플릿 디렉토리 내에 앱 이름으로 (polls) 디렉토리를 또 하나 만들어 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 %}
이러한 구분 없이 템플릿 안에 그냥 html 파일을 만들게 되면 장고는 다른 앱의 템플릿과 구분을 할 수가 없다.
그래서 앱 이름으로 디렉토릐를 만들어 관리를 해야 한다.
만든 index.html 에 코드 입력을 위와 같이 마치고,
polls/views.py 에 index 뷰를 업데이트 해보자.
기존 썼던 def index 코드는 주석 처리 하고 나와있는 임포트모듈 세개와 def index 를 새로 업데이트 하자.
views 를 보게 되면 템플릿을 로드해서 리스폰스 해주면 되는 구조이다.
여기서 눈여겨 봐야될 것은 context 이다.
context 를 통해서 템플릿에 데이터를 전달해 주는데 latest_question_list 해가지고 데이터를 템플릿 에다가 전달을 하며 템플릿에서 이 데이터를 사용하게 될 것이다.
index.html 을 보듯이 말이다.
이걸 뷰로 부터 받아가지고 리스트를 만들어서 화면에 보여지게 되겠다.
화면으로 우선 보자. 화면을 보게 되면 질문이 하나밖에 없어 하나만 나오고 질문이 여러개 있다면 가지고 와서 views.py 에서 보이듯 컨텍스트 안의 데이터를 밀어 넣어 주고 이 데이터를 템플릿을 활용하여 템플릿 내에서 이 데이터가 보여지게 되겠다.
question id 가 잘 전달되었는지 궁금하다면 f12 를 눌러서 ul, li 눌러 보면 잘 전달이 된 것을 볼 수 가 있겠다.
렌더 함수를 사용하게되면 코드 양을 줄일 수 있는데 숏컷 이라고 해서 정형회된 작업은 소스코드를 줄이기 위해서 간단한 함수로 제공이 된다, 렌더를 사용하게 되면 이 loader 그리고 httpresponse 함수를 import 하지 않고 render 라고 명시를 한 다음에 한줄로 작성해주면 되겠다.
하지만 내 경우에는 httpresponse 함수를 import 하였다.
다시 polls/views.py 에서 작성한 것들을 주석처리하고
새로운 코드를 def index 에 갖다 넣어보자.
장고는 리로딩이라는 기능이 있어서 변경되는 내용이 있으면 다시 이렇게 리로딩하여 변경된작업이 적용된 것을 볼 수가 있다.
shortcut import render 도 함께 적용시킨다.
이제 렌더가 적용되어 작성된 것을 확인 할 수가 있다.
자 이제 에러와 관련된 내용이 있다.
프로그래밍 했을 때 관건 중 하나가 얼마나 에러 처리를 잘 아느냐 이다. 이 경우에는 question의 데이터가 없는 경우 그러니까 이 question id 를 전달을 받고 question 을 조회를 했을 때 데이터가 없으면 question does not exist 이라고 보여지겠다.
주의할 점은 polls/views.py 의 디테일 내용을 수정하면 되겠다. 기존 디테일 내용은 주석처리하고 진행한다.
detail.html 템플릿도 수정을 해보도록 하자.
{{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>
없는 아이디를 조회했을 때 어떻게 에러가 발생하는지 보도록 하자.
127.0.0.1:8000/polls 에서 질문을 눌러보면
127.0.0.1:8000/polls/1 아이디 1 은 이렇게 데이터가 존재 하고 2 3. 4 5 는 없으니 http 404 에러가 되어 있어 다시 Http404를 response 옆에다가 import 를 하도록 하자.
이제 f12 를 눌러보면
없는 데이터를 조회하게 되면 question does not exist 라고 우리가 처리한 예외 메세지를 볼 수 가 있다.
http 404 예외처리를 데이터를 조회할 때 자주 쓰이는 문법인데 이 기능을 장고에서 shortcut 으로 제공하게 된다.
try except 처리 없이 shortcut 을 사용하여 코드를 작성해 보도록 하자.
import render 옆에 get_object_or_404 넣어주고
polls/views.py 내에서 def detail 내 주석처리 다 해주고 지름길 이라는 그 코드를 입력해 넣어 준다.
이렇게 숏컷 코드를 사용해도 동일하게 작동하는 것을 볼 수가 있겠다.
detail 뷰 에서도 context 변수에 question 을 같이 넣어 주었다.
question을 어떻게 사용하는지 이제 볼텐데 소스 코드를 작성하고 동작하는 모습을 본 이후에 적어보도록 하자.
olls/templates/polls/detail.html 을 수정해 보자.
기존의 {{question}} 지우고 html 코드를 넣어 보도록 한다.
템플릿의 question 을 넘겨 받고 question 내의 question text 를 < h1 > 태그의 제목으로 보여질 것이다.
런서버 돌려서 확인해보면 h1 태그로 감싸진 제목이 이렇게 보여진다. 8000/polls/1
그리고 이제 question 을 외래 키로 사용하는 choice 들이 있다.
polls/models.py 파일을 다시 보도록 하자.
question 을 이제 모델로 사용하는 choice 들이 있는데,
detail.html 에서 보이듯 question 오브젝트 choice 셋 이렇게 하면 초이스들이 이제 이 question 을 foreignkey 로 갖고 있는 모든 걸 지금 가지고 와라 이런 내용이 된다.
다시 한번 question은 What's New? 라는 question이 하나 있고, 이 What's New?에 대해서 외래키를 갖는 초이스들을 다 가지고 와라 라는 코드 이다.
현재는 이 왓스뉴에 대해서 이제 foreignkey 를 갖는 초이스들이 없기 때문에 지금 리스트는 보이지 않는다. polls/admin.py 들어가서 초이스를 추가를 시키고 화면을 다시 보도록 하자.
choice 도 임포트 해주고,
admin.site.register(Question)
admin.site.register(Choice)
이렇게 바꿔주고 choice 들을 웹 상에서 추가를 해봐주자.(런서버돌려가지고)
Choice 들을 가지고 온 다음에 리스트에다가 하나씩 하나씩 하나씩 뿌려 주면 되는 느낌이라 한다.
templates 내 index.html 을 보면
href=“” 해가지고 url 이 하드코딩이 되어 있다.
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
이 url 이 변경되는 경우가 있다. url 을 템플릿에 하드코딩 하게 되면 url 변경 시 템플릿에 있는 코드들도 변경해 주어야 한다.
조금 전 이야기 했듯이 우리는 어떻게 하면 수정에 대한 영향 범위를 줄일 수 있을까 라는 고민을 해야 되는 데 장고에서는 url 마다 name 을 명시할 수가 있다.
url 코드에 name 을 명시해서 템플릿에 그 네임을 직접 써 주게 되면 하드코딩된 유알엘이 변경되더라도 고유네임을 갖고 있기 때문에 템플릿 url 에 대해서는 소스 코드 변경을 할 필요가 없게 된다.
그럼 풀 url 대신 url을 명시한 네임을 템플릿에 명시해 보도록 하자.
index.html 에서 url ‘detail’ ~~~ 이렇게 바꾼다.
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
이러한 강력하게 결합되고 하드코딩된 접근방식의 문제는 수 많은 템플릿을 가진 프로젝트들의 URL을 바꾸는 게 어려운 일이 된다는 점입니다. 그러나, polls.urls 모듈의 path() 함수에서 인수의 이름을 정의했으므로, {% url %} template 태그를 사용하여 url 설정에 정의된 특정한 URL 경로들의 의존성을 제거할 수 있습니다.
다른 앱에서도 detail 에 대한 url 네임을 이렇게 사용할 수 있기 때문에 (mysite - urls.py 를 보면),
다른 앱을 또 만들 수도 있고 다른 앱에서도 이 디테일을 사용할 수 있기 때문에 name=‘detail’ 이렇게 표시를 해준다.
해당 앱에서 사용하는 url 에 대해서는 앱 이름을 명시를 해 주면 되겠다. app_name = ‘polls’
이름을 위와 같이 이렇게 명시를 해 주고 이제 템플릿에 추가를 해 주면 된다.
index.html 에서 polls:detail 로 변경한다.
화면에 돌아가 정상동작하는 걸 보자.
인덱스가 나오고 해당 유알엘을 볼수있고 상세페이지를 볼수가 있다.
디자인과 폼에 대한 내용은 없었지만 간단한 앱을 만들어 볼 수 있었다.