Django - Tutorial part 3 View, Template

24697 단어 djangodjango

이번 튜토리얼에서는 View를 만들어봅니다

View는 Django 어플리케이션이 일반적으로 특정 기능과 템플릿을 제공하는 웹 페이지의 한 종류입니다.

장고에서는 웹 페이지와 기타 콘텐츠가 뷰로 전달됩니다.
각 뷰는 함수, (클래스 기반 View의 경우 메서드)로 표현됩니다.

장고는 요청된 URL을 검토하여 보기를 선택합니다.

URL로부터 뷰를 얻기 위해, Django는 URLconfs 라는 것을 사용합니다.
URLconf는 URL 패턴을 뷰에 연결합니다.


polls/views.py에 새로운 뷰를 추가하겠습니다.

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

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

사용자가 만약 polls/45/을 요청하는 경우

  1. Django는 mysite.urls 파이썬 모듈을 불러오게 됩니다.
    왜냐하면 settings.py/ROOT_URLCONF 설정에 의해 해당 모듈(mysite.urls)을 바라보도록 지정되어 있기 때문입니다.
ROOT_URLCONF = "mysite.urls"
  1. mysite.urls에서 urlpatterns라는 변수를 찾고 polls/를 찾습니다.
urlpatterns = [
    path("polls/", include("polls.urls")),
    ...
]
  1. 일치하는 텍스트("polls/")를 버리고, 남은 텍스트45/를 〈polls.urls〉 URLconf로 전달합니다.
urlpatterns = [
    path('<int:question_id>/', views.detail, name='detail'),
    ...
]

남은 텍스트45/<int:question_id>/와 일치하여, 결과적으로 detail() 뷰 함수가 호출되게 됩니다.

호출된 detail() 함수입니다.

detail(request=<HttpRequest object>, question_id=45)`

question_id=45<int:question_id> 에서 왔습니다.
:question_id> 부분은 일치되는 패턴을 구별하기 위해 정의한 이름이며,
<int: 부분은 어느 패턴이 해당 URL 경로에 일치되어야 하는 지를 결정하는 컨버터입니다.

결과적으로 polls/45/로 요청하면 detail()함수가 실행되어 이런 응답을 줍니다.


이제는 뷰가 뭔가를 더하도록 만들어보겠습니다.

각 뷰는 두 가지 중 하나를 하도록 되어 있습니다.
요청된 페이지의 내용이 담긴 HttpResponse 객체를 반환하거나, Http404 같은 예외를 발생하게 해야합니다.

뷰는 데이터베이스의 레코드를 읽을 수도 있습니다. 또한 뷰는 Django나 Python에서 서드파티로 제공되는 템플릿 시스템을 사용할 수도 있습니다. 뷰는 PDF를 생성하거나, XML을 출력하거나, 실시간으로 ZIP 파일을 만들 수 있습니다. 뷰는 당신이 원하는 무엇이든, Python의 어떤 라이브러리라도 사용할 수 있습니다.

Django에 필요한 것은 HttpResponse 객체 혹은 예외입니다.

왜냐면 그렇게 다루는것이 편하기 때문입니다.

새로운 index()뷰를 작성하겠습니다.
index()는 시스템에 저장된 최소 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

여기서 몇가지 문제가 있습니다.
뷰에서 페이지의 디자인이 하드코딩 되어 있다고할 때, 페이지가 보여지는 방식을 바꾸려 합니다.
뷰에서 사용할 수 있는 템플릿을 작성해, python코드로부터 디자인을 분리하도록 Django의 템플릿 시스템을 사용해보겠습니다.

polls 디렉토리에 templates라는 디렉토리를 만듭니다.


settings.py의 TEMPLATES 설정을 잠깐 보겠습니다.
TEMPLATES 설정은 어떻게 템플릿을 불러오고 렌더링 할 것인지 기술해놓은 코드입니다.

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [],
        "APP_DIRS": True,
        "OPTIONS": {
            "context_processors": [
                "django.template.context_processors.debug",
                "django.template.context_processors.request",
                "django.contrib.auth.context_processors.auth",
                "django.contrib.messages.context_processors.messages",
            ],
        },
    },
]

DjangoTemplates은 각 INSTALLED_APPS 디렉토리의 《templates》 하위 디렉토리를 탐색합니다.

그래서 우리는 DjangoTemplates이 찾을 수 있게 polls 디렉토리에 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 %}

템플릿을 이용해 index뷰를 업데이트 해보겠습니다.

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)

polls/index.html 템플릿을 불러온 후, context를 전달합니다.

context는 템플릿에서 쓰는 변수명과 python객체를 연결하는 사전형 값입니다.

여기서 render()함수는 필수인자로 request와 template_name을 받고
선택인수로 context 사전형 객체를 받아 HttpResponse 객체를 반환합니다.


다음은 detail뷰를 업데이트 해보겠습니다.

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

polls/detail.html 템플릿에 다음과 같이 작성하겠습니다.

<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
    <li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>

이제 뷰는 요청된 질문의 id가 없을 경우 Http404예외를 발생시킵니다.

get_object_or_404() 함수는 Django 모델을 첫번째 인자로 받습니다.
몇개의 키워드 인수를 모델 관리자의 get() 함수에 넘깁니다. 만약 객체가 존재하지 않을 경우, Http404 예외가 발생합니다

상위 계층에서 ObjectDoesNotExist 예외를 자동으로 잡아 내는 대신 get_object_or_404() 도움 함수(helper functoin)를 사용하거나, ObjectDoesNotExist 예외를 사용하는 대신 Http404 를 사용하는 이유는 무엇일까요?
왜냐하면, 모델 계층을 뷰 계층에 연결하는 방법이기 때문입니다. Django의 중요한 설계 목표는, 약결합(loose coupling)을 관리하는 데에 있습니다. 일부 제어된 결합이 django.shortcuts 모듈에서 도입되었습니다.


템플릿에서 하드코딩된 url을 제거해보겠습니다.

polls/index.html에 가보면 다음과 같이 하드코딩되어있는 것을 볼 수 있습니다.

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

이러한 하드코딩의 문제는 수많은 템플릿을 가진 프로젝트들의 url을 바꾸는게 어려운 일이 된다는 점입니다.

하지만 우리는 polls/urls.py에서 name들을 설정해줬습니다.

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

다음과 같이 템플릿의 {% url %} 태그를 사용해 하드코딩된 url을 제거할수 있습니다.

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

이렇게 바꿔주면
만약 polls/12/ 에서 polls/specifics/12/ 으로 바꾸고 싶다면,
템플릿에서 바꾸는 것이 아니라 polls/urls.py에서 바꿔주면 됩니다.

# path('<int:question_id>/', views.detail, name='detail'),
path('specifics/<int:question_id>/', views.detail, name='detail'),

튜토리얼 프로젝트에서는 polls라는 앱 하나만 가지고 진행했습니다.
하지만 실제 Django 프로젝트는 앱이 몇개라도 올 수 있습니다.
여러개의 앱들의 URL을 구별하기위해 URLconf에 이름공간(namespace)를 추가하는 방법이있습니다.
urls.py파일에 app_name을 추가해 어플리케이션의 이름공간을 설정할 수 있습니다.

polls/urls.py파일에 app_name을 추가하겠습니다.

app_name = 'polls'
urlpatterns = [
    ...
]

이제 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>

좋은 웹페이지 즐겨찾기