소개
이 튜토리얼은 데이터베이스 설정 (Set Up the Database) 에서 중단된 부분부터 시작합니다. 웹 설문 애플리케이션을 계속 진행하며, 공개 인터페이스인 "뷰 (views)"를 생성하는 데 집중할 것입니다.
개요
뷰 (view) 는 Django 애플리케이션에서 특정 기능을 수행하고 특정 템플릿을 갖는 웹 페이지의 "유형"입니다. 예를 들어, 블로그 애플리케이션에서는 다음과 같은 뷰를 가질 수 있습니다.
- 블로그 홈페이지 -- 최신 항목 몇 개를 표시합니다.
- 항목 "상세" 페이지 -- 단일 항목에 대한 영구 링크 페이지입니다.
- 연도별 아카이브 페이지 -- 주어진 연도에 항목이 있는 모든 달을 표시합니다.
- 월별 아카이브 페이지 -- 주어진 달에 항목이 있는 모든 날짜를 표시합니다.
- 일별 아카이브 페이지 -- 주어진 날짜에 있는 모든 항목을 표시합니다.
- 댓글 액션 -- 주어진 항목에 댓글을 게시하는 것을 처리합니다.
설문 애플리케이션에서는 다음과 같은 네 가지 뷰를 갖게 됩니다.
- 질문 "인덱스" 페이지 -- 최신 질문 몇 개를 표시합니다.
- 질문 "상세" 페이지 -- 질문 텍스트를 표시하며, 결과는 없지만 투표를 위한 양식이 있습니다.
- 질문 "결과" 페이지 -- 특정 질문에 대한 결과를 표시합니다.
- 투표 액션 -- 특정 질문에서 특정 선택 항목에 대한 투표를 처리합니다.
Django 에서 웹 페이지 및 기타 콘텐츠는 뷰를 통해 제공됩니다. 각 뷰는 Python 함수 (또는 클래스 기반 뷰의 경우 메서드) 로 표현됩니다. Django 는 요청된 URL (정확히는 도메인 이름 뒤의 URL 부분) 을 검사하여 뷰를 선택합니다.
웹을 사용하면서 ME2/Sites/dirmod.htm?sid=&type=gen&mod=Core+Pages&gid=A6CD4967199A42D9B65B1B와 같은 아름다운 URL 을 접했을 수 있습니다. Django 는 이보다 훨씬 더 우아한 URL 패턴을 제공한다는 것을 알게 되어 기쁠 것입니다.
URL 패턴은 URL 의 일반적인 형식입니다. 예를 들어 /newsarchive/<year>/<month>/와 같습니다.
URL 에서 뷰로 이동하기 위해 Django 는 'URLconf'라고 알려진 것을 사용합니다. URLconf 는 URL 패턴을 뷰에 매핑합니다.
이 튜토리얼은 URLconf 사용에 대한 기본적인 지침을 제공하며, 자세한 내용은 /topics/http/urls를 참조하십시오.
뷰 추가 작성
이제 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)
다음 ~django.urls.path 호출을 추가하여 이러한 새로운 뷰를 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"),
]
이제 서버를 다시 실행합니다.
cd ~/project/mysite
python manage.py runserver 0.0.0.0:8080
Web 8080 탭으로 전환하여 /polls/34/를 입력합니다. detail() 메서드를 실행하고 URL 에 제공한 ID 를 표시합니다. /polls/34/results/ 및 /polls/34/vote/도 시도해 보세요. 이러한 URL 은 자리 표시자 결과 및 투표 페이지를 표시합니다.

