[Django] 장고의 기본 요소 익히기 - 조회와 템플릿

싱숭생숭어·2023년 4월 29일
0

Django

목록 보기
8/19
post-thumbnail

위 글은 점프 투 장고를 참고해 작성하였습니다.

이번 포스팅에서는 pybo 앱의 핵심기능인 질문 목록과 질문 상세 기능을 구현할 것이다.

질문 목록, 질문 상세

  • 질문 목록: 등록한 질문들을 게시물 목록으로 조회하는 기능
  • 질문 상세: 게시물 목록 중 한 건의 데이터를 상세하게 조회하는 기능

질문 목록

현재 '안녕하세요 pybo에 오신것을 환영합니다.'라는 문구가 나오는 http://localhost:8000/pybo/ 페이지를 요청 시 등록한 질문들을 조회할 수 있도록 pybo/views.py파일을 수정할 것이다.

from django.shortcuts import render
from .models import Question


def index(request):
    question_list = Question.objects.order_by('-create_date')
    context = {'question_list': question_list}
    return render(request, 'pybo/question_list.html', context)
  • 질문 목록 데이터(Question_list)는 Question.objects.order_by('-create_date')로 얻어옴. order_by는 조회결과를 정렬하는 함수로, order_by('-create_date')는 create-date 속성을 기준으로 역순 정렬이라는 의미 !(-가 앞에 붙으면 역순 정렬임)

  • render 함수는 파이썬 데이터를 템플릿에 적용해 HTML로 반환하는 함수

  • render() 함수는 request 객체를 첫번째 인수로 받고, 템플릿 이름을 두번째 인수로 받으며, context 사전형 객체를 세번째 선택적(optional) 인수로 받습니다. 인수로 지정된 context로 표현된 템플릿의 HttpResponse 객체가 반환됩니다. (장고 공식문서 참고)
  • 템플릿 파일은 HTML 파일과 비슷하지만 파이썬 데이터를 읽어서 사용가능한 HTML 파일

템플릿 디렉터리

이제 render 함수에서 사용한 pybo/question-list.html 템플릿 파일을 작성해야 함

템플릿 파일을 작성하기 전 템플릿 파일을 저장할 디렉터리를 config/settings.py파일의 TEMPLATES 항목에 설정해야 함

...
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [BASE_DIR / 'templates'],
        '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',
            ],
        },
    },
]
...
  • DIRS는 템플릿 디렉터리를 여러개 등록할 수 있도록 리스트로 되어있고, 파이보는 BASE_DIR / 'templates 디렉터리 한 개만 등록함

  • Base_DIR /'templates'에서 BASE_DIR(=앱 생성 위치)은 projects/mysite이므로 추가한 디렉터리의 전체 경로는 users/workspace/projects/mysite/templates 이다.

(mysite) gyu@DESKTOP-4OUGKIK:~/workspace/projects/mysite$ mkdir templates
(mysite) gyu@DESKTOP-4OUGKIK:~/workspace/projects/mysite$ 
  • mkdir templates를 통해 templates 디렉터리를 생성

  • 템플릿 = 화면을 구성하는 파일(안에 내용을 작성하면 화면에 표시됨)

template 파일 위치

  • 위에서 만든 mysite의 templates 파일은 여러 앱이 사용하는 공통 템플릿
  • 따라서 pybo 앱만의 templates 디렉터리를 생성해야함
    • projects/mysite/pybo/templates이 위치에 생성해도 되지만, 이 경우 하나의 웹사이트에서 여러 앱을 사용할때 여러 템플릿이 여러 앱 디렉터리에 나누어져 있어 관리에 불편하다.
    • 따라서 템플릿 파일들은 한 디렉터리에 모아 관리하는 것이 여러 모로 좋다 !
    • 여러 앱이 공통으로 사용할 템플릿 디렉터리 - projects/mysite/templates
    • pybo 앱이 사용할 템플릿 디렉터리 - projects/mysite/templates/pybo
    • common 앱이 사용할 템플릿 디렉터리 - projects/mysite/templates/common

템플릿 파일

위에 render 함수에서 사용한 템플릿 파일명은 pybo/question_list.html

따라서 question_list.html파일은 projects/mysite/templates/pybo/question_list.html경로에 생성해야 함

그리고 question_list.html파일 안 내용을 아래와 같이 작성

{% if question_list %}
    <ul>
    {% for question in question_list %}
        <li><a href="/pybo/{{ question.id }}/">{{ question.subject }}</a></li>
    {% endfor %}
    </ul>
{% else %}
    <p>질문이 없습니다.</p>
{% endif %}
  • {% 이나 %}로 둘러쌓인 문장들: 템플릿 태그라 함

  • 태그설명
    {% if question_list %}question_list가 있다면
    {% for question in question_list %}question_list를 순회하며 순차적으로 하나씩 question에 대입
    {{ question.id }}for문에 의해 대입된 question 객체의 id 번호를 출력
    {{ question.subject }}for문에 의해 대입된 question 객체의 제목을 출력
  • 템플릿에서 사용한 question_list는 render 함수로 전달한 "질문 목록" 데이터(아래 사진 참고)

템플릿 태그

장고에서 사용하는 템플릿 태그는 다음과 같이 3가지 유형

1. 분기

분기문 태그의 사용법은 다음과 같다.

{% if 조건문1 %}
    <p>조건문1에 해당되는 경우</p>
{% elif 조건문2 %}
    <p>조건문2에 해당되는 경우</p>
{% else %}
    <p>조건문1, 2에 모두 해당되지 않는 경우</p>
{% endif %}
  • 파이썬 if문과 비슷함. 다만 항상 {% else %} 태그로 닫아주어야 함

