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)
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 코드를 자동으로 생성해 부트스트랩을 적용할 수 없음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 대신 공백으로 표시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 %}