221010_점프투장고 2-07 ~ 2-10

Csw·2022년 10월 10일
0

Django

목록 보기
11/14
post-custom-banner

🌉 점프 투 장고 사이트를 참고하여 일정 단위의 chapter에 대해 실습 후 새롭게 알게된 내용, 혹은 복습이 꼭 필요한 내용에 대해 정리!!

🌞 2-07. 스태틱


🌈 템플릿에 스타일 적용

  • 작성한 스타일시트 파일을 질문 상세 템플릿에 적용하는 방법
    • html 문서 상단에 하단의 코드 추가
    • 템플릿에 스타일시트와 같은 스태틱 파일을 사용하기 위해서는 템플릿 최상단에 {% load static %}태그를 먼저 삽입해야 함.
    • 그래야만 {% static ... %}와 같은 템플릿 태그를 사용 가능.
{% load static %}
<link rel="stylesheet" type="text/css" href="{% static 'style.css' %}">

🌞 2-08. 부트스트랩

  • 부트스트랩(Bootstrap)은 디자이너의 도움 없이도 개발자 혼자서 상당히 괜찮은 수준의 웹 페이지를 만들수 있게 도와주는 프레임워크.
  • 부트스트랩은 트위터(Twitter)를 개발하면서 만들어졌고 현재 지속적으로 관리되고 있는 오픈소스 프로젝트

🌈 부트스트랩 설치

🚦 부트스트랩 주의사항

  • 부트스트랩은 3.x, 4.x, 5.x 등의 버전이 존재하고 메이저 번호(3, 4, 5)에 따라 그 사용방법이 다름.
  • 이 책은 부트스트랩 버전 5 기준으로 실습을 진행.
  • 다른 부트스트랩 버전을 사용하면 이 책의 예제가 정상 동작하지 않음.
  • 압축파일 안에는 많은 파일들이 있는데 이 중에서 bootstrap.min.css 파일과 bootstrap.min.js 파일을 카피하여 static 디렉터리에 저장

🌈 부트스트랩 적용

  • 먼저 질문 목록 template에 부트스트랩을 다음처럼 적용

📄 /templates/pybo/question_list.html

{% load static %}                                                                 # 추가
<link rel="stylesheet" type="text/css" href="{% static bootstrap.min.css' %}">    # 추가
{% if question_list %}
(... 생략 ...)
  • 이제 부트스트랩 style을 적용 했으니 template도 부트스트랩을 사용하도록 다음과 같이 수정
    • 기존에는 <ul> 태그로 심플하게 작성했던 질문 목록을 테이블 구조로 변경.
    • 그리고 번호와 작성일시 항목도 추가함.
    • 번호는 for 문의 현재 순서를 의미하는 {{ forloop.counter }}를 이용.
    • 여기서 사용한 class="container my-3", class="table", class="table-dark" 등은 부트스트랩 스타일에 정의되어 있는 클래스들.

📄 /templates/pybo/question_list.html

{% load static %}
<link rel="stylesheet" type="text/css" href="{% static 'bootstrap.min.css' %}">
<div class="container my-3">
    <table class="table">
        <thead>
        <tr class="table-dark">
            <th>번호</th>
            <th>제목</th>
            <th>작성일시</th>
        </tr>
        </thead>
        <tbody>
        {% if question_list %}
        {% for question in question_list %}
        <tr>
            <td>{{ forloop.counter }}</td>
            <td>
                <a href="{% url 'pybo:detail' question.id %}">{{ question.subject }}</a>
            </td>
            <td>{{ question.create_date }}</td>
        </tr>
        {% endfor %}
        {% else %}
        <tr>
            <td colspan="3">질문이 없습니다.</td>
        </tr>
        {% endif %}
        </tbody>
    </table>
</div>

📌 부트스트랩에 대한 자세한 내용은 다음 URL을 참조!!!

  • 이제 다음처럼 부트스트랩이 적용된 질문 목록을 볼 수 있을

  • 이어서 질문 상세 template에도 다음처럼 부트스트랩을 적용

    • 이번에는 수정사항이 좀 많음.
    • 부트스트랩으로 화면을 구성하다 보면 가끔은 이렇게 많은 양의 HTML 코드를 작성해야 함.
    • 질문이나 답변하나의 뭉치에 해당하므로 부트스트랩의 card 컴포넌트를 사용.

