본문 바로가기

Django/따라하는 장고

14. 폼

[질문등록]

 

질문 등록을 하기위해 '질문 등록하기' 버튼을 만들어야 한다. question_list.html 파일을 수정해 '질문 등록하기' 버튼을 생성하자.

<a href="...">과 같은 링크이지만 부트스트랩의 btn btn-primary 클래스를 적용하면 버튼으로 보인다. 버튼을 클릭하면 pybo:question_create 별칭에 해당되는 URL 이 호출된다.

이제 pybo:question_create 별칭에 해당하는 URL 매핑규칙을 추가해야한다.

projects\mysite\pybo\urls.py 파일을 수정하자.

views.question_create 함수를 호출하도록 매핑했다.

 

[Form]

 

이제 views.question_create 함수를 작성해야 한다. 그 전에 Form 에 대해서 먼저 알아보면, 폼은 페이지 요청시 전달되는 파라미터들을 쉽게 관리하기 위해 사용하는 클래스이다. 폼은 필수 파라미터의 값이 누락되지 않았는지, 파라미터의 형식은 적절한지 등을 검증할 목적으로 사용한다. 이 외에 HTML을 자동으로 생성하거나 폼에 연결된 모델을 이용하여 데이터를 저장하는 기능도 있다.

새롭게 projects\mysite\pybo\forms.py 파일을 새로 만들어 작성하자.

QuestionForm 은 모델 폼(forms.ModelForm)을 상속했다. 장고의 폼은 일반 폼(forms.Form)과 모델 폼(forms.ModelForm)이 있는데 모델 폼은 모델과 연결된 폼으로 폼을 저장하면 연결된 모델의 데이터를 저장할 수 있는 폼이다. 모델 폼은 이너 클래스인 Meta 클래스가 반드시 필요하다. Meta 클래스에는 사용할 모델과 모델의 속성을 적어야 한다.

즉, QuestionForm 은 Quiestion 모델과 연결된 폼이고 속성으로 Question 모델의 subjects, contnet 를 사용한다고 정의한 것이다.

 

이제 다시 아까 필요했던 함수 views.question_create 함수를 작성해보자.

projects\mysite\pybo\views.py 파일을 수정하자.

question_create 함수는 위에서 작성한 QuestionForm 을 사용했다. render 함수에 전달한 {'form':form} 은 템플릿에서 질문 등록시 사용할 폼 엘리먼트를 생성할 때 쓰인다.

 

이제 템플릿을 작성해야한다. 방금 작성한 pybo/question_form.html 파일을 만들어 줘야한다.

projects\mysite\template\pybo\question_form.html 파일을 만들고 다음과 같이 작성하자.

템플릿에서 사용한 {{ form.as_p }} 의 폼은 question_create  함수에서 전달한 QuestionForm 의 객체이다. {{ form.as_p }} 는 폼에 정의한 subject, content 속성에 해당하는 HTML 코드를 자동으로 생성한다.

보통 form 태그에는 항상 action 속성을 지정하여 submit 실행시 action 에 정의된 URL로 폼을 전송해야한다. 하지먼 방금 작성한  폼 태그 <form method="post"> 에서는 특별하게 action 속성을 지정하지 않았다. 폼 태그에 액션 속성을 지정하지 않으면 현재 페이지의 URL 이 디폴트action 으로 설정 된다.

<form method="post" action="{% url 'pybo:question_create' %}"> 처럼 action 속성을 명확하게 지정해도 된다.

하지만 이렇게 작성하면 question_form.html 템플릿은 "질문 등록" 에서만 사용 가능하다. 이후에 사용할 '질문 수정' 템플릿에서는 사용할 수가 없다. 그 이유는 질문 수정일 경우에는 action 값을 다르게 해야하기 때문이다. 동일한 템플릿을 여러기능에서 함께 사용할 경우에는 이와같이 폼의 액션 속성을 비워드는 경우도 종종 있다.

 

이제 수정한 페이지를 다시 요청해보면

질문 등록하기 버튼이 추가 된 것을 확인할 수 있다. 질문 등록하기 버튼을 누르면

질문을 등록하는 페이지가 나온다. 하지만 subject 와 content 에 아무거나 입력하고 저장하기 버튼을 눌러도 아무 반응이 없다. 우리는 아직 question_create 함수에 데이터를 저장하는 코드를 작성하지 않았기 때문이다. 

 

question_create 함수 수정을 위해

projects\mysite\pybo\views.py 파일을 수정하자.

동일한 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 문이 실행된다.

 

if문을 살펴보자면,

 

GET 방식에서는 form = QuestionForm() 처럼 QuestionForm을 인수없이 생성했지만 POST 방식에서는 form = QuestionForm(request.POST) 처럼 request.POST 를 인수로 생성했다. request.POST 를 인수로 QuestionForm 을 생성할 경우에는 request.POST 에 담긴 subject, content 값이 QuestionForm 의 subject, content 속성에 자동으로 저장되어 객체가 생성된다.

 

