리셋 되지 말자

[점프 투 장고] 데이터 저장 본문

Django

[점프 투 장고] 데이터 저장

kyeongjun-dev 2020. 12. 1. 21:16

답변등록 버튼

상세조회 템플릿에 다음과 같이 답변을 저장할 수 있는 기능을 추가한다.

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은 {% url 'pybo:answer_create' question.id %} 이다.

form태그 바로 밑에 있는 {% csrf_token %} 은 보안에 관련된 항목으로, form으로 전송된 데이터가 실제 웹페이지에서 작성된 데이터인지를 판단해 주는 가늠자 역할을 한다. 기타 다른 해킹 툴등에 의해서 데이터가 전송될 경우에는 서버에서 발생한 csrf_token 값과 해당 툴에서 보낸 csrf_token 값이 일치하지 않기때문에 오류가 발생한다.

따라서 form태그 바로 밑에 {% csrf_token %} 을 항상 위치시키도록 해야 한다.

csrf_token 사용을 위해서는 CsrfViewMiddleware라는 미들웨어가 필요한데 이 미들웨어는 장고 프로젝트 생성시 장고 settings.py의 MIDDLEWARE 항목에 자동 추가된다.

MIDDLEWARE = [
    ...
    'django.middleware.csrf.CsrfViewMiddleware',
    ...
]

 

URL 추가

"답변등록"버튼 클릭시 이동하는 url을 추가해야 한다.

pybo/urls.py

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'),
]

localhost:8000/pybo/answer/create/2/ 와 같은 페이지를 요청하면 views.py의 answer_create 함수를 호출하라고 매핑을 추가했다.

 

뷰 함수 추가

매핑에 정의된 answer_create함수를 pybo/views.py 파일에 추가한다.

from django.shortcuts import render, get_object_or_404, redirect
from django.utils import timezone
from django.http import HttpResponse
from .models import Question
# Create your views here.
def index(request):
    """
    pybo 목록 출력
    """
    question_list = Question.objects.order_by('-create_date')
    context = {'question_list': question_list}
    return render(request, 'pybo/question_list.html', context)

def detail(request, question_id):
    """
    pybo 내용 출력
    """
    # question = Question.objects.get(id=question_id)
    question = get_object_or_404(Question, pk=question_id)
    context = {'question': question}
    return render(request, 'pybo/question_detail.html', context)

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매핑에 의해 전달된다. localhost:8000/pybo/answer/create/2/ 라는 페이지가 요청되면, question_id에는 2라는 값이 전달된다.

첫번째 매개변수 request 객체를 통해 폼에서 입력한 답변 데이터를 읽을 수 있다. request.POST.get('content') 는 POST로 전송된 form 데이터 항목 중 content의 값을 의미한다.

그리고 답변을 생성하기 위해 question.answer_set.create 를 사용했다. question.answer_set 은 질문의 답변을 의미한다. Question과 Answer 모델은 서로 ForeignKey 로 연결되어 있기 때문에 이처럼 사용이 가능하다.

답변을 저장하는 또 다른 방법은 다음처럼 Answer 모델을 직접 사용하는 방법이다.

question = get_object_or_404(Question, pk=question_id)
answer = Answer(question=question, content=request.POST.get('content'), create_date=timezone.now())
answer.save()

 

답변저장, 답변조회

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>

답변을 저장할 수 있다.

Comments