📄 /templates/pybo/question_detail.html

{% load static %}
<link rel="stylesheet" type="text/css" href="{% static 'bootstrap.min.css' %}">
<div class="container my-3">
    <!-- 질문 -->
    <h2 class="border-bottom py-2">{{ question.subject }}</h2>
    <div class="card my-3">
        <div class="card-body">
            <div class="card-text" style="white-space: pre-line;">{{ question.content }}</div>
            <div class="d-flex justify-content-end">
                <div class="badge bg-light text-dark p-2">
                    {{ question.create_date }}
                </div>
            </div>
        </div>
    </div>
    <!-- 답변 -->
    <h5 class="border-bottom my-3 py-2">{{question.answer_set.count}}개의 답변이 있습니다.</h5>
    {% for answer in question.answer_set.all %}
    <div class="card my-3">
        <div class="card-body">
            <div class="card-text" style="white-space: pre-line;">{{ answer.content }}</div>
            <div class="d-flex justify-content-end">
                <div class="badge bg-light text-dark p-2">
                    {{ answer.create_date }}
                </div>
            </div>
        </div>
    </div>
    {% endfor %}
    <!-- 답변 등록 -->
    <form action="{% url 'pybo:answer_create' question.id %}" method="post" class="my-3">
        {% csrf_token %}
        <div class="mb-3">
            <label for="content" class="form-label">답변내용</label>
            <textarea name="content" id="content" class="form-control" rows="10"></textarea>
        </div>
        <input type="submit" value="답변등록" class="btn btn-primary">
    </form>
</div>
  • 부트스트랩 card 컴포넌트 :

    https://getbootstrap.com/docs/5.1/components/card/

  • 질문 상세 템플릿에 사용한 부트스트랩 클래스

    • 그리고 질문 내용과 답변 내용에는 style="white-space: pre-line;" 과 같은 스타일을 지정해 줌.
    • 글 내용의 줄 바꿈을 정상적으로 표시하기위해 적용한 스타일임.
  • 부트스트랩을 적용한 질문 상세 화면


🌞 2-09. 템플릿 상속


🌈 표준 HTML 구조

  • 표준 HTML 문서의 구조는 위의 예처럼 html, head, body 엘리먼트가 있어야 하며, CSS 파일 링크는 head 엘리먼트 안에 있어야 함.
    또한 head 엘리먼트 안에는 meta, title 엘리먼트 등이 포함되어야 함.

📄 [표준 HTML 구조의 예]

<!doctype html>
<html lang="ko">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <link rel="stylesheet" type="text/css" href="/static/bootstrap.min.css">
    <title>Hello, pybo!</title>
</head>
<body>
(... 생략 ...)
</body>
</html>

🏳‍🌈 태그와 엘리먼트

<table> (... 생략 ...) </table>  <!-- table 엘리먼트 -->

위에서 <table>table 태그이고 <table> ~ </table> 처럼 table 태그로 시작해서 table 태그로 닫힌 구간(Block)은 table 엘리먼트이다.


🌈 템플릿 상속

  • 앞에서 작성한 질문 목록, 질문 상세 템플릿을 표준 HTML 구조가 되도록 수정하는 방법은 무엇일까?
    • 그런데 템플릿 파일들을 모두 표준 HTML 구조로 변경하면 body 엘리먼트 바깥 부분(head 엘리먼트 등)은 모두 같은 내용으로 중복될 것임.
    • 그러면 CSS 파일 이름이 변경되거나 새로운 CSS 파일이 추가될 때마다 모든 템플릿 파일을 일일이 수정해야 함.
  • 장고는 이런 중복과 불편함을 해소하기 위해 템플릿 상속(extend) 기능을 제공함.
  • 템플릿 상속 : 기본 틀이 되는 템플릿을 먼저 작성하고 다른 템플릿에서 그 템플릿을 상속해 사용하는 방법