어떤 사람이 웹사이트에서 페이지를 요청하면 (예: /polls/34/), Django 는 ROOT_URLCONF 설정에 의해 지정되어 있기 때문에 mysite.urls Python 모듈을 로드합니다. urlpatterns라는 변수를 찾고 패턴을 순서대로 탐색합니다. 'polls/'에서 일치하는 항목을 찾은 후 일치하는 텍스트 ("polls/") 를 제거하고 나머지 텍스트 ("34/") 를 추가 처리를 위해 'polls.urls' URLconf 로 보냅니다. 거기에서 '<int:question_id>/'와 일치하여 다음과 같이 detail() 뷰를 호출합니다.
detail(request=<HttpRequest object>, question_id=34)
question_id=34 부분은 <int:question_id>에서 가져옵니다. 꺾쇠 괄호 ("<>") 를 사용하면 URL 의 일부를 "캡처"하여 뷰 함수에 키워드 인수로 보냅니다. 문자열의 question_id 부분은 일치하는 패턴을 식별하는 데 사용될 이름을 정의하고, int 부분은 이 URL 경로의 일부와 일치해야 하는 패턴을 결정하는 변환기 (converter) 입니다. 콜론 (":") 은 변환기와 패턴 이름을 구분합니다.
실제로 무언가를 하는 뷰 작성
각 뷰는 두 가지 중 하나를 담당합니다. 요청된 페이지의 콘텐츠를 포함하는 ~django.http.HttpResponse 객체를 반환하거나 ~django.http.Http404와 같은 예외를 발생시키는 것입니다. 나머지는 여러분에게 달려 있습니다.
뷰는 데이터베이스에서 레코드를 읽을 수도 있고, 그렇지 않을 수도 있습니다. Django 의 템플릿 시스템과 같은 템플릿 시스템이나 타사 Python 템플릿 시스템을 사용할 수도 있고, 그렇지 않을 수도 있습니다. PDF 파일을 생성하거나, XML 을 출력하거나, 즉석에서 ZIP 파일을 만들 수 있으며, 원하는 Python 라이브러리를 사용하여 원하는 모든 작업을 수행할 수 있습니다.
Django 가 원하는 것은 ~django.http.HttpResponse입니다. 또는 예외입니다.
편의상 튜토리얼 2 에서 다룬 Django 자체 데이터베이스 API 를 사용해 보겠습니다. 다음은 시스템에서 최신 5 개의 설문 질문을 게시 날짜에 따라 쉼표로 구분하여 표시하는 새로운 index() 뷰의 한 가지 시도입니다.
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
하지만 여기에는 문제가 있습니다. 페이지의 디자인이 뷰에 하드 코딩되어 있습니다. 페이지의 모양을 변경하려면 이 Python 코드를 편집해야 합니다. 따라서 Django 의 템플릿 시스템을 사용하여 뷰가 사용할 수 있는 템플릿을 만들어 디자인을 Python 과 분리해 보겠습니다.
먼저 polls 디렉토리에 templates라는 디렉토리를 만듭니다. Django 는 거기에서 템플릿을 찾습니다.
프로젝트의 TEMPLATES 설정은 Django 가 템플릿을 로드하고 렌더링하는 방법을 설명합니다. 기본 설정 파일은 APP_DIRS <TEMPLATES-APP_DIRS> 옵션이 True로 설정된 DjangoTemplates 백엔드를 구성합니다. 관례에 따라 DjangoTemplates는 각 INSTALLED_APPS에서 "templates" 하위 디렉토리를 찾습니다.
방금 만든 templates 디렉토리 내에 polls라는 다른 디렉토리를 만들고, 그 안에 index.html이라는 파일을 만듭니다. 즉, 템플릿은 polls/templates/polls/index.html에 있어야 합니다. 위에서 설명한 대로 app_directories 템플릿 로더가 작동하는 방식 때문에 Django 내에서 이 템플릿을 polls/index.html로 참조할 수 있습니다.
템플릿 네임스페이스 (Template namespacing)
이제 템플릿을 polls/templates에 직접 넣는 것 (다른 polls 하위 디렉토리를 만들지 않고) 으로 해결할 수 있을지도 모르지만, 실제로 좋지 않은 생각입니다. Django 는 이름이 일치하는 첫 번째 템플릿을 선택하며, 다른 애플리케이션에 동일한 이름의 템플릿이 있는 경우 Django 는 이를 구별할 수 없습니다.
Django 가 올바른 템플릿을 가리키도록 해야 하며, 이를 보장하는 가장 좋은 방법은 *네임스페이스 (namespacing)*를 사용하는 것입니다. 즉, 해당 템플릿을 애플리케이션 자체의 이름을 딴 다른 디렉토리 안에 넣는 것입니다.
해당 템플릿에 다음 코드를 넣습니다.
{% 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 을 사용합니다. 자체 프로젝트에서는 완전한 HTML 문서를 사용해야 합니다.
이제 polls/views.py의 index 뷰를 업데이트하여 템플릿을 사용해 보겠습니다.
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))
이 코드는 polls/index.html이라는 템플릿을 로드하고 컨텍스트를 전달합니다. 컨텍스트는 템플릿 변수 이름을 Python 객체에 매핑하는 사전입니다.
서버를 다시 실행합니다.
python manage.py runserver 0.0.0.0:8080
브라우저를 "/polls/"로 가리켜 페이지를 로드하면 튜토리얼 2 의 "What's up" 질문이 포함된 글머리 기호 목록이 표시됩니다. 링크는 질문의 상세 페이지를 가리킵니다.

