python/Django
Django - mysite (폼)
- -
링크
폼
질문 등록
- 질문 등록 버튼 생성
- 경로 :
...templates\pybo\question_list.html
...
</table>
# 추가
<a href="{% url 'pybo:question_create' %}" class="btn btn-primary"> 질문 등록하기 </a>
</div>
{% endblock %}
<a href="">
는 링크이지만 부트스트랩의btn btn-primary
클래스를 적용하면 버튼으로 보인다.- 버튼을 클릭하면
pybo:question_create
별칭에 해당되는 URL을 호출한다.
1 URL 매핑
pybo:question_create
별칭에 해당되는 URL 매핑을 추가한다.- 경로 :
...misite\pybo\urls.py
...
urlpatterns =[
...
# 추가
path('question/create/', views.question_create, name='question_create'),
]
2 폼(form)
폼은 페이지 요청시 전달되는 파라미터들을 쉽게 관리하기 위해 사용하는 클래스이다.
폼은 필수 파라미터의 값이 누락되지 않았는지, 파라미터의 형식은 적절한지 등을 검증할 목적으로 사용된다.
HTML을 자동으로 생성하거나 폼에 연결된 모델을 이용하여 데이터를 저장할 수도 있다.
질문 등록시 사용할 QuestionForm을 만든다.
froms.py
파일을 생성한다.경로 :
...mysite\pybo\forms.py
from dango import forms
from pybo.models import Question
class QuestionForm(forms.ModelsForm):
class meta:
model = Question # 사용할 모델
fields = ['subject', 'content'] # QuestionForm에서 사용할 Question 모델의 속성
- 모델 폼(forms.ModelsForm)을 상속했다.
- 장고의 폼은 일반 폼(forms.Form)과 모델 폼(forms.ModelForm)이 있다.
- 모델 폼은 모델과 연결된 폼을 저장하면 연결된 모델의 데이터를 저장할 수 있는 폼이다.
- 모델 폼은 이너 클래스인
Meta
클래스가 반드시 필요하다.Meta
클래스에서는 사용할 모델과 모델의 속성을 적어야 한다.
- QuestionForm은 Question 모델과 연결된 폼이고 속성으로 Question 모델의 subject와 content를 사용한다고 정의한것이다.
3 뷰 함수
views.question_create
함수를 작성한다.- 경로 :
...mysite\pybo\views.py
...
from .models import Question
# 추가
from .forms import QuestionForm
...
...
# 추가
def question_create(request):
"""
pybo 질문 등록
"""
form = QuestionForm()
return render(request, 'pybo/question_form.html', {'form' : form})
question_create
함수는 위에서 작성한QuestionForm
을 사용했다.- render 함수에 전달한
{'form' : form'}
은 템플릿에서 질문 등록시 사용할 폼 엘리먼트를 생성할때 쓰인다.
4 템플릿
pybo/question_form.html
템플릿을 작성한다- 경로 :
...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" 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
의 객체이다. {{form.as_p}}
는 폼에 정의한 subject, content 속성에 해당하는 HTML 코드를 자동으로 생성한다.- form태그에 action 속성을 지정하지 않으면 현재 페이지의 URL이 디폴트 action으로 설정된다.
- action 속성을 명확하게 지정해도 되지만 그러면
question_form.html
템플릿은 "질문 등록"에서만 사용 가능하다. (다른 곳에서는 action 값을 다르게 해야하기 때문)
5 GET과 POST
forms.py
와 같은 신규 파일을 작성했으므로 서버를 재시작해야 브라우저에서 확인이 가능하다.- 질문 등록하기 페이지에서 작성한 내용을 저장하기 위해
question_create
함수에 데이터를 저장하는 코드를 작성한다. - 경로 :
...mysite\pybo\views.py
def question_create(request):
"""
pybo 질문등록
"""
<!--함수 내용 수정 (기존 내용 삭제)-->
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)
- ***이번 챕터에서 가장 핵심이 되는 부분
- URL 요청을 POST, GET 요청 방식에 따라 다르게 처리했다.
- 질문 목록 화면에서 "질문 등록하기" 버튼을 클릭한 경우에는
/pybo/question/create/
페이지가 GET 방식으로 요청되어question_create
함수가 실행된다.<a href="{% url 'pybo:question_create' %}" class="btn btn-primary">질문 등록하기</a>
과 같이 링크를 통해 페이지를 요청할 경우에는 무조건 GET 방식이 사용되기 때문- 이 경우에는
request.method
값이 GET이 되어if .. else ..
에서 else 구문을 타게 되어 결국 질문 등록 화면을 보여줄 것이다.
- 질문 등록 화면에서 subject, content 항목에 값을 기입하고 "저장하기" 버튼을 클릭하면
/pybo/question/create
페이지가 POST 방식으로 요청된다.- form 태그에 action 속성이 지정되지 않으면 현재 페이지가 디폴트 action으로 지정되기 때문
- 따라서 "저장하기" 버튼을 클릭하면
question_create
함수가 실행되고request.method
값은 POST가 되어 다음 코드가 실행된다.
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')
- GET 방식에서는
form = QuestionForm()
처럼 QuestionForm을 인수없이 생성했다. - POST 방식에서는
form = QuestionForm(request.POST)
처럼request.POST
를 인수로 생성했다. request.POST
에는 화면에서 사용자가 입력한 값이 담겨있다.form.is_valid()
는 form이 유효한지 검사한다.- 만약 form에 저장된 subject, content 값이 올바르지 않다면 form에는 오류 메시지가 저장되고 form.is_valid()가 실패하여 다시 질문 등록화면으로 돌아간다.
- 이때 form에 저장된 오류 메시지는 질문 등록 화면에 표시된다.
- form이 유요하다면
if form.ist_valid():
이후의 문장이 수행되어 질문 데이터가 생성된다. question = form.save(commit=False)
는 form으로 Question 데이터를 저장하기 위한 코드이다.- QuestionForm이 Question 모델과 연결된 모델 폼이기 때문에 이와 같이 사용할 수 있다.
commit=False
는 임시 저장을 의미한다. (실제 데이터는 아직 데이터베이스에 저장되지 않은 상태)- 여기서
form.save(commit=False)
대신form.save()
를 수행하면 Question 모델의create_date
에 값이 없다는 오류가 발생한다.- QuestionForm에는 현재 subject, content 속성만 정의 되어있기 때문
create_date
속성은 데이터 저장 시점에 자동 생성해야 하는 값이므로 QuestionForm에 등록하여 사용하지 않는다.
- QuestionForm이 Question 모델과 연결된 모델 폼이기 때문에 이와 같이 사용할 수 있다.
- 만약 form에 저장된 subject, content 값이 올바르지 않다면 form에는 오류 메시지가 저장되고 form.is_valid()가 실패하여 다시 질문 등록화면으로 돌아간다.
- 저장이 완료되면
return redirect('pybo:index)
를 호출하여 질문 목록 화면으로 이동한다.
6 폼 위젯
{{ form.as_p }}
태그는 HTML코드를 자동으로 생성하기 때문에 부트스트랩을 적용할 수가 없다.- 이를 해결하기 위해 QuestionForm을 수정한다.
- 경로 : ...mysite\pybo\forms.py
...
class QuestionForm(forms.ModelForm):
class Meta:
...
fields = ['subject', 'content']
# 추가
widgets = {
'subject': forms.TextInput(attrs={'class': 'form-control'}),
'content': forms.TextInput(attrs={'class': 'form-control', 'rows': 10}),
}
Meta
클래스의 widgets 속성을 지정하면 입력 필드에form-control
과 같은 부트스트랩 클래스를 추가할 수 있다.
7 폼 레이블
- 질문 등록 화면에 표시되면 subject, content를 한글로 표시하고 싶으면 lables 속성을 지정하면 된다.
- 경로 :
...\mysite\pybo\forms.py
...
...
widgets = {
'subject': forms.TextInput(attrs={'class': 'form-control'}),
'content': forms.Textarea(attrs={'class': 'form-control', 'rows': 10}),
}
# 추가
labels = {
'subject': '제목',
'content': '내용',
}
8 수동 폼 작성
{{ form.as_p }}
를 사용하면 빠르게 템플릿을 만들 수 있지만 HTML 코드가 자동 생성되므로 여러 제한이 생긴다.- 이번에는 자동이 아닌 직접 HTML 코드를 작성한다.
mysite\pybo\forms.py
파일의 widget 항목 제거- 경로 :
...templates\pybo\question_form.html
{% csrf_token %}
# 추가
<!-- 오류 표시 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="from-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.as_p}}
로 자동 생성되는 HTML 대신 제목과 내용에 해당되는 HTML 코드를 직접 작성했다.question_create
함수에서form.is_valid()
가 실패할 경우 발생하는 오류의 내용을 표시하기 위해 오류를 표시하는 영역을 추가했다.- 제목 항목의 value에는
{{form.subject.value|default_if_none:''}}
처럼 값을 대입 해주었는데 이것은 오류가 발생했을 경우 기존에 입력했던 값을 유지하기 위함이다.|default_if_none:''
의 의미는 폼 데이터(form.subject.value
)에 값이 없을 경우 None이라는 문자열 대신 공백으로 표시하라는 의미이다.- 장고의 템플릿 필터는 위와 같이 ' | ' 기호와 함께 사용된다.
답변 등록
- 답변 등록에 장고 폼을 적용한다.
- 답변을 등록할때 사용할
AnswerForm
을pybo/form.py
파일에 작성 - 경로 :
...mysite\pybo\forms.py
# 수정
from pybo.models import Question, Answer
...
...
# 추가
class AnswerForm(form.ModelForm):
class Meta:
model = Answer
fields = ['content']
labels = {
'content': '답변내용',
}
answer_create
함수 수정- 경로 :
...mysite\pybo\views.py
...
# 수정
from .forms import QuestionForm, AnswerForm
...
def answer_create(request, question_id):
"""
pybo 답변 등록
"""
question = get_object_or_404(Question, pk=question_id)
# 추가
if requet.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:
form = AnswerForm()
context = {'question': question, 'form':form}
return render(request, 'pybo/question_detail.html', context)
question_create
와 같은 방법으로 AnswerForm을 이용하도록 변경했다.- 답변 등록은 POST 방식만 사용되기 때문에 if..else.. 구문에서 else는 호출되지 않는다.
- 패턴의 동일성을 위해 남겨두었다.
- 답변 등록은 POST 방식만 사용되기 때문에 if..else.. 구문에서 else는 호출되지 않는다.
질문 상세 템플릿도 오류를 표시하기 위해 영역을 추가한다.
경로 :
...templates\pybo\question_detail.html
...
{% csrf_token %}
# 추가
{% if form.errors %}
<div class="alert alert-dange" role="alert">
{% for field in form %}
{% if field.errors %}
<strong>{{field.label}}</strong>
{{field.errors}}
{% endif %}
{% endfor %}
</div>
{% endif %}
<div class="form-group">
반응형
Contents
소중한 공감 감사합니다