🚦 base.html 템플릿

  • 모든 템플릿이 상속해야 하는 템플릿
  • 표준 HTML 문서의 기본 틀
  • body 엘리먼트 안의 {% block content %}{% endblock %} 템플릿 태그는 base.html을 상속한 템플릿에서 개별적으로 구현해야 하는 영역이 됨.

📄 /templates/base.html

{% load static %}
<!doctype html>
<html lang="ko">
<head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <!-- Bootstrap CSS -->
    <link rel="stylesheet" type="text/css" href="{% static 'bootstrap.min.css' %}">
    <!-- pybo CSS -->
    <link rel="stylesheet" type="text/css" href="{% static 'style.css' %}">
    <title>Hello, pybo!</title>
</head>
<body>
<!-- 기본 템플릿 안에 삽입될 내용 Start -->
{% block content %}
{% endblock %}
<!-- 기본 템플릿 안에 삽입될 내용 End -->
</body>
</html>

🚦 question_list 템플릿

  • question_list.html 템플릿을 다음과 같이 변경
    • base.html 템플릿을 상속하기 위해 {% extends 'base.html' %} 처럼 extends 템플릿 문법을 사용.
    • 상단의 두 줄은 base.html에 이미 있는 내용이므로 삭제.
    • 그리고 {% block content %}{% endblock %} 사이에 question_list.html에서만 쓰이는 내용을 작성.
    • 이렇게 하면 이제 question_list.htmlbase.html 템플릿을 상속받아 표준 HTML문서로 바뀌게 됨.

📄 /templates/pybo/question_list.html

{% extends 'base.html' %}
{% block content %}
<div class="container my-3">
    <table class="table">

        (... 생략 ...)

    </table>
</div>
{% endblock %}

🚦 question_detail 템플릿

  • question_detail.html 템플릿을 다음과 같이 변경
    • {% extends 'base.html' %} 템플릿 태그를 맨 위에 추가하고 기존 내용 위 아래로 {% block content %}{% endblock %}를 작성.
    • 템플릿 상속을 적용한 후 질문 목록, 질문 상세를 조회해보면 화면에 보여지는 것은 동일하지만 표준 HTML 구조로 변경된 것을 확인할 수 있음.

📄 /templates/pybo/question_list.html

{% extends 'base.html' %}
{% block content %}
<div class="container my-3">
    <h2 class="border-bottom py-2">{{ question.subject }}</h2>

    (... 생략 ...)

    </form>
</div>
{% endblock %}

🌞 2-10. 폼


🌈 질문 등록

  • html 파일에 질문 등록하기 버튼 만들기
    • <a href="..."> 과 같은 링크이지만 부트스트랩의 btn btn-primary 클래스를 적용하면 버튼으로 보이게 됨.
    • 버튼을 클릭하면 pybo:question_create 별칭에 해당되는 URL이 호출될 것

📄 /templates/pybo/question_list.html

    (... 생략 ...)
    </table>
    <a href="{% url 'pybo:question_create' %}" class="btn btn-primary">질문 등록하기</a>     # 추가
</div>
{% endblock %}

🚦 URL 매핑

  • pybo:question_create 별칭에 해당되는 URL 매핑 규칙을 추가
    • question_create 함수를 호출하도록 매핑

📄 /pybo/urls.py

from pybo.views import index, detail, question_create, answer_create     # 추가

(... 생략 ...)
urlpatterns = [
    (... 생략 ...)
    path('question/create/', question_create, name='question_create'),    # 추가
]