2. 반복

반복문 태그의 사용법은 다음과 같다.

{% for item in list %}
    <p>순서: {{ forloop.counter }} </p>
    <p>{{ item }}</p>
{% endfor %}
  • 파이썬의 for문과 비슷함. 다만 항상 {% endfor %} 태그로 닫아주어야 함

  • 템플릿 for문 안에서는 다음과 같은 forloop 객체를 사용 가능

  • forloop 속성설명
    forloop.counter루프내의 순서로 1부터 표시
    forloop.counter0루프내의 순서로 0부터 표시
    forloop.first루프의 첫번째 순서인 경우 True
    forloop.last루프의 마지막 순서인 경우 True

3. 객체 출력

객체를 출력하기 위한 태그의 사용법은 다음과 같다.

{{	객체	}}

ex) {{ item }}
객체에 속성이 있는 경우 파이썬과 동일한 방법으로 도트(.)문자를 이용해 표시

{{	객체.속성	}}

ex) {{question.id}}, {{question.subject}}

템플릿 문법에 대한 자세한 내용은 다음 URL을 참고

테스트

위의 question_list.html까지 수정하고 http://localhost:8000/pybo/ 페이지를 입력한 뒤, python manage.py runserver를 통해 장고 개발 서버를 구동해보자 !

  • 지난 포스팅의 장고 shell에서 만든 질문 1건과 admin 페이지에서 만든 질문 1건, 총 2건이 조회됨

질문 상세

페이지에서 뜬 질문 목록 중 한 개를 선택하여 클릭해보자

위와 같은 오류가 표시됨. 이유는 http://localhost:8000/pybo/3/과 같은 페이지에 대한 URL매핑이 아직 없기 때문 !

urls.py

질문 목록 화면에서 링크를 클릭하여 요청한 질문 상세 URL은 http://localhost:8000/pybo/3/

이는 id 값이 3인 Question을 상세 조회한다는 뜻으로, 이 URL이 동작할 수 있도록 pybo/urls.py를 다음과 같이 수정

from django.urls import path

from . import views

urlpatterns = [
    path('', views.index),
    path('<int:question_id>/', views.detail),
]
  • 맨 아래 path('<int:question_id>/', views.detail),라는 URL 매핑을 추가함

  • 이제 http://localhost:8000/pybo/3/ 페이지가 요청되면 위에서 적용한 매핑에 의해서 http://localhost:8000/pybo/<int:question_id>/ 가 적용되어 question_id 에 3가 저장되고 views.detail 함수도 실행될 것 !

  • <int:question_id> 에서 int는 숫자가 매핑됨을 의미함

views.py

위 URL 매핑 규칙에 의해 실행되는 views.detail함수를 만들어야 함

pybo/views.py를 다음과 같이 수정

...

def detail(request, question_id):
    question = Question.objects.get(id=question_id)
    context = {'question': question}
    return render(request, 'pybo/question_detail.html', context)
  • 전에 만든 index 함수와 크게 다른 부분은 없지만 detail 함수 호출 시 전달되는 매개변수가 request 외에 question_id가 추가됨

  • 여기서 매개변수 question_id는 3(question 모델에 할당되는 PK값)

  • id=question_id(여기에서는 3)인 question을 Question.object.get으로 가져와서 question 객체 안에 담음

  • 그리고 render() 함수로 파이썬 데이터를 템플릿에 적용해 HTML로 반환 !

question_detail.html

datail함수에서 사용할 'pybo/question_detail.html' 템플릿을 projects/mysite/templates/pybo 안에 생성하고 아래와 같이 작성

<h1>{{ question.subject }}</h1>
<div>
    {{ question.content }}
</div>
  • {{ question.suject }}{{ question.content }}의 question은 detail 함수에서 템플릿에 context 변수로 전달한 Question 모델 객체
    • 위에서 question = Question.objects.get(id=question_id) 이 부분 question

테스트

위의 question_detail.html까지 수정하고 http://localhost:8000/pybo/ 페이지를 입력한 뒤, python manage.py runserver를 통해 장고 개발 서버를 구동해보자 !

질문을 누르면 질문 상세가 화면에 출력된다 !


오류 페이지

이번에는 http://localhost:8000/pybo/30/ 페이지를 요청해보자

  • 그러면 DoesNotExist 오류가 발생! 이 오류는 전달된 question_is가 30이므로 위의 detail 함수에서 Question.object.get(id=30)이 호출되어 발생한 오류임. 이 때 브라우저에 전달되는 오류코드는 500

  • 오류코드설명
    200성공 (OK)
    500서버오류 (Internal Server Error )
    404서버가 요청한 페이지(Resource)를 찾을 수 없음 (Not Found)
  • 이렇게 없는 데이터를 요청할 경우 500 오류 보다는 "Not Found (404)" 페이지를 리턴하는 것이 바람직하므로, 500 페이지 대신 404 페이지를 출력하도록 pybo/views.py의 detail 함수를 수정해보자

from django.shortcuts import render, get_object_or_404
from .models import Question

...

def detail(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    context = {'question': question}
    return render(request, 'pybo/question_detail.html', context)
  • Question.objects.get(id=question_id)get_object_or_404(Question, pk=question_id)로 변경

  • 여기서 사용한 pk는 Question 모델의 기본키(question 모델에서는 question_id)에 해당하는 값을 의미함

  • get_object_or_404는 객체를 가져오거나 오류가 발생하면 404 에러를 반환

이렇게 수정하고 다시 http://localhost:8000/pybo/30/ 페이지를 요청해보면 500 대신 404 오류 페이지가 출력되는 것을 확인할 수 있다.

profile
공부합시당

0개의 댓글