답글을 작성하거나 수정한 후에 항상 페이지 상단으로 스크롤이 이동되어 작성한 답변을 확인하려면 다시 스크롤을 내려서 확인해야 한다. Ajax같은 비동기 통신 기술을 사용할 수 도 있지만 보다 쉬운 방법으로 이 문제를 해결해 보자. HTML 에는 URL 호출시 원하는 위치로 이동시켜 주는 앵커 태그가 있다. 예를 들어 HTML 중간에 <a id="django"></a> 라는 앵커 태그가 있다면 이 HTML 을 호출하는 URL 뒤에 #django 라고 붙여주면 해당 페이지가 호출되면서 해당 앵커로 스크롤이 이동된다. 이를 적용해 보자.
먼저 답변에 앵커기능을 추가해 보자.
projects\mysite\templates\pybo\question_detail.html 을 수정하자.
답변 작성, 수정시에이동해야할 앵커 태그를 추가했다. 답변이 반복되어 표시되는 for 문 바로 뒤에 추가했다. name 속성은 유일한 값 이어야 하므로 answer_{{ answer.id }} 같은 답변 id 를 사용했다.
이제 답변을 등록하거나 수정한 뒤 지정한 앵커 태그로 이동하도록 코드를 수정하자. 다음은 답변 등록 또는 답변 수정을 한 뒤 사용했던 기존 코드의 일부이다.
하나의 질문에 여러명이 추천할 수 있고 한명이 여러 개의 질문에 추천할 수 있으므로 다대다, N:N 관계를 의미하는 ManytoManyField 를 사용해야 한다.
projects\mysite\pybo\models.py 파일을 다음과 같이 수정하자.
voter 를 ManytoManyField 관계로 추가했다. 모델을 변경했기 때문에 makrmigrations, migrate 를 해야한다.
하지만 makemigrations 를 하면 오류가 발생한다. Question 모델에서 사용한 author와 voter 모두 User 모델과 연결되어 있기 때문에 User.question_set 처럼 User 모델을 통해서 Question 데이터에 접근하려고 할 때 author를 기준으로 할지 voter 를 기준으로 해야 할 지 명확하지 않다는 오류이다. 오류의 HINT 에서도 알 수 있듯이 related_name 인수를 추가하여 해결할 수 있다.
projects\mysite\pybbo\models.py 를 수정하자.
author 에는 related_name='author_question' 라는 인수를 지정하고 voter 에는 related_name='voter_question' 라는 인수를 지정했다. 이렇게 하면 특정 사용자가 작성한 질문을 얻기 위해서는 some_user.author_question.all() 처럼 사용할 수 있다. 마찬가지로 특정 사용자가 추천한 질문을 얻기 위해서는 some_user.voter_question.all() 처럼 사용할 수 있다. some_user 는 특정 사용자를 의미한다.
Answer 모델에도 voter 속성을 다음처럼 추가하자.
projects\mysite\pybo\models.py 를 수정하자.
Answre 모델에도 author, voter 속성에 related_name 인수를 추가했다. 다시 makemigrates 와 migrate 를 실행하자.
오류없이 잘 실행된다.
[질문 추천]
이제 질문 추천 기능을 만들어보자.
projects\mysite\templates\pybo\question_detail.html 를 수정하자.
질문의 추천 버튼을 질문의 수정 버튼 좌측에 추가했다. 그리고 버튼에는 추천수도 함께 보이도록 했다. 추천 버튼을 클릭하면 href의 속성이 javascript:void(0) 으로 되어 있기 때문에 아무런 동작도 하지 않는다. 하지만 class 속성에 recommend 를 추가하여 자바스크립트로 data-uri 에 정의된 URL이 호출되게 할 것이다. 이와 같은 방법을 사용하는 이유는 추천 버튼을 눌렀을때 확인창을 통해 사용자의 확인을 구하기 위함이다.
그 확인창을 만들어 보자.
projcets\mysite\templates\pybo\question_detail.html 을 수정하자.
오타가 안나게 잘 따라 작성하자
추천 버튼에 class="recommend" 가 적용되어 있으므로 recommend 라는 이름을 가진 클래스가 있으면 명령문이 실행되어 '정말로 추천하시겠습니까?' 라는 확인창이 나오고 확인을 누르면 data-uri 속성에 정의한 URL 이 호출된다.
question_detail.html 에 적은것 처럼 {% url 'pybo:question_vote' question.id %} URL이 추가되었으므로 pybo/urls.py 에 다음처럼 URL매핑 규칙을 추가해야 한다.
projects\mysite\pyob\urls.py 를 수정하자.
그리고 URL 매핑에 의해 실행되는 question_vote 함수도 작성해줘야 한다.
projects\mysite\pybo\views\question_views.py 에 다음과 같이 작성하자.
작성한 것을 보면 알 수 있듯이 로그인한 사용자와 추천하려는 질문의 글쓴이가 같은 유저일 경우 추천할 수 없게 했다. Question 모델의 voter 는 여러사람을 추가할 수 있는 ManyToManyField 이므로 question.voter.add(request.user) 처럼 add 함수를 사용하여 추천인을 추가한다. 동일한 사용자가 동일한 질문을 여러번 추천해도 추천수가 증가하지 않는다. 오류도 발생하지 않는다. 이는 ManyToManyField 내부에서 자체적으로 처리된다. 서버를 재시작해서 테스트를 해보자. 잘 작동되는지 확인하자.
[답변 추천]
답변 추천도 질문 추천과 동일하다. 먼저
projects\mysite\templates\pybo\question_detail.html 을 작성하자.
{% url 'pybbo:answer_vote' answer.id %} 이 추가 되어 URL 매핑규칙을 추가하자.
projects\mysite\pybo\urls.py 를 수정하자.
URL 매핑에 의해 실행되는 answer_vote 함수를 작성해준다.
projects\mysite\pybo\views\answer_views.py 에 작성해주자.
점프 투 장고 튜토리얼을 계속 따라하고있는데 계속 하다보면 views.py 에 함수가 계속 늘어나서 불편함을 느끼는데 이를 해결해보자.
이를 해결하기 위해서는 2가지 방법이 있는데 2가지 방법을 다 해보자. 먼저 첫 번째 방법을 한 후에 두 번째 방법을 할 수 있기 때문에 먼저 첫번째 방법부터 따라해보자.
[첫 번째 방법]
첫 번째 방법은 views.py 파일만 여러 파일로 분리하고 나머지는 고치지 않아도 되는 변화가 적은 방법이다.
먼저 projects\mysite\pybo\views 디렉토리를 만들자.
views.py 파일에 정의된 함수를 기능별로 views 디렉토리에 다음의 파일로 저장하자.
파일명
기능
함수
base_views.py
기본관리
index, detail
question_views.py
질문관리
question_create, question_modify, question_delete
answer_views.py
답변관리
answer_create, answer_modify, answer_delete
projects\mysite\pybo\views\base_views.py 파일을 만들고 다음과 같이 작성하자.
pybo\views.py 에 있던 index 함수와 detail 함수의 내용을 그냥 복사 붙여넣기를 하면 된다. import 문맥만 수정해주면된다. pybo\views.py 에서는 from .models import Question 처럼 같은 디렉토리에 있는 models.py 모듈을 import 했지만 지금은 views 디렉토리 하위에 base_views.py 파일이 위치하므로 from ..models import Question 처럼 부모디렉토리의 models.py 모듈을 import 해야 한다.
projects\mysite\pybo\views\question_views.py 파일을 새로 만들고 작성하자.
views.py 파일에서 함수들을 복사해 붙여넣기하자.
projects\mysite\pybo\views\answer_views.py 파일을 새로 만들고 내용을 복사 붙여넣기하자.
그리고 projects\mysite\pybo\views\__init__.py 파일을 새로 만들고 다음과 같이 작성하자.
views 디렉토리의 __init__.py 파일에서 views 디렉토리에 있는 base_views.py 등의 모든 뷰 파일의 함수를 import 했기 때문에 pybo\urls.py 와 같은 다른 모듈에서 views.py 모듈의 함수를 사용하는 부분을 수정할 필요가 없다.
그리고 이제 pybo\views.py 파일을 삭제하자.
테스트해보면 아무런 문제가 없다.
[두 번째 방법]
첫 번째 방법에는 단점이 있다.
장고는 디버깅시 보통 urls.py 파일에서 URL 에 매핑된 함수를 찾는것으로 시작한다. 하지만 첫 번째 방법을 사용하면 urls.py 파일에 매핑된 함수명은 알 수 있지만 어떤 뷰 파일의 함수인지는 알 수가 없다. 이는 views 디렉토리의 모든 뷰 파일을 찾아봐야 하는 불편함을 초래하게 한다. 이러한 이유로 혼자가 아닌 여러명이 함께 하는 프로젝트라면 첫 번째 방법은 절대로 추천하지 않는다고 한다.
두 번째 방법은 views 디렉토리의 __init__.py 파일을 제거하고 pybo/urls.py 에서 views.index 대신 base_views.index 와 같이 전체 경로를 써주는 방법이다. 먼저 views 디렉토리의 __init__.py 파일을 제거하자.
그 후 projects\mysite\pybo\urls.py 파일을 다음과 같이 수정하자.
URL 매핑시 views.index 가 아닌 base_views.index 이런식으로 모듈명이 표시되도록 바꾸었다. 이제 어떤 뷰 파일의 함수인지 명확하게 인지할 수 있다.
projects\mysite\config\urls.py 파일도 다음과 같이 수정하자.
이 파일도 index 에 해당되는 URL 매핑을 좀 더 명확하게 수정했다.
두 번째 방법은 프로그램을 모두 하나하나 수정해 주어야하는 단점이 있지만 좀 더 명확해지는 장점이 있다.
질문 수정 및 삭제를 만들었으니 답변 수정및 삭제도 만들어줘야 한다. 질문 수정 및 삭제 방법과 유사하게 진행된다. 답변 수정은 답변 등록 템플릿이 따로 없으므로 답변 수정에 사용할 템플릿이 추가로 필요하다. 답변 등록은 질문 상세 화면 아래쪽에 텍스트 입력창을 추가해 만든것 이므로 질문 상세 템플릿은 답변 수정용으로는 적합하지 않다.
먼저 답변수정버튼을 추가하자.
projects\mysite\templates\pybo\question_detail.html 을 수정하자.
위에서 {% url 'pybo:answer_modify' answer.id %}가 추가되었기 때문에 pybo/urls.py 에 URL 매핑을추가하면 된다.
projects\mysite\pybo\urls.py 에 추가하자.
views.answer_modify 함수를 참조한다고 했으니 함수를 새로 만들어주자.
projects\mysite\pybbo\views.py 에 만들자.
내용은 질문 수정 함수와 유사하다.
answer_form.html 을 참조한다고 적었지만 아직 그 폼이 없으므로 새로 작성해주자.
projects\mysite\templates\pybo\answer_form.html 을 새로 만들고 작성해주자.
답변 수정도 질문 수정과 삭제 처럼 로그인한 사용자와 답변을 작성한 사용자가 같을 경우에만 수정 버튼이 나타난다.
작동도 잘 되는지 테스트 해보자.
[답변 삭제]
답변 삭제버튼을 추가하기위해
projects\mysite\templates\pybo\question_detail.html 을 수정하자.
수정 버튼 옆에 삭제 버튼을 만들었고, 삭제 버튼에 delete 클래스를 적용했으므로 삭제 버튼을 누르면 data-uri 속성에 설정한 url 이 실행될 것이다. {% url 'pybo:answer_delete' answer.id %} 이 추가 되었기 때문에 pybo/urls.py 에 URL 매핑 규칙을 추가하자.
projects\mysite\pybbo\urls.py 를 수정하자.
views.answer_delete 함수를 참조한다고 했기때문에 새로 작성해주자.
projects\mysite\pybo\views.py 에 추가하자.
이제 로그인한 사용자와 답변 작성자가 같으면 삭제 버튼이 나온다.
잘 작동하는지 테스트도 해보자.
[수정일시 표시]
이제 마지막으로 수정일시를 표시해보자.
projects\mysite\templates\pybo\question_detail.html 파일을 다음과 같이 수정하자.
null=True 는 데이터베이스에서 modify_date 칼럼에서 null을 허용한다는 의미이며, blank=True 는 form.is_valid()를 통한 입력 데이터 검증시 값이 없어도 된다는 의미이다. 즉, 아직 modify_date 값이 없다 = 아직 수정을 하지 않았을 경우 값을 비워둘 수 있다는것을 의미한다. 모델이 변경되었으므로 makemigrations, migrate 명령을 수행하자.
[질문 수정]
먼저 질문 수정 버튼을 만들어주자.
projects\mysite\templates\pybo\question_detail.html 를 수정하자.
해석하면 수정 버튼은 로그인한 사용자와 글쓴이가 동일한 경우에만 노출되도록 {% if request.user == question.author %} 을 적용했다. 그 아래는 버튼을 구성한 것이다. 그리고 {% url 'pybo:question_modify' question.id %} URL이 추가되었으므로 pybo/urls.py 에 URL 매핑규칙을 추가해야한다.
projects\mysite\pybo\urls.py 를 수정하자.
보면 알겠지만 views.question_modify 함수를 참조한다고 적어놨다. 아직 우리는 저 함수가 없기 때문에 새로 만들어주자,
projects\mysite\pybo\views.py 에 추가하자.
question_modify 함수는 로그인한 사용자(request.user)와 수정하려는 질문의 글쓴이(question.author)가 다르면 '수정권한이 없습니다' 오류를 발생한다. 이 오류를 발생시키기 위해 messages 모듈을 이용했다. messages 는 장고가 제공하는 모듈로 넌필드 오류를 발생시킬때 사용한다.
'수정' 버튼을 클릭하면 http://localhost:8000/pybo/question/modify/2/ 페이지가 GET 방식으로 호출되어 질문수정 화면이 보여진다. 질문 수정화면에서 사용한 템플릿은 질문 등록시 사용했던 pybo/question_form.html 파일과 동일하다. 질문 수정화면에서 저장하기 버튼을 클릭하면 http://localhost:8000/pybo/question/modify/2/ 페이지가 POST 방식으로 호출되어 데이터가 수정된다. form 태그에 action 속성이 없는 경우 디폴트 action 은 현재 페이지가 되기 때문이다.
GET 요청인 경우 질문수정 화면에 조회된 질문의 제목과 내용이반영될 수 있도록 form = QuestionForm(instance=question) 을 추가했다. 폼 생성시 이처럼 instance 값을 지정하면 폼의 속성 값이 instance 값으로 채워진다. 따라서 질문을 수정하는 화면에서 제목과 내용이 채워진 채로 보일 것이다.
POST 요청인 경우 수정된 내용을 반영해야 하는 케이스 이므로 form = QuestionForm(request.POST, instance=question) 을 추가해주었다. 이 코드의 의미는 instance 를 기준으로 QuestionForm 을 생성하지만 request.POST 의 값으로 덮어쓰라는 의미이다. 따라서 질문 수정화면에서 제목 또는 내용을 변경하여 POST 로 요청하면 변경된 내용이 QuestionForm 에 저장된다.
질문 수정일시는 question.modify_date = timezone.now() 로 현재일시를 저장한다.
길게 적혀있어 어렵지만 내가 이해한 바로 설명해보자면, 질문 수정버튼을 누르면 GET 방식으로 된다는 것을 의미하고. GET 방식으로 요청된 경우에는 질문수정 화면이 나오는데 이 때 양식은 질문생성할때의 question_form.html 과 동일하고, 안의 내용들은 수정하려던 질문의 제목 과 내용이 자동으로 채워져있다. 이 상태에서 내용이나 제목을 수정해 수정하기 버튼을 누르는 것은 POST 방식으로 요청한다는 것같다. POST 방식으로 요청한 경우 수정한 내용들로 원래 있던 내용과 제목에 덮어쓰라는 것을 의미한다. 그리고 수정했기 때문에 값이 비어있던 수정일시도 현재시간으로 추가해 저장한다. 이러한 의미인것같다.
messages 모듈에 의해 발생되는 '수정권한이 없습니다' 오류가 표시될 수 있도록 질문 상세 화면 위쪽에 오류 영역을 추가해야한다.
projects\mysite\templates\pybo\question_detail.html 을 수정하자.
수정은 로그인 한 사용자와 글 작성자가 동일한 경우에만 가능하기 때문에 오류는 표시되지 않지만. 비 정상적인 방법으로 질문을 수정할 경우에 이 오류를 보여주어야 한다.
테스트해보자.
로그인 사용자와 작성자가 같을경우 수정 버튼이 보이고, 아닐경우 보이지 않는다. 수정 버튼을 눌렀을 경우에도 잘 작동되는지 확인해보자. 잘 작동되지만 아직은 수정일시가 표시되지는 않는다.
[질문 삭제]
질문삭제 버튼을 추가하기위해
projects\mysite\templates\pybo\question_detail.html 을 수정하자.
data-url 이 아니고 data-uri 이다. 스펠링을 잘 확인해보자.
삭제버튼은 수정버튼과 달리 href 속성 값을 javascript:void(0) 으로 설정했다. href 속성값을 이렇게 설정하면 해당 링크를 클릭해도 아무런 동작도 하지 않는다. 그리고 삭제를 실행할 URL을 얻기 위해 data-url 속성을 추가하고, 삭제 버튼이 눌리는 이벤트를 확인할 수 있도록 class 속성에 'delete' 항목을 추가했다. data-uri 속성은 자바스크립트에서 클릭 이벤트 발생시 this.dataset.uri와 같이 사용하여 그 값을 얻을 수 있다. href 에 삭제 URL을 직접 사용하지 않고 이러한 방식을 사용하는 이유는 삭제 버튼을 클릭했을때 '정말 삭제 하시겠습니까?' 같은확인창이 필요하기 때문이다.
삭제 버튼을 눌렀을 때 확인창을 나오게 하기 위해서는 다음과 같은 자바스크립트가 필요하다. 일단 눈으로만 보며 뜻을 알아보자.
이 자바스크립트의 의미는 delete 라는 클래스를 포함하는 컴포넌트(버튼이나 링크) 를 클릭하면 "정말로 삭제하시겠습니까?" 라는 질문을 하고 "확인" 버튼을 선택했을때 해당 컴포넌트의 data-uri 값으로 URL 호출을 하라는 의미이다. "확인" 대신 "취소"를 선택하며 아무런 일도 발생하지 않는다. 이와 같은 스크립트를 추가하면 "삭제" 버튼을 클릭하고 "확인" 버튼을 선택하면 data-uri 속성에 해당하는 {% url 'pybo:question_delete' question.id %} 이 호출된다.
자바스크립트는 HTML 구조에서 </body> 태그 바로 위에 삽입하는 것을 추천한다. 왜냐하면 이렇게 해야 화면 렌더링이 완료된 후에 자바스크립트가 실행되기 때문이다. 화면 렌더링이 완료되지 않은 상태에서 자바스크립트를 실행하면 화면의 값을 읽지 못하는 오류가 발생할수도 있고 화면 로딩이 지연되는 문제가 발생할 수 도 있다.
projects\mysite\templates\base.html 을 다음과 같이 수정하자.
base.html 을 상속하는 템플릿들에서 content 블록을 구성했던것 처럼 script 를 구현한다.</body> 태그 바로 위에 {% block script %}, {% endblock %} 블록을 추가했다. 이렇게하면 base.html 을 상속하는 템플릿은 자바스크림트의 삽입위치를 신경쓸 필요없이 저 블록을 사용해 자바스크립트를 작성하면 된다.
이를 이용해
projects\mysite\templates\pybo\question_detail.html 을 다음과 같이 수정하자.
그리고 data-uri 에 {% url 'pybo:question_delete' question.id %} URL이 추가되었으므로 pybo/urls.py 에 매핑규칙을 추가해야한다.
<th>글쓴이</th>로 항목을 추가하고 th 엘리먼트를 가운데 정렬하도록 tr 엘리먼트에 text-center 클래스를 추가하고, 제목의 너비가 전체의 50%가 되도록 style="width:50%" 를 지정했다. <td>{{ question.author.username }}</td> 를 추가해 질문의 글쓴이를 표시했다. 테이블 내용을 가운데 정렬하도록 tr 엘리먼트에 text-center 를 추가하고, 제목을 왼쪽 정하도록 td 엘리먼트에 text-start 클래스를 추가했다. 테이블 항목도 3개에서 글쓴이를 추가해 4개가 되었으므로 colspan 을 4로 수정했다. 테스트 해보자.
이제 질문상세템플릿에도 author 를 추가해 질문과 답변에 글쓴이와 작성일시가 같이 보이도록 수정하자.
projects\mysite\templates\pybo\question_detail.html 을 수정하자.
질문, 답변을 달면 누가 글을 작성했는지 알려주는 작성자 항목이 필요하다. Question, Answer 모델에 글쓴이 에 해당되는 author 속성을 추가해야한다.
먼저 질문 모델에 추가하기 위해 projects\mysite\pybo\models.py
author 필드는 User 모델을 ForeignKey 로 적용하여 선언했다. User 모델은 django.contrib.auth 앱이 제공하는 사용자 모델로 회원 가입시 데이터 저장에 사용했던 모델이다. on_delete=models.CASCADE 는 계정이 삭제되면 이 계정이 작성한 질문을 모두 삭제하라는 의미이다.
모델을 변경한 후에는 반드시 makemigrations 와 migrate 로 데이터베이스를 변경해 주어야한다.
python manage.py makemigrations 명령어를 실행하자.
명령어를 입력하면 옵션을 선택하라는 메세지가 나온다. Question 모델에 author 를 추가하면 이미 등록되어 있던 게시물에 author 에 해당되는 값이 저장되어야하는데, author 에 어떤 값을 넣어야 하는지 모르기 때문이다. 그것에 관해 어떤 옵션을 선택하라는지 묻는것이다.
1번 옵션은 one-off default 값을 제공한다. 현재 NULL 필드(author 필드) 에 값을 제공하겠다는 의미. 2번 옵션은 실행을 취소하고 models.py 에 default 값을 추가 하겠다는 옵션이다. 우리는 1번 옵션을 선택할 것이다.
1번을 입력하면
그렇게 하면 파이썬 셸이 가동된다 기본값을 지금 입력해달라는 뜻이다. 여기서 1을 다시 입력한다.
여기서 입력한 1은 admin 계정의 id 값이다. 따라서 기존 게시물의 author 에는 admin 계정이 등록된것이다. 계정을 만들때마다 id 가 1부터 순차적으로 증가하는데 처음에 만들었던 슈퍼유저 admin 이 1 인것이다.
다 했다면 python manage.py migrate 로 데이터베이스에 적용하자.
Answer 모델에도 author 속성을 똑같이 추가하자.
그리고 저번처럼 python manage.py makemigrations 명령어로를 한후 옵션을 아까처럼 선택하자.
이렇게 하면된다. 그렇게하면 migrate 할 때 데이터베이스에 null 허용 컬럼으로 생성된다.
Question, Answer 모델에 author 속성이 추가되었으므로 질문과 답변 저장시에 author도 함께 저장해야 한다.
projects\mysite\pybo\views.py 파일을 다음과 같이 수정하자.
답변과 질문의 글쓴이는 현재 로그인한 계정이므로 answer.author = request.user 로 처리했다. request.user 는 현재 로그인한 계정의 User 모델 객체이다. 이제 다시 서버를 시작하고 테스트 하면 잘 된다.
하지만 로그아웃 상태에서 질문 또는 답변을 등록하면 다음과 같은 ValueError가 발생한다.
이 오류는 request.user 가 User 객체가 아닌 AnonymousUser 객체라서 발생한 것이다. 로그인 상태일땐 User 로그아웃일땐 AnonymousUser 이다. 앞에서 author 속성을 정의할 때 User를 이용하도록 했는데 로그아웃시에는 author 에 User 가 아닌 AnonymousUser 가 대입되어 오류가 발생한 것이다.
이를 해결하기 위해선 request.user 를 사용하는 함수에 @login_required 애너테이션을 사용해야 한다. 이 애너테이션이 붙은 함수는 로그인이 필요한 함수를 의미한다.
projects\mysite\pybo\views.py 를 다음과 같이 수정하자.
질문생성과 답변생성함수는 함수내에서 request.user 를 사용하므로 로그인이 필요한 함수이기 때문에 위에 @login_required 어노테이션을 사용했다. 로그아웃상태에서 실행을 하려고 하면 로그인 화면으로 이동한다.
실제로 로그아웃상태에서 질문등록하기를 누르면 로그인 화면으로 이동이 된다. 이 때의 URL 을 확인해보자.
URL을 확인해보면 next 파라미터가 있는데 이는 로그인 성공후 next 파라미터에 있는 URL로 페이지를 이동하겠다는 의미이다. 하지만 실제로 해보면 로그인을 하면 원래 하려던 질문 등록페이지로 안가고 홈페이지로 가진다. 이를 해결하려면 로그인 템플릿에 hidden 타입의 next 항목을 추가해야한다.
projects\mysite\templates\common\login.html 을 다음과 같이 수정하자.
이렇게 하면 로그인 후 next 항목의 URL 로 이동한다.
로그아웃시에 질문 작성하기버튼을 눌렀을때의 오류는 해결했지만 로그아웃상태일때 답변을 달려고 할 때 가 있다. 로그아웃 상태에서도 저장하기 버튼을 누르면 로그인 화면으로 자동으로 넘어가지만 문제는 작성해놓은게 사라진다는 점이다. 작성하기 버튼을 눌렀을때 로그인 화면으로 이동하게 하는것보다 아예 로그아웃상태이라면 작성을 못하게 하는게 좋다.
projects\mysite\templates\pybo\question_detail.html 파일을 다음과 같이 수정하자.
의미를 해석하자면 로그인 상태가 아닌 경우 textates 태그에 disabled 속성을 적용하여 입력을 못하게 만들었다. 보면 알겠지만 {% if not user.is_authenticated %} 태그는 현재 사용자가 로그아웃 상태인지를 체크하는 태그이다.
로그아웃상태일때는 답변내용이 사진과 같이 사용하지 못하게 막혀있다. 이 상태에서 답변등록 버튼을 누르면 로그인 화면으로 이동한다. 그리고 로그인을 하면 오류가 발생할것이다. 로그인시 전달된 next 파라미터 때문에 로그인 후에 답변등록 URL 인 /answer/create 가 GET 방식으로 호출되기 때문이다. 하지만 답변등록시 POST 방식이 아닌경우 HttpResponseNotAllowed 오류가 발생하도록 코딩했다 그래서 오류가 발생하는것이다. 이를 해결하기위해
projects\mysite\pybo\views.py 파일을 다음과 같이 수정하자.
from django.http import HttpResponseNotAllowed 를 삭제하고. else 부분에 form = AnswerForm() 를 추가하자.
projects\mysite\templates\navbar.html 파일을 다음과 같이 수정하자.
로그인/로그아웃 링크 바로 옆에 회원가입 링크를 추가했다. 회원가입은 로그아웃 상태에서만 보일수있게 했다. {% url 'common:signup' %} 태그를 적어넣었으므로 URL매핑규칙을 추가해줘야 한다. projects\mysite\common\urls.py 에 다음과 같이 추가하자.
회원가입 링크를 누르면 views.signup 함수가 실행된다. 이제 이 함수를 만들어 줘야하지만 그 전에 일단 계정생성시 사용할 UserForm을 projects\mysite\common\forms.py 파일을 추가해 새로작성하자.
UserForm 은 djnago.contrib.auth.forms 모듈의 UserCreationForm 클래스를 상속하여 만들었다. 그리고 email 속성을 추가했다. UserForm 을 따로 만들지 않고 UserCreationForm 을 그대로 사용해도 되지만 위처럼 이메일 등의 속성을 추가하기 위해서는 UserCreationForm 클래스를 상속해서 만들어야 한다. password2 는 password1 을 제대로 입력했는지 대조하기 위한 값이다.
이제 아까 만들어야했던 views.signup 함수를 작성하자. projects\mysite\common\views.py 파일에 다음과 같이 작성하자.
signup 함수는 POST 요청인 경우에는 화면에서 입력한 데이터로 사용자를 생성하고 GET 요청인 경우에는 회원가입 화면을 보여준다. form.cleaned_data.get 함수는 폼의 입력값을 개별적으로 얻고 싶은 경우에 사용하는 함수로 여기서는 인증시 사용할 사용자명과 비밀번호를 얻기 위해 사용한다. 사용자를 생성한 후에 자동 로그인 될 수 있도록 authenticated, login 함수가 사용되었다. authenticated 와 login 함수는 django.contrib.auth 모듈의 함수로 사용자 인증과 로그인을 담당한다.
이제 회원가입 화면을 구성하는 common/signup.html 템플릿을 작성해야한다.
projects\mysite\templates\common\signup.html 파일을 추가하고 다음과 같이 작성하자.
form 태그 밑에는 오류를 표시하기위해 form_errors.html 템플릿을 include 했다.
이제 테스트를 해보자.
비밀번호와 비밀번호 확인이 다를 경우 오류가 발생한다. 일단 id 는 stdiohan 비밀번호는 testpasswd 로 설정했다. 그리고
http://localhost:8000/admin/ 페이지를 요청해보면
이런 경고 메세지가 나온다. 어드민 페이지는 슈퍼유저로 접속해야한다. 저번에 만든 아이디 admin 비밀번호 1111 로 로그인해 보면