🚦

  • views.py에서 question_create 함수를 작성하기 전에 폼(Form)을 작성

    • 폼(Form)은 페이지 요청시 전달되는 파라미터들을 쉽게 관리하기 위해 사용하는 클래스
    • 폼은 필수 파라미터의 값이 누락되지 않았는지, 파라미터의 형식은 적절한지 등을 검증할 목적으로 사용
    • 이 외에도 HTML을 자동으로 생성하거나 폼에 연결된 모델을 이용하여 데이터를 저장하는 기능도 있음.
    • question_create 함수를 호출하도록 매핑
  • 질문 등록시 사용할 QuestionFormforms.py 파일에 작성

    • QuestionForm은 모델 폼(forms.ModelForm)을 상속
    • 장고의 폼은 일반 폼(forms.Form)과 모델 폼(forms.ModelForm)이 있음.
      • 모델 폼(forms.ModelForm) : 모델(Model)과 연결된 폼으로 폼을 저장하면 연결된 모델의 데이터를 저장할수 있는 폼
        👉 모델 폼은 이너 클래스인 Meta 클래스가 반드시 필요
        👉 Meta 클래스에는 사용할 모델과 모델의 속성을 기재해야 함.
  • 즉, QuestionFormQuestion 모델과 연결된 폼이고 속성으로 Question 모델의 subjectcontent를 사용한다고 정의한 것

📄 /pybo/forms.py

from django import forms
from pybo.models import Question


class QuestionForm(forms.ModelForm):
    class Meta:
        model = Question  # 사용할 모델
        fields = ['subject', 'content']  # QuestionForm에서 사용할 Question 모델의 속성

🚦 view 함수

  • views.py에서 question_create 함수를 작성
    • question_create 함수는 위에서 작성한 QuestionForm을 사용
    • render 함수에 전달한 {'form': form}은 템플릿에서 질문 등록시 사용할 폼 엘리먼트를 생성할 때 사용

📄 /pybo/forms.py

from .forms import QuestionForm

(... 생략 ...)

def question_create(request):
    form = QuestionForm()                                        
    return render(request, 'pybo/question_form.html', {'form': form})

🚦 template

  • pybo/question_form.html 템플릿을 작성
    • 템플릿에서 사용한 {{ form.as_p }}formquestion_create 함수에서 전달한 QuestionForm의 객체
    • {{ form.as_p }}는 폼에 정의한 subject, content 속성에 해당하는 HTML 코드를 자동으로 생성

📄 /template/pybo/question_form.html

{% extends 'base.html' %}
{% block content %}
<div class="container">
    <h5 class="my-3 border-bottom pb-2">질문등록</h5>
    <form method="post">
        {% csrf_token %}
        {{ form.as_p }}
        <button type="submit" class="btn btn-primary">저장하기</button>
    </form>
</div>
{% endblock %}

📌 action 속성

  • 보통 form 태그에는 항상 action 속성을 지정하여 submit 실행시 action에 정의된 URL로 폼을 전송해야 함.

    • 하지만 여기서는 특별하게 action 속성을 지정하지 않았음.
    • form 태그에 action 속성을 지정하지 않으면 현재 페이지의 URL이 디폴트 action으로 설정됨.
  • 물론 action 속성을 다음처럼 명확하게 지정해도 됨.

<form method="post" action="{% url 'pybo:question_create' %}">
  • 위와 같이 action 속성을 지정할 경우, question_form.html 템플릿은 질문 등록 에서만 사용 가능
    • 이후에 진행할 질문 수정 에서는 이 템플릿을 활용할 수가 없음.
    • 왜냐하면 질문 수정일 경우에는 action 값을 달리해야 하기 때문
    • 동일한 템플릿을 여러 기능에서 함께 사용할 경우에는 이처럼 formaction 속성을 비워두는 트릭을 종종 사용
    • 우리는 이후에 질문 수정 기능을 구현할 때도 question_form.html 템플릿을 사용할 것이므로 action 속성은 비워둘 것!!

🚦 GET과 POST

서버 재시작 후 브라우저에서 정상 작동 여부 확인

  • forms.py와 같은 신규 파일 작성시에는 로컬 서버 재시작이 필요

  • 질문 목록 화면 하단에 "질문 등록하기" 버튼이 추가됨.

    • "질문 등록하기" 버튼을 클릭하면 다음과 같이 "질문 등록" 화면이 나타남.
  • question_create 함수에 데이터를 저장하는 코드를 작성

📄 /pybo/views.py

