표준 HTML구조가 되도록 템플릿을 수정하기
표준 html 구조는 html, head, body 테그를 포함하고 스타일시트는 head 안에.
html 작성 시 표준스타일로 수정하면, body 테그 외에는 모두 동일한 내용일 것임. -> 중복 발생
중복 피하고 시트명이 변경되거나 추가될 때 하나하나 수정할 필요 없도록 템플릿 상속(extend) 자용하기.
템플릿 dir에 기본이 되는 틀 작성하기
emmet 코드를 이용해서 !를 텝하면 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>
body 태그 안의 {% block content %} 와 {% endblock %} 는 base.html을 상속한 템플릿에서 구현해야 하는 영역
{% extends 'base.html' %}
{% block content %}
#기존코드...
{% endblock %}
부트스트랩 사용하면서 본 파일은 필요없어서 빈파일로 남기기.
질문 등록하는 기능 구현하기
목록 페이지에 질문 등록할 수 잇는 버튼 생성하기
"질문 등록하기" 링크 {% url 'pybo:question_create' %} 를 추가
링크가 추가되었으니까 urls.py에 url 매핑 추가
path('question/create/', views.question_create, name='question_create')
매핑에 쓴 question_create 대로 views.py에 뷰 함수 추가로 작성.
from .forms import QuestionForm
def question_create(request):
"""
pybo 질문등록
"""
form = QuestionForm()
return render(request, 'pybo/question_form.html', {'form': form})
[C:\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이라는 모델과 연결된 폼이고 속성으로 subject와 content를 사용한다고 정의하였다.
> 장고의 폼: 일반 폼(forms.Form)과 모델 폼(forms.ModelForm)
모델폼 : 모델(Model)과 연결된 폼. 폼을 저장하면 연결된 모델의 데이터를 저장할 수 있음.
모델 폼은 class Meta 라는 내부(Inner) 클래스가 반드시 필요 - Meta 클래스에는 사용할 모델과 모델의 속성을 적어주어야 한다.
### 템플릿 작성
question_form.html 템플릿 작성
``` html
{% extends 'base.html' %}
{% block content %}
<div class="container">
<h5 class="my-3 border-bottom pb-2">질문등록</h5>
<form method="post" class="post-form my-3">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-primary">저장하기</button>
</form>
</div>
{% endblock %}
{{ form.as_p }} 의 form은 question_create 함수에서 전달된 QuestionForm의 객체 - 제목과 내용같은 폼 입력항목을 위한 HTML코드들을 자동으로 만들어 냄
질문등록버튼 생성됨
qeustion/create 페이지 생성
def question_create(request):
"""
pybo 질문등록
"""
# ---------------------------------------- [edit] ---------------------------------------- #
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)
화면에서 버튼 클릭 -> question/create 페이지가 get 방식으로 호출 -> 화면 호출.
질문 등록 화면에서 저장하기 버튼-> question/create 페이지가 POST 방식으로 호출, 데이터 저장
POST 요청의 경우 QuestionForm은 request.POST를 사용하여 생성해야함 -> QuestionForm의 subject와 content에는 request.POST로 전달받은 데이터가 저장
is_valid : 전송된 입력 항목의 값들이 유효한지를 검사
commit=False 옵션: 폼에 연결된 모델을 저장하지 않고 생성된 모델 객체만 리턴
페이지 깔끔하게 개선하기
{{ form.as_p }} 는 HTML코드가 자동으로 생성되기 때문에 부트스트랩을 적용할 수가 없다
QuestionForm을 조금 수정하면 어느정도 해결이 가능
widgets 속성을 지정하면 폼 입력 항목에 부트스트랩의 클래스를 추가 가능
label 사용하면 다른 이름으로 쓸 수 있음
{{ form.as_p }}를 사용하면 빠르게 템플릿을 만들 수 있는 장점이 있지만 HTML코드가 자동으로 생성되기 때문에 디자인적인 측면에서 많은 제한 - 태그 추가, 클래스 추가 어려움.
폼 이용해서 직접 HTML 코드 작성하기
widget 제거
{% extends 'base.html' %}
{% block content %}
<div class="container">
<h5 class="my-3 border-bottom pb-2">질문등록</h5>
<form method="post" class="post-form my-3">
{% csrf_token %}
<!-- -------------------------------------- [edit] -------------------------------------- -->
<!-- 오류표시 Start -->
{% if form.errors %}
<div class="alert alert-danger" role="alert">
{% for field in form %}
{% if field.errors %}
<strong>{{ field.label }}</strong>
{{ field.errors }}
{% endif %}
{% endfor %}
</div>
{% endif %}
<!-- 오류표시 End -->
<div class="form-group">
<label for="subject">제목</label>
<input type="text" class="form-control" name="subject" id="subject"
value="{{ form.subject.value|default_if_none:'' }}">
</div>
<div class="form-group">
<label for="content">내용</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.subject.value)에 값이 없을 경우 None 이라는 문자열이 표시되는데 None 대신 공백으로 표시
만약 내용 입력 없이 저장시도하면 알림뜸. 제목에 쓴 건 그대로 유지
from pybo.models import Question, Answer
class AnswerForm(forms.ModelForm):
class Meta:
model = Answer
fields = ['content']
labels = {
'content': '답변내용',
}
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
answer.question = question
answer.save()
return redirect('pybo:detail', question_id=question_id)
else:
form = Answerform()
context = {'question' : question, 'form': form}
return render(request, 'pybo/question_detail.html', context)
<form action="{% url 'pybo:answer_create' question.id %}" method="post" class="my-3">
{% csrf_token %}
<!-- -------------------------------------- [edit] -------------------------------------- -->
{% if form.errors %}
<div class="alert alert-danger" role="alert">
{% for field in form %}
{% if field.errors %}
<strong>{{ field.label }}</strong>
{{ field.errors }}
{% endif %}
{% endfor %}
</div>
{% endif %}