카테고리 없음

[Django] Document 따라 읽기(4) - 앱 작성(1)

안용감한호랑이 2023. 11. 7. 22:09

[백엔드/Django] - [Django] Document 따라 읽기(3) - admin 페이지

 

[Django] Document 따라 읽기(3) - admin 페이지

[백엔드/Django] - [Django] Documnet 따라 읽기(2) - View, Model, CRUD API(1) [Django] Documnet 따라 읽기(2) - View, Model, CRUD API(1) [백엔드/Django] - Django Document 따라 읽기(1) - Tutorial(설치, 데이터베이스 연결) [Django] Do

dog-foot-writen.tistory.com


개요

뷰는 일반적으로 특정 기능을 제공하고 특정 템플릿을 잦는 Django 애플리케이션의 웹 페이지 "유형"입니다.

Django에서는 웹페이지와 기타 콘텐츠가 View를 통해 전달됩니다. 각 View는 Python 함수(또는 클래스 기반 View의 경우 매서드)로 표시됩니다. Django는 요청된 URL을 검사하여 뷰를 선택합니다.

 

 

 

 

더 많은 View 작성

이제 polls/views.py에 몇 가지 View를 더 추가하겠습니다.

# polls/views.py

def detail(request, question_id):
    return HttpResponse("You're looking at question %s." % question_id)

def results(request, question_id):
    return HttpResponse(response % question_id)

def vote(request, question_id):
    return HttpResponse("You're voting on question %s." % question_id)

 

새로 작성한 View들을 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"),
]

 

 

 

위와 같이 작성해 주었다면 

  1. http://localhost:8000/polls/34/
  2. http://localhost:8000/polls/34/results/
  3. http://localhost:8000/polls/34/vote/

위 경로에 들어가면 mysite.urls.py 모듈이 ROOT_URLCONF 설정에 의해 polls/ 에서 일치하는 것을 찾은 후 나머지 텍스트 "34/"를 polls.urls.py로 전송하여 추가적인 처리를 수행합니다.

 

각각의 View는 요청된 페이지의 내용을 포함하는 HttpResponse object를 반환하거나 404 페이지와 같은 예외처리 작업을 수행하게 됩니다.

 

 

 

 

 

View 작성하는 여러 가지 방법

1.  

Django의 database api를 사용하여 게시 날짜에 따라 시스템의 최신 5개 설문 조사 질문을 쉼표로 구분하여 표시하는 View를 작성하겠습니다.

# polls/views.py
from django.http import HttpResponse

from .models. import Question


# Question의 object를 가져와 pub_date를 내림차순으로 정렬하여 5개만 가져온다.
# select * from question order by pub_date desc limit 5 와 동일합니다.
# 이후 리스트 컴프리헨션을 샤용하여 question_text 리스트를 만들고 ", "로 이어 붙이고 반환합니다.
# "q1, q2, q3, q4, q5"
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)

 

 

 

 

 

2. loader

하지만 위와 같은 코드에는 페이지 디자인이 View에 하드 코딩되어 있다는 문제가 있습니다. 페이지 모양을 변경하려면 이 Python 코드를 변경해야 합니다. 따라서 Django의 template 시스템을 사용하여 View가 사용할 수 있는 template을 생성하여 Python에서 디자인을 분리해 보겠습니다.

 

먼저 polls 디렉터리에 templates라는 디렉터리를 만들어 Django가 해당 디렉터리에서 템플릿을 찾도록 합니다. 

# polls/templates/polls/index.html
# 아래 HTML은 예시를 위해 불완전한 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.py의 index를 변경합니다.

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

 

위 코드는 호출된 템플릿(polls/index.html)을 로드하고 콘텍스트에 전달합니다. 콘텍스트는 템플릿 변수 이름을 Python 객체에 매핑하는 dictionary입니다.

브라우저에서 "polls/" 페이지를 로드하면 index View를 호출하게 되고 index View에서는 Question object를 가져와 latest_question_list에 담아 template에 전달합니다.

전달받은 latest_question_list는 index.html에서 사용되어 question.id에 매핑되게 됩니다.

 

 

 

 

 

3.  render

템플릿을 로드하고, 콘텍스트를 채우고 렌더링 된 템플릿의 결과를 HttpResponse object에 담아 반환하는 것은 일반적인 일로 Djagno는 shortcut을 제공합니다.

모든 View에서 이 작업을 완료하면 loader, HttpResponse를 가져올 필요가 없습니다.

render() 함수는 요청 개체를 첫 번째 인수로, 템플릿 이름을 두 번째 인수로, dictionary를 세 번째 인수(필수가 아님)로 사용합니다. 주어진 콘텍스트로 렌더링 된 주어진 템플릿의 HttpResponse object를 반환합니다.

 

render() 함수를 적용한 결과입니다.

# polls/views.py
from django.shortcuts import render

from .models import Question

def index(reuqest):
    latest_question_list = Question.objects.order_by("-pub_date")[:5]
    context = {"latest_question_list": latest_question_list}
    return render(request, "polls/index.html", context)

 

 

 

2, 3번 모두 같은 결과를 반환합니다. 

  • loader : template을 불러와 HttpResponse를 반환시킨다
  • render : loader + HttpResponse와 비슷한 개념으로 request를 받아 template을 포함하여 반환시킨다.

 

 

 

 

404 발생시키기

404 페이지는 http에서 파일을 찾지 못했을 때 나오는 오류 코드입니다.

특정 설문조사에 대한 질문 텍스트를 표시하는 페이지인 질문 세부정보 보기에서 요청된 질문이 없는 경우를 만들어 보겠습니다.

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

 

 

"http://localhost:8000/polls/2/"로 접속한다면 아래와 같은 페이지로 redirect 됩니다.

 

 

 

 

Django는 404페이지 또한 render와 같은 shortcut을 제공합니다. 해당 코드는 아래와 같습니다.

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

 

 

 

 

 

 

Template 시스템 사용

detail View로 돌아가 context 변수 question이 주어지면  polls/detail.html의 Template은 다음과 같습니다.

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

 

Template 시스템은 점 조회(".") 구문을 사용하여 변수 속성에 액세스 합니다.

위 HTML의 {{  question.question_text  }} 에서 Djagno는 object의 dictionary에서 먼저 찾은 후 없다면 attribute에서 찾습니다. 위의 경우에 해당합니다. 하지만 attribute에서도 없다면 list-index에서 시도하게 됩니다.

메서드 호출은 {% for %} 루프에서 일어납니다. question.choice_set.all은 Python 코드인 question.choice_set.all()로 해석되게 됩니다.

 

 

 

 

 

Template에서 하드코딩된 URL 제거하기

Template에 질문에 대한 링크를 작성할 때 polls/index.html링크는 아래와 같이 하드코딩 되었습니다.

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

 

이러한 방식은 템플릿이 많은 프로젝트에서 URL을 변경하기 어렵다는 문제가 있습니다. 하지만 polls.urls 모듈의 path() 함수에 name에 값을 지정했기 때문에 {% url %} Template 태그를 사용하여 URL 구성에 정의된 특정 URL 경로에 대한 의존성을 제거할 수 있습니다.

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

 

 

 

하지만 Django에서의 View 조회 때문에 다른 app에 detail이 있다면 여러 app들이 존재할 때 어떤 detail을 호출하는지 구별하기 어려울 수 있습니다. 따라서 Django에서는 URLconf에 네임스페이스를 추가하여 구분합니다.

# 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

{% if latest_question_list %}
    <ul>
    {% for question in latest_question_list %}
        <li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
    {% endfor %}
    </ul>
{% else %}
    <p>No polls are available.</p>
{% endif %}