def question_create(request):
    if request.method == 'POST':
        form = QuestionForm(request.POST)
        if form.is_valid():  # 폼이 유효하다면
            question = form.save(commit=False)  # 임시 저장하여 question 객체를 리턴받고
            question.create_date = timezone.now()  # 실제 저장을 위해 작성일시를 설정.
            question.save()  # 데이터를 실제로 저장.
            return redirect('pybo:index')
    else:
        form = QuestionForm()
    context = {'form': form}
    return render(request, 'pybo/question_form.html', context)

📌 POST, GET

  • 동일한 URL 요청을 POST, GET 요청 방식에 따라 다르게 처리

    • 질문 목록 화면에서 "질문 등록하기" 버튼을 클릭한 경우에는 /pybo/question/create/ 페이지가 GET 방식으로 요청되어 question_create 함수가 실행됨.
      • 왜냐하면 <a href="{% url 'pybo:question_create' %}" class="btn btn-primary">질문 등록하기</a>와 같이 링크를 통해 페이지를 요청할 경우에는 무조건 GET 방식이 사용되기 때문.
        따라서 이 경우에는 request.method 값이 GET이 되어 if .. else .. 구문에서 else 구문을 타게 되어 질문을 등록하는 화면을 렌더링

    • 그리고 질문 등록 화면에서 subject, content 항목에 값을 기입하고 저장하기 버튼을 누르면 이번에는 /pybo/question/create/ 페이지를 POST 방식으로 요청함.
      • 왜냐하면 앞서 설명했듯이 form 태그에 action 속성이 지정되지 않으면 현재 페이지가 디폴트 action으로 설정되기 때문.
      • 따라서 질문 등록 화면에서 저장하기 버튼을 클릭하면 question_create 함수가 실행되고 request.method 값은 POST가 되어 해당 코드 블럭이 실행됨.
  • GET 방식에서는 form = QuestionForm() 처럼 QuestionForm을 인수 없이 생성.

  • POST 방식에서는 form = QuestionForm(request.POST) 처럼 request.POST를 인수로 생성.

    • request.POST를 인수로 QuestionForm을 생성할 경우에는 request.POST에 담긴 subject, content 값이 QuestionFormsubject, content 속성에 자동으로 저장되어 객체가 생성됨.
      • request.POST에는 화면에서 사용자가 입력한 내용들이 담겨있음.
    • 그리고 form.is_valid()form이 유효한지를 검사
      • 만약 form에 저장된 subject, content의 값이 올바르지 않다면 form에는 오류 메시지가 저장되고 form.is_valid()가 실패하여 다시 질문 등록 화면을 렌더링.
      • 이 때 form에는 오류 메시지가 저장되므로 화면에 오류를 표시할 수 있음.
  • if form.is_valid(): form이 유효하다면 이후의 문장이 수행되어 질문 데이터가 생됨.

  • question = form.save(commit=False) : form에 저장된 데이터로 Question 데이터를 저장하기 위한 코드

    • QuestionFormQuestion 모델과 연결된 모델 폼이기 때문에 이와 같이 사용 가능.
    • 여기서 commit=False는 임시 저장을 의미.
      • 즉, 실제 데이터는 아직 데이터베이스에 저장되지 않은 상태를 뜻함.
      • 여기서 form.save(commit=False) 대신 form.save()를 수행하면 Question 모델의 create_date에 값이 없다는 오류가 발생.
      • 왜냐하면 QuestionForm에는 현재 subject, content 속성만 정의되어 있고 create_date 속성은 없기 때문.
      • 이러한 이유로 임시 저장을 하여 question 객체를 리턴받고 create_date에 값을 설정한 후 question.save()로 실제 데이터를 저장하는 것
        • create_date 속성은 데이터 저장 시점에 생성해야 하는 값이므로 QuestionForm에 등록하여 사용하지 않음.
  • 이제 브라우저에서 질문 등록이 잘 되는지 확인




