본문 바로가기
Django

(10) 폼

by kingyejin 2024. 7. 2.

질문 등록

질문을 등록하려면 먼저 "질문 등록하기" 버튼을 만들어야 한다.

[파일이름: projects\mysite\templates\pybo\question_list.html]

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


URL 매핑

이제 pybo:question_create 별칭에 해당되는 URL 매핑 규칙을 추가하자.

[파일명: projects\mysite\pybo\urls.py]

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


폼(Form)

흐름에 따라 이제 views.question_create 함수를 작성해야 한다.

폼은 쉽게 말해 페이지 요청시 전달되는 파라미터들을 쉽게 관리하기 위해 사용하는 클래스이다. 

폼은 필수 파라미터의 값이 누락되지 않았는지, 파라미터의 형식은 적절한지 등을 검증할 목적으로 사용한다.

이 외에도 HTML을 자동으로 생성하거나 폼에 연결된 모델을 이용하여 데이터를 저장하는 기능도 있다.


질문 등록시 사용할 QuestionForm을 만들면서 자세히 알아보자. 

forms.py 파일은 신규로 작성해야 한다.

[파일명: projects\mysite\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 모델의 속성

 

즉, QuestionForm은 Question 모델과 연결된 폼이고 속성으로 Question 모델의 subject와 content를 사용한다고 정의한 것이다.


뷰 함수

 views.question_create 함수를 다음과 같이 작성하자.

[파일명: projects\mysite\pybo\views.py]

from django.shortcuts import render, get_object_or_404, redirect
from django.utils import timezone
from .models import Question
from .forms import QuestionForm

(... 생략 ...)

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


템플릿

이제 템플릿을 작성할 차례이다. pybo/question_form.html 템플릿을 다음과 같이 새로 작성하자.

[파일명: projects\mysite\templates\pybo\question_form.html]

Copy{% 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 %}


GET과 POST

이제 지금까지 진행한 것들이 잘 동작하는지 브라우저에서 확인해 보자.

로컬 서버가 이미 구동되어 있다면 재시작하고 다음을 따라하자.

 

다음과 같이 질문 목록 페이지를 요청해 보자.

질문 목록 화면 하단에 "질문 등록하기" 버튼이 추가되었다.

"질문 등록하기" 버튼을 클릭하면 다음과 같이 "질문 등록" 화면이 나타난다.


저장

다음으로 question_create 함수에 데이터를 저장하는 코드를 아직 작성해야 "저장하기" 버튼을 눌러도 저장이 된다.

다음처럼 question_create 함수를 수정하자.

[파일명: projects\mysite\pybo\views.py]

Copydef question_create(request):
    if request.method == 'POST':
        form = QuestionForm(request.POST)
        if form.is_valid():
            question = form.save(commit=False)
            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)


폼 위젯

우리는 화면을 이쁘게 만들수 있는 부트스트랩을 준비해 두었다.

하지만 {{ form.as_p }} 태그는 HTML 코드를 자동으로 생성하기 때문에 부트스트랩을 적용할 수가 없다.

이에 QuestionForm을 조금 수정하면 어느정도 해결이 가능하다.

[파일명: projects\mysite\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}),
        }

위와 같이 widgets 속성을 지정하면 subject, content 입력 필드에 

form-control과 같은 부트스트랩 클래스를 추가할 수 있다.


이제 다시 질문 등록 화면을 요청해 보면 다음과 같이 부트스트랩이 적용된 화면을 볼 수 있다.


수동 폼 작성

이번에는 폼을 이용하여 자동으로 HTML 코드를 생성하지 말고 직접 HTML 코드를 작성하는 방법을 사용해 보자.

우선 수작업시 필요없는 widget 속성을 제거하자.

[파일명: projects\mysite\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 모델의 속성
        labels = {
            'subject': '제목',
            'content': '내용',
        }

-> 제거한 코드


그리고 질문 등록 템플릿을 다음과 같이 수정하자.

[파일명: projects\mysite\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을 pybo/forms.py 파일에 다음과 같이 작성하자.

[파일명: projects\mysite\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 함수를 다음과 같이 수정하자.

[파일명: projects\mysite\pybo\views.py]

Copy(... 생략 ...)
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)


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

[파일명: projects\mysite\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="mb-3">
            <textarea name="content" id="content" class="form-control" rows="10"></textarea>
        </div>
        <input type="submit" value="답변등록" class="btn btn-primary">
    </form>
</div>
{% endblock %}

 

이렇게 수정하고 답변등록 테스트를 해 보자.

만약 답변 내용 없이 답변을 등록하려고 하면 다음과 같은 오류 메시지가 표시될 것이다.

'Django' 카테고리의 다른 글

Django rest framework 설치 및 기본 세팅  (0) 2024.07.05
(11) 내비게이션 바  (0) 2024.07.05
(9) 템플릿 상속  (0) 2024.07.02
(8) 부트스트랩  (0) 2024.07.02
(7) 데이터 저장 및 스태틱  (0) 2024.07.02