폼이 유효하다면 if form.is_valid(): 이후의 문장이 수행되어 질문 데이터가 생성된다. question = form.save(commit=False) 는 form 에 저장된 데이터로 Question 데이터를 저장하기 위한 코드이다. QeustionForm 이 Question 모델과 연결된 모델 폼이기 때문에 이와같이 사용할 수 있다. commit=False 는 임시저장을 의미한다. 즉, 실제 데이터는 아직 데이터베이스에 저장되지 않은 상태이다. 여기서 form.save(commit=False) 대신 form.save() 를 수행하면 Question 모델의 create_date 값이 없다는 오류가 발생한다. QuestionForm 에 subject, content 속성만 정의되어 있고 create_date 속성이 없기 때문이다. 그렇기 때문에 임시 저장을하여 question 객체를 리턴받고 create_date 값을 성정한 후 question.save() 로 실제 데이터를 저장하는 것이다.

 

이제 작성한 것들이 잘 작동되는지 확인해보자.

질문 등록하기 버튼을 누르면 GET 요청방식(if ... else ... 문에서 else 문)을 따라 질문등록 페이지가 렌더링 되고, subject, content 를 기입하고 저장하기 버튼을 누르면 POST 방식으로 요청한다.

저장하기 버튼을 누르면 POST 방식으로 요청해(if 문을 수행한다) 제목과 내용을 저장하고 작성일시를 설정하고 저장된다.

 

[폼 위젯]

 

이제 화면을 꾸며보자. 부트스트랩을 적용해 꾸미면 되지만 {{ form.as_p }} 태그는 HTML 코드를 자동으로 생성하기 때문에 부트스트랩을 적용할 수 없다. QuestionForm 을 수정하면 어느정도 해결이 가능하다. projects\mysite\pybo\forms.py 를 수정하자.

이와 같이 widgets 속성을 지정하면 subject, content 입력 필드에 form-control 과 같은 부트스트랩 클래스를 추가할 수 있다. 추가환하면을 확인해보면 이렇다.

projects\mysite\pybo\forms.py 의 label 속성을 지정해주면 subject, content 를 한글로 표시할 수 있다.

이렇게 수정후 확인해보면 

한글로 수정되어 있다.

 

[수동으로 폼 작성]

 

{{ form.as_p }} 을 사용하면 빠르게 템플릿을 만들 수 있지만 HTML 코드가 자동으로 생성되므로 디자인측면에서 많은 제한이 생긴다. 예를 들어 엘리먼트 내에 특정 태그를 추가하거나 필요한 클르스를 추가하는 작업에 제한이 생긴다. 또 디자인 영역과 서버 프로그램 영역이 혼재되어 웹 디자이너와 개발자의 역할을 분리하기도 모호해질수있다.

이번엔 폼을 이용해 자동으로 HTML 코드를 생성하지 않고 직접 HTML 코드를 작성해보자. 일단 수작업시 필요없는 widget 속성을 제거하자.

projects\mysite\pybo\forms.py 파일을열어 수정하자

widget= { ... } 부분을 삭제했다. 그후 projects\mysite\templates\pybo\question_form.html 파일을 다음과 같이 수정하자.

{{ form.as_p }}로 자동 생성되는 HTML 대신 제목과 내용에 해당되는 HTML코드를 직접 작성했다. 그리고 question_create 함수에서 form.is_valid() 가 실패할 경우에 발생하는 오류의 내용을 표시하기 위해 요류를 표시하는 영역을 추가했다.

subject 항목의 value 에는 {{ form.subject|default_if-none:'' }} 처럼 값을 대입해 주었는데 이것은 오류가 발생했을 경우 기존에 입력했던값을 유지하기 위함이다. |default_if_none:'' 의 의미는 폼 데이터 ( form.subject.value )에 값이 없을 경우 None 이라는 문자열이 표시되는데 None 대신 공백으로 표시하라는 의미의 템플릿 필터이다.

장고의 템플릿 필터는 |default_if_none:'' 처럼 | 기호와 함께 사용된다.

여기까지 했으면 테스트를해보자. '질문등록'화면에서 제목에 TEST 라고 입력하고 '내용'은 비워둔채 '저장하기'를 누르면

'내용'에 아무런 값도 입력하지 않아내용을 입력하라는 오류메세지를 볼수있다. 그리고 '제목'에 입력했던 TEST 는 사라지지 않고 유지되는것도 확인할 수 있다.

 

[답변 등록]

 

이번엔 답변등록도 폼에 적용해보자. projects\mysite\pybo\forms.py 를 다음과 같이 수정하자.

 

그리고 answer_create 함수도 수정해야한다. projects\mysite\pybo\views.py 

question_create 와 같은 방법으로 AnswerForm 을 이용하도록 변경했다. 답변등록때는 POST 방식만 사용되기 때문에 GET 방식으로 요청할 경우에는 HttpResponseNotAllowed 오류가 발생하도록 수정했다.

마지막으로 projects\mysite\templates\pybo\question_detail.html 을 다음과 같이 수정해 오류를 표시하기 위한 영역을 추가하자.

이제 테스트를 해보자.

답변 내용을 아무것도 작성하지 않고 답변등록 버튼을 누르면 답변내용은 필수항목이라는 오류메세지가 표시된다.

'Django > 따라하는 장고' 카테고리의 다른 글

16. Paginator  (0) 2022.11.05
15. 네비게이션 바  (0) 2022.11.04
13. 표준 HTML & 템플릿 상속  (0) 2022.10.31
12. 부트스트랩  (0) 2022.10.27
11. 스태틱 디렉토리  (0) 2022.10.25