🚦 form widget

  • {{ form.as_p }} 태그는 HTML 코드를 자동으로 생성하기 때문에 부트스트랩을 적용할 수가 없음.
    • 완벽하지는 않지만 다음처럼 QuestionForm을 조금 수정하면 어느정도 해결이 가능
    • widgets 속성을 지정하면 subject, content 입력 필드에 form-control과 같은 부트스트랩 클래스를 추가 가능

📄 /pybo/forms.py

from django import forms
from pybo.models import Question


class QuestionForm(forms.ModelForm):
    class Meta:
        model = Question
        fields = ['subject', 'content']
        widgets = {
            'subject': forms.TextInput(attrs={'class': 'form-control'}),
            'content': forms.Textarea(attrs={'class': 'form-control', 'rows': 10}),
        }
  • 다시 질문 등록 화면을 요청해 보면 다음과 같이 부트스트랩이 적용된 것을 확인 가능


🚦 form label

  • 질문 등록 화면에 표시되는 Subject, Content를 영문이 아니라 한글로 표시하고 싶다면 다음처럼 labels 속성을 지정

📄 /pybo/forms.py

from django import forms
from pybo.models import Question


class QuestionForm(forms.ModelForm):
    class Meta:
        model = Question
        fields = ['subject', 'content']
        widgets = {
            'subject': forms.TextInput(attrs={'class': 'form-control'}),
            'content': forms.Textarea(attrs={'class': 'form-control', 'rows': 10}),
        }
        labels = {                  # 추가
            'subject': '제목',       # 추가
            'content': '내용',       # 추가
        }                           # 추가


🚦 수동 폼 작성

  • {{ form.as_p }}

    • {{ form.as_p }}를 사용하면 빠르게 템플릿을 만들 수 있지만 HTML 코드가 자동으로 생성되므로 디자인 측면에서 많은 제한이 생김.
    • 예를 들어 폼 엘리먼트 내에 특정 태그를 추가하거나 필요한 클래스를 추가하는 작업에 제한이 생김.
    • 또 디자인 영역과 서버 프로그램 영역이 혼재되어 웹 디자이너와 개발자의 역할을 분리하기도 모호해짐.
  • 이용하여 자동으로 HTML 코드를 생성하지 않고 직접 HTML 코드를 작성하는 방법을 사용하기 위해 수작업시 필요없는 widget 속성을 제거

📄 /pybo/forms.py

from django import forms
from pybo.models import Question


class QuestionForm(forms.ModelForm):
    class Meta:
        model = Question  # 사용할 모델
        fields = ['subject', 'content']  # QuestionForm에서 사용할 Question 모델의 속성
        widgets = {
            'subject': forms.TextInput(attrs={'class': 'form-control'}),
            'content': forms.Textarea(attrs={'class': 'form-control', 'rows': 10}),
        }
  • 그리고 질문 등록 템플릿을 다음과 같이 수정
    • {{ form.as_p }}로 자동으로 생성되는 HTML 대신 제목과 내용에 해당되는 HTML코드를 직접 작성
    • 그리고 question_create 함수에서 form.is_valid() 가 실패할 경우 발생하는 오류의 내용을 표시하기 위해 오류를 표시하는 영역을 추가

    • 제목(subject) 항목의 value에는 {{ form.subject.value|default_if_none:'' }} 처럼 값을 대입해 주었는데 이것은 오류가 발생했을 경우 기존에 입력했던 값을 유지하기 위함.
    • |default_if_none:''의 의미 : 폼 데이터(form.subject.value)에 값이 없을 경우 None 이라는 문자열이 표시되는데 None 대신 공백으로 표시하라는 의미의 템플릿 필터
      • 장고의 템플릿 필터|default_if_none:'' 처럼 | 기호와 함께 사용됨.

📄 /templates/pybo/question_form.html

{% extends 'base.html' %}

