[TIL | 240107] Django - 폼(Form)

sun_U·2024년 1월 7일
0

TIL

목록 보기
13/21
post-thumbnail

폼 (Form)


1. 질문 등록

1) 버튼 생성

question_list.html

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

2) URL 매핑

pybo/urls.py

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

3) 폼(Form)

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

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 모델의 속성
  • 모델 폼(forms.ModelForm) : 모델(Model)과 연결된 폼으로 폼을 저장하면 연결된 모델의 데이터를 저장할수 있는 폼
  • Meta 클래스 : 모델 폼 사용시 필수. 사용할 모델과 모델의 속성 작성

4) 뷰 함수

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

5) 템플릿

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 %}
        {{ form.as_p }}
        <button type="submit" class="btn btn-primary">저장하기</button>
    </form>
</div>
{% endblock %}
  • {{ form.as_p }} : 폼에 정의한 subject, content 속성에 해당하는 HTML 코드를 자동으로 생성. form은 question_create 함수에서 전달한 QuestionForm의 객체

  • form 태그에 action 속성을 지정하지 않으면 현재 페이지의 URL이 디폴트 action으로 설정

    ⇒ 동일한 템플릿을 여러 기능에서 함께 사용할 경우, form의 action 속성을 비워두는 트릭을 종종 사용

6) 답변 저장 ( GET, POST)

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)
  • "질문 등록하기" 버튼을 클릭한 경우에는 /pybo/question/create/ 페이지가 GET 방식으로 요청되어 question_create 함수가 실행 ⇒ href 와 같이 링크 페이지를 통해 요청할 경우 무조건 GET 방식 사용
  • request.POST에는 화면에서 사용자가 입력한 내용들이 담겨있음
  • request.POST를 인수로 QuestionForm을 생성할 경우 → request.POST에 담긴 subject, content 값이 QuestionForm의 subject, content 속성에 자동으로 저장되어 객체가 생성
  • question = form.save(commit=False) → form에 저장된 데이터로 Question 데이터를 저장하기 위한 코드
  • commit=False → 임시 저장 ( DB 저장 X)

7) 폼 위젯

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 코드를 자동으로 생성해 부트스트랩을 적용할 수 없음
  • widgets 속성을 지정 → subject, content 입력 필드에 form-control과 같은 부트스트랩 클래스를 추가할 수 있음

8) 폼 레이블

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': '내용',
        }

9) 수동 폼 작성

  • {{ form.as_p }} → HTML을 빠르게 생성할 수는 있지만 디자인 측면에서 제한이 많음.

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 %}
  • |default_if_none:' → form 데이터가 없을 경우 None 대신 공백으로 표시

2. 답변 등록

forms.py

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

(... 생략 ...)

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

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

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

profile
Data Engineer🐣

0개의 댓글