단축키: ~django.shortcuts.render
템플릿을 로드하고, 컨텍스트를 채우고, 렌더링된 템플릿의 결과로 ~django.http.HttpResponse 객체를 반환하는 것은 매우 일반적인 관용구입니다. Django 는 단축키를 제공합니다. 다음은 다시 작성된 전체 index() 뷰입니다.
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)
이러한 모든 뷰에서 이 작업을 수행했으면 더 이상 ~django.template.loader 및 ~django.http.HttpResponse를 가져올 필요가 없습니다 (아직 detail, results, vote에 대한 스텁 메서드가 있는 경우 HttpResponse를 유지하고 싶을 것입니다).
~django.shortcuts.render 함수는 첫 번째 인수로 요청 객체를, 두 번째 인수로 템플릿 이름을, 세 번째 인수로 선택적 사전을 사용합니다. 주어진 컨텍스트로 렌더링된 주어진 템플릿의 ~django.http.HttpResponse 객체를 반환합니다.
404 오류 발생시키기
이제 주어진 설문에 대한 질문 텍스트를 표시하는 페이지인 질문 상세 뷰를 살펴보겠습니다. 다음은 뷰입니다.
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})
여기서 새로운 개념은 다음과 같습니다. 요청된 ID 의 질문이 존재하지 않으면 뷰가 ~django.http.Http404 예외를 발생시킵니다.
polls/detail.html 템플릿에 넣을 수 있는 내용에 대해서는 잠시 후에 논의하겠지만, 위의 예제를 빠르게 작동시키려면 다음 내용만 포함하는 파일이 있으면 됩니다.
{{ question }}
지금은 이것으로 시작할 수 있습니다.
단축키: ~django.shortcuts.get_object_or_404
~django.db.models.query.QuerySet.get을 사용하고 객체가 존재하지 않으면 ~django.http.Http404를 발생시키는 것은 매우 일반적인 관용구입니다. Django 는 단축키를 제공합니다. 다음은 다시 작성된 detail() 뷰입니다.
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})
~django.shortcuts.get_object_or_404 함수는 Django 모델을 첫 번째 인수로, 임의의 수의 키워드 인수를 두 번째 인수로 사용하며, 이를 모델의 매니저의 ~django.db.models.query.QuerySet.get 함수에 전달합니다. 객체가 존재하지 않으면 ~django.http.Http404를 발생시킵니다.
왜 ~django.shortcuts.get_object_or_404 도우미 함수를 사용하여 상위 수준에서 ~django.core.exceptions.ObjectDoesNotExist 예외를 자동으로 포착하거나 모델 API 가 ~django.core.exceptions.ObjectDoesNotExist 대신 ~django.http.Http404를 발생시키지 않습니까?
그렇게 하면 모델 계층이 뷰 계층에 결합되기 때문입니다. Django 의 주요 설계 목표 중 하나는 느슨한 결합을 유지하는 것입니다. 일부 제어된 결합은 django.shortcuts 모듈에 도입됩니다.
~django.shortcuts.get_list_or_404 함수도 있으며, ~django.shortcuts.get_object_or_404와 동일하게 작동합니다. 단, ~django.db.models.query.QuerySet.get 대신 ~django.db.models.query.QuerySet.filter를 사용합니다. 목록이 비어 있으면 ~django.http.Http404를 발생시킵니다.
템플릿 시스템 사용하기
설문 애플리케이션의 detail() 뷰로 돌아가 보겠습니다. 컨텍스트 변수 question이 주어지면, polls/detail.html 템플릿은 다음과 같을 수 있습니다.
<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에 대한 딕셔너리 조회를 수행합니다. 실패하면, 속성 조회를 시도합니다. 이 경우 작동합니다. 속성 조회가 실패하면, 목록 인덱스 조회를 시도했을 것입니다.
메서드 호출은 {% for %}<for> 루프에서 발생합니다. question.choice_set.all은 Python 코드 question.choice_set.all()로 해석되어 Choice 객체의 반복 가능한 객체를 반환하며, {% for %}<for> 태그에서 사용하기에 적합합니다.
템플릿에서 하드코딩된 URL 제거하기
polls/index.html 템플릿에서 질문에 대한 링크를 작성했을 때, 링크가 다음과 같이 부분적으로 하드코딩되었던 것을 기억하실 겁니다.
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
이 하드코딩된, 긴밀하게 결합된 접근 방식의 문제는 템플릿이 많은 프로젝트에서 URL 을 변경하는 것이 어려워진다는 것입니다. 하지만, polls.urls 모듈의 ~django.urls.path 함수에서 name 인수를 정의했으므로, {% url %} 템플릿 태그를 사용하여 URL 구성에 정의된 특정 URL 경로에 대한 의존성을 제거할 수 있습니다.
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
이것이 작동하는 방식은 polls.urls 모듈에 지정된 대로 URL 정의를 찾는 것입니다. 'detail'의 URL 이름이 아래에서 정확히 어디에 정의되어 있는지 확인할 수 있습니다.
## the 'name' value as called by the {% url %} template tag
path("<int:question_id>/", views.detail, name="detail"),
설문 상세 뷰의 URL 을 다른 것으로 변경하고 싶다면, 예를 들어 polls/specifics/12/와 같이 변경하고 싶다면, 템플릿 (또는 템플릿들) 에서 변경하는 대신 polls/urls.py에서 변경합니다.
템플릿을 전혀 변경할 필요가 없습니다.
## added the word 'specifics'
path("specifics/<int:question_id>/", views.detail, name="detail"),
URL 이름 네임스페이스 지정하기
튜토리얼 프로젝트에는 polls라는 하나의 앱만 있습니다. 실제 Django 프로젝트에서는 5 개, 10 개, 20 개 이상의 앱이 있을 수 있습니다. Django 는 어떻게 그들 사이에서 URL 이름을 구별할까요? 예를 들어, polls 앱에는 detail 뷰가 있고, 블로그용으로 동일한 프로젝트의 앱에도 있을 수 있습니다. {% url %} 템플릿 태그를 사용할 때 Django 가 어떤 앱 뷰를 URL 에 대해 생성해야 하는지 알 수 있도록 하려면 어떻게 해야 할까요?
해답은 URLconf 에 네임스페이스를 추가하는 것입니다. polls/urls.py 파일에서 app_name을 추가하여 애플리케이션 네임스페이스를 설정합니다.
from django.urls import path
from . import views
app_name = "polls"
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.html 템플릿을 다음에서 변경합니다.
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
네임스페이스가 지정된 상세 뷰를 가리키도록 변경합니다.
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>

뷰 작성이 익숙해지면, Form Processing and Cutting Down Our Code를 읽고 폼 처리 및 제네릭 뷰에 대한 기본 사항을 알아보세요.
요약
축하합니다! 공개 인터페이스 뷰 생성 랩을 완료했습니다. LabEx 에서 더 많은 랩을 연습하여 실력을 향상시킬 수 있습니다.