본문 바로가기
Research/Django

[Django] 점프 투 장고 튜토리얼 - 02-6. 데이터 저장

by RIEM 2021. 11. 11.

Django 점프 투 장고 정리

작성일 : 2021-11-09

문서버전 : 1.0

 

개요

이 문서는 점프 투 장고 사이트의 장고 튜토리얼 학습 내용을 정리한 내용입니다.

레퍼런스

점프 투 장고 https://wikidocs.net/72242

 

2-6.데이터 저장

답변 등록하는 기능을 만들어보자.

답변등록

질문 상세 템플릿에 다음처럼 답변을 저장할 수 있는 폼(form)을 추가하자.

> ../projects/mysite/templates/pybo/question_detail.html

 
<h1>{{ question.subject }}</h1>

<div>
    {{ question.content }}
</div>

<form action="{% url 'pybo:answer_create' question.id %}" method="post">
{% csrf_token %}
<textarea name="content" id="content" rows="15"></textarea>
<input type="submit" value="답변등록">
</form>

 

답변 내용 입력할 수 있는 텍스트창, 답변 저장할 수 있는 답변등록 버튼을 추가했다. 답변 저장을 위한 URL은 form 태그의 action 속성에 {% url ‘pybo:answer_create’ question.id %}로 지정했다.

 

{% csrf_token %}은 보안 관련 항목이다. Form으로 전송한 데이터가 실제 웹 페이지에서 작성한 데이터인지 판단한다. 해커가 다른 방법으로 데이터를 전송할 경우 서버에서 발행한 csrf_token 값과 툴에서 보낸 csrf_token 값의 불일치로 오류를 발생시킨다.

 

따라서 {% scrf_token %}의 위치는 form 태그 바로 밑에 두어야 한다.

 

Csrf_token 사용을 위해 CsrfViewMiddleware 미들웨어가 필요하지만 이 미들웨어는 settings.py의 MIDDLEWARE 항목에 디폴트로 추가되어 있으므로 별도의 설정은 필요없다. 

> ../projects/mysite/config/settings.py

 
...
MIDDLEWARE = [
  'django.middleware.security.SecurityMiddleware',
  'django.contrib.sessions.middleware.SessionMiddleware',
  'django.middleware.common.CommonMiddleware',
  'django.middleware.csrf.CsrfViewMiddleware',
  'django.contrib.auth.middleware.AuthenticationMiddleware',
  'django.contrib.messages.middleware.MessageMiddleware',
  'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
...

 

URL 매핑

위에서 상세 템플릿을 고쳤으니 이제 상세페이지를 요청해보자.

 

Answer_create 별칭이 없다는 오류가 발생했다. 왜냐하면 질문 상세 템플릿에 {% url ‘pybo:answer_create’ question.id %}과 같은 별칭을 사용했기 때문이다.


> pybo/urls.py

오류 해결을 위해 아래와 같이 URL 매핑을 등록하자

from django.urls import path

from . import views

app_name = 'pybo'

urlpatterns = [
  path('', views.index, name='index'),
  path('<int:question_id>/', views.detail, name='detail'),
  path('answer/create/<int:question_id>/', views.answer_create, name='answer_create'),
]

 

http://localhost:8000/pybo/answer/create/2/ 와 같은 페이지 요청 시 views.answer_create 함수가 호출된다.

 

View 함수

위 매핑에서 정의된 views.anwer_create 함수를 아래 추가하자.

> pybo/views.py

from django.shortcuts import render, get_object_or_404, redirect
from django.utils import timezone
from .models import Question

(... 생략 ...)

def answer_create(request, question_id):
    """
    pybo 답변등록
    """
    question = get_object_or_404(Question, pk=question_id)
    question.answer_set.create(content=request.POST.get('content'), create_date=timezone.now())
    return redirect('pybo:detail', question_id=question.id)

 

Answer_create 함수의 매개변수 question_id는 URL 매핑에 의해 값이 전달된다. 만약 http://localhost:8000/pybo/answer/create/2/ 라는 페이지를 요청할 경우 매개변수 question_id에 2가 전달된다.

 

answer_create 함수의 첫번째 매개변수 request를 통해 답변 등록시 텍스트창에 입력한 내용을 읽는다. request.POST.get(‘content’)는 POST로 전송된 폼(form) 데이터 항목 중 content 값을 의미한다.

 

답변 생성 위해 question.answer_set.create를 사용했다. Question.answer_set은 질문의 답변을 의미한다. 이렇게 사용할 수 있는 것은 Question과 Answer 모델은 서로 ForeignKey로 연결되어 있기 때문이다.

 

답변을 저장하는 또 다른 방법이 있다. 이를 방법B라고 하자.

 
(... 생략 ...)
from .models import Question, Answer

(... 생략 ...)

def answer_create(request, question_id):
    """
    pybo 답변등록
    """
    question = get_object_or_404(Question, pk=question_id)
    answer = Answer(question=question, content=request.POST.get('content'), create_date=timezone.now())
    answer.save()
    return redirect('pybo:detail', question_id=question.id)



정리해보자.

 

답변등록 방법A

 
def answer_create(request, question_id):
    """
    pybo 답변등록
    """
    question = get_object_or_404(Question, pk=question_id)
    question.answer_set.create(content=request.POST.get('content'), create_date=timezone.now())
    return redirect('pybo:detail', question_id=question.id)

 

답변등록 방법B

def answer_create(request, question_id):
    """
    pybo 답변등록
    """
    question = get_object_or_404(Question, pk=question_id)
    answer = Answer(question=question, content=request.POST.get('content'), create_date=timezone.now())
    answer.save()
    return redirect('pybo:detail', question_id=question.id)

 

방법 A, B의 결과는 동일하다. 

 

Redirect 함수는 페이지 이동을 위한 함수다. 답변 생성 후에는 redirect() 함수를 사용해 질문 상세 화면을 다시 보여주게 했다. detail 별칭은 question_id가 필요하므로 question.id를 인수로 전달했다.


> localhost:8000/pybo/2/

답변 등록은 되지 않는다. 등록된 답변을 표시하는 기능을 템플릿에 추가하지 않은 상태이기 때문이다.

 

답변 조회

등록된 답변을 질문 상세 화면에 표시하기 위해 다음과 같이 질문 상세 템플릿을 수정해야 한다.

> projects/mysite/templates/pybo/question_detail.html

<h1>{{ question.subject }}</h1>

<div>
    {{ question.content }}
</div>

<h5>{{ question.answer_set.count }}개의 답변이 있습니다.</h5>
<div>
    <ul>
    {% for answer in question.answer_set.all %}
        <li>{{ answer.content }}</li>
    {% endfor %}
    </ul>
</div>

<form action="{% url 'pybo:answer_create' question.id %}" method="post">
{% csrf_token %}
<textarea name="content" id="content" rows="15"></textarea>
<input type="submit" value="답변등록">
</form>

 

중간에 답변 확인할 수 있는 영역을 추가했다. question.answer_set.count 는 답변의 총 갯수를 의미한다. 위와 같이 수정하고 상세 화면을 호출하면 아래와 같이 나온다.

 

답변을 확인할 수 있게 되었따.

 

댓글