이제 게시글 등록은 잘 되지만 한 가지 아쉬운 점이 있다. 우리는 화면을 더 아름답게 꾸밀 수 있는 부트스트랩을 준비해 두었다. 그러나 {{ form.as_p }} 태그는 HTML 코드를 자동으로 생성하기 때문에, 부트스트랩 스타일을 직접 적용할 수 없다.
해결방법은 없을까?
완벽하지는 않지만, 다음처럼 QuestionForm을 조금 수정하면 어느 정도 해결이 가능하다.
boards\forms.py
class PostForm(forms.ModelForm):
class Meta:
model = Post # 사용할 모델
fields = ['title', 'content']
widgets = {
'subject': forms.TextInput(attrs={'class': 'form-control'}),
'content': forms.Textarea(attrs={'class': 'form-control', 'rows': 10}),
}
아래를 추가 해보자
labels = {
'title': '제목',
'content': '내용',
}


{{ form.as_p }}를 사용하면 빠르게 템플릿을 만들 수 있지만, HTML 코드가 자동으로 생성되기 때문에 디자인 측면에서 많은 제한이 생긴다. 예를 들어, 폼 엘리먼트 내에 특정 태그를 추가하거나 필요한 클래스를 적용하는 작업에 제약이 따른다. 또한, 디자인 영역과 서버 프로그램 영역이 혼재되어 웹 디자이너와 개발자의 역할 분리가 모호해질 수 있다.
이번에는 폼을 이용해 자동으로 HTML 코드를 생성하지 않고, 직접 HTML 코드를 작성하는 방법을 사용해 보자. 우선, 수작업 시 필요 없는 widget 속성을 제거하자.
boards\forms.py
아래와 같이 삭제

그리고 질문 등록 템플릿을 다음과 같이 수정하자.
templates\boards\post_form.html
{% extends 'boards/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="title" id="title" value="{{ form.title.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 %}

{{ form.as_p }}로 자동으로 생성되는 HTML 대신 제목과 내용에 해당되는 HTML코드를 직접 작성했다. 그리고 post_create 함수에서 form.is_valid() 가 실패할 경우 발생하는 오류의 내용을 표시하기 위해 오류를 표시하는 영역을 추가했다.
제목(title) 항목의 value에는 {{ form.title.value|default_if_none:'' }} 처럼 값을 대입해 주었는데 이것은 오류가 발생했을 경우 기존에 입력했던 값을 유지하기 위함이다. |default_if_none:''의 의미는 폼 데이터(form.title.value)에 값이 없을 경우 None 이라는 문자열이 표시되는데 None 대신 공백으로 표시하라는 의미의 템플릿 필터이다.
장고의 템플릿 필터는 |default_if_none:'' 처럼 | 기호와 함께 사용된다.
"내용"에 아무런 값도 입력하지 않았기 때문에 "내용"을 입력하라는 오류메시지를 볼 수 있다. 그리고 "제목"에 입력했던 "TEST"는 사라지지 않고 계속 유지되는 것도 확인할수 있다

이번에는 질문 등록에 장고 폼을 적용한 것처럼 답변 등록에도 장고 폼을 적용해 보자. 답변을 등록할 때 사용할 ReplyForm을 boards/forms.py 파일에 다음과 같이 작성하자.
boards\forms.py
class CommentForm(forms.ModelForm):
class Meta:
model = Comment # 사용할 모델
fields = ['content']
labels = {
'content': '댓글',
}
boards\urls.py
아래를 추가
path('reply/create/<int:post_id>/', views.comment_create, name='reply_create'),
boards\views.py 추가
def comment_create(request,post_id):
"""
댓글 등록
"""
post = get_object_or_404(Post, pk=post_id)
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid(): # 폼이 유효하다면
comment = form.save(commit=False) # 임시 저장하여 post 객체를 리턴받는다.
#post.created_at = timezone.now()# 실제 저장을 위해 작성일시를 설정한다.
comment.post = post
comment.save()# 데이터를 실제로 저장한다.
return redirect('boards:index', post_id=post.id)
else:
return HttpResponseNotAllowed('Only POST is possible.')
context = {'post':post,'form': form}
return render(request, 'boards/post_form.html', context)
하지만 답변 등록은 POST 방식만 사용되기 때문에 GET 방식으로 요청할 경우에는 HttpResponseNotAllowed 오류가 발생하도록 했다.
templates\boards\post_detail.html
{% extends 'boards/base.html' %}
{% block content %}
<div class="container my-3">
<!-- 부모글 -->
<h2 class="border-bottom py-2">{{ post.title }}</h2>
<div class="card my-3">
<div class="card-body">
<div class="card-text" style="white-space: pre-line;">{{ post.content }}</div>
<div class="d-flex justify-content-end">
<div class="badge bg-light text-dark p-2">
{{ post.created_at }}
</div>
</div>
</div>
</div>
<!-- 댓글목록 -->
<h5 class="border-bottom my-3 py-2">{{post.comment_set.count}}개의 댓글이 있습니다.</h5>
{% for comment in post.comment_set.all %}
<div class="card my-3">
<div class="card-body">
<div class="card-text" style="white-space: pre-line;">{{ comment.content }}</div>
<div class="d-flex justify-content-end">
<div class="badge bg-light text-dark p-2">
{{ comment.created_at }}
</div>
</div>
</div>
</div>
{% endfor %}
<!-- 댓글등록 -->
{% comment %} <form action="{% url 'boards:reply_create' post.id %}" method="post">
{% csrf_token %}
<textarea name="content" id="content" rows="15"></textarea>
<input type="submit" value="댓글등록">
</form> {% endcomment %}
<form action="{% url 'boards:reply_create' post.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">
<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>
{%endblock%}