{% block content %}
<div class="container">
    <h5 class="my-3 border-bottom pb-2">질문등록</h5>
    <form method="post">
        {% csrf_token %}
        <!-- 오류표시 Start -->                                               # 추가 시작
        {% if form.errors %}
        <div class="alert alert-danger" role="alert">
            {% for field in form %}
            {% if field.errors %}
            <div>
                <strong>{{ field.label }}</strong>
                {{ field.errors }}
            </div>
            {% endif %}
            {% endfor %}
        </div>
        {% endif %}
        <!-- 오류표시 End -->
        <div class="mb-3">
            <label for="subject" class="form-label">제목</label>
            <input type="text" class="form-control" name="subject" id="subject"
                   value="{{ form.subject.value|default_if_none:'' }}">
        </div>
        <div class="mb-3">
            <label for="content" class="form-label">내용</label>
            <textarea class="form-control" name="content"
                      id="content" rows="10">{{ form.content.value|default_if_none:'' }}</textarea>
        </div>                                                            # 추가 끝
        <button type="submit" class="btn btn-primary">저장하기</button>
    </form>
</div>
{% endblock %}

  • "질문등록" 화면에서 제목에만 "TEST"라고 입력하고 "내용"은 비워둔 채 "저장하기" 버튼을 클릭해보면
    • "내용"에 아무런 값도 입력하지 않았기 때문에 "내용"을 입력하라는 오류메시지 확인 가능
    • 그리고 "제목"에 입력했던 "TEST"는 사라지지 않고 계속 유지되는 것도 확인 가능.


🌈 답변 등록

  • 질문 등록에 장고 폼을 적용한 것처럼 답변 등록에도 장고 폼을 적용.

🚦 AnswerForm

  • 답변을 등록할 때 사용할 AnswerFormpybo/forms.py 파일에 다음과 같이 작성

📄 /pybo/forms.py

from django import forms
from pybo.models import Question, Answer

(... 생략 ...)

class AnswerForm(forms.ModelForm):
    class Meta:
        model = Answer
        fields = ['content']
        labels = {
            'content': '답변내용',
        }

🚦 answer_create 함수

  • answer_create 함수를 다음과 같이 수정
    • question_create와 같은 방법으로 AnswerForm을 이용하도록 변경.
    • 하지만 답변 등록은 POST 방식만 사용되기 때문에 GET 방식으로 요청할 경우에는 HttpResponseNotAllowed 오류가 발생하도록 설정.

📄 /pybo/views.py

(... 생략 ...)
from django.http import HttpResponseNotAllowed
from .forms import QuestionForm, AnswerForm
(... 생략 ...)

def answer_create(request, question_id):
    """
    pybo 답변등록
    """
    question = get_object_or_404(Question, pk=question_id)
    if request.method == "POST":
        form = AnswerForm(request.POST)
        if form.is_valid():
            answer = form.save(commit=False)
            answer.create_date = timezone.now()
            answer.question = question
            answer.save()
            return redirect('pybo:detail', question_id=question.id)
    else:
        return HttpResponseNotAllowed('Only POST is possible.')
    context = {'question': question, 'form': form}
    return render(request, 'pybo/question_detail.html', context)

🚦 question_detail.html

  • 질문 상세 템플릿도 오류를 표시하기 위한 영역을 다음처럼 추가

📄 /templates/pybo/question_detail.html

{% extends 'base.html' %}
{% block content %}
<div class="container my-3">
    (... 생략 ...)
    <form action="{% url 'pybo:answer_create' question.id %}" method="post" class="my-3">
        {% csrf_token %}
        <!-- 오류표시 Start -->                     # 추가 시작
        {% if form.errors %}
        <div class="alert alert-danger" role="alert">
            {% for field in form %}
            {% if field.errors %}
            <div>
                <strong>{{ field.label }}</strong>
                {{ field.errors }}
            </div>
            {% endif %}
            {% endfor %}
        </div>
        {% endif %}
        <!-- 오류표시 End -->                       # 추가 끝
        <div class="form-group">
            <textarea name="content" id="content" class="form-control" rows="10"></textarea>
        </div>
        <input type="submit" value="답변등록" class="btn btn-primary">
    </form>
</div>
{% endblock %}
  • 이렇게 수정하고 답변 내용 없이 답변을 등록하려고 하면 다음과 같은 오류 메시지가 표시됨.

post-custom-banner

0개의 댓글