Django view 관리
views.py 정리를 통한 view 관리
1. view 모듈 분리¶
기능을 추가하다보면 views.py가 점점 거대해지면서 관리가 어려워지는데, 이를 개선하려면 Python의 패키지 기능을 사용해서 views.py 모듈을 패키지로 분리해 관리하면 된다.
우선 board_qna/views 디렉토리를 만들고, 아래 내용으로 __init__.py 파일을 만들어준다.
__init__.py 파일은 해당 폴더를 Python 패키지로 인식하도록 해서 Python이 디렉토리와 디렉토리 안의 모듈을 혼동하는 일을 방지해주는 파일이고, __all__를 통해 아래와 같이 *을 통해 패키지 내의 모든 모듈을 import하려고 할 때 import 되는 모듈을 정의해준다.
2. view 정리¶
board_qna/views 디렉토리에 base.py 파일을 만들고, 아래와 같이 board_qna 앱의 기본 기능과 관련된 view들을 모아준다.
from django.shortcuts import render, get_object_or_404
from django.core.paginator import Paginator
from board_qna.models import Question
def index(request):
"""index view for question_list"""
page = request.GET.get(key='page', default='1') # get value of 'page' from HTTP Request
question_list = Question.objects.order_by('-id') # order by id desc
paginator = Paginator(object_list=question_list, per_page=10) # number of object per page
page_obj = paginator.get_page(number=page) # page to return
total_pages = paginator.num_pages # get number of total pages
context = {'question_list': page_obj, 'total_pages': total_pages} # total_page is for template filter
return render(request=request, template_name='board_qna/question_list.html', context=context)
def detail(request, question_id):
"""view for details of each question"""
question = get_object_or_404(Question, pk=question_id) # returns 404 instead of 500 when requested not existing question_id
context = {'question': question}
return render(request, 'board_qna/question_detail.html', context)
board_qna/views 디렉토리에 question.py 파일을 만들고, 아래와 같이 board_qna 앱의 질문과 관련된 view들을 모아준다.
from django.shortcuts import render, get_object_or_404, redirect
from django.utils import timezone
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from board_qna.models import Question
from board_qna.forms import QuestionForm
@login_required()
def question_create(request):
"""view for create question"""
if request.method == 'POST':
form = QuestionForm(request.POST)
if form.is_valid():
question = form.save(commit=False) # temporal saving with commit=False option
question.user = request.user # 'request.user' returns current login user
question.date_create = timezone.now() # add time data to form
question.save()
return redirect('board_qna:index')
else:
form = QuestionForm()
context = {'form': form}
return render(request, 'board_qna/question_form.html', context)
@login_required()
def question_modify(request, question_id):
question = get_object_or_404(Question, pk=question_id)
if request.user != question.user: # blocking invalid approach
messages.error(request, '수정 권한이 없습니다')
return redirect('board_qna:detail', question_id=question.id) # type: ignore
if request.method == "POST":
form = QuestionForm(data=request.POST, instance=question) # override instance with requested POST
if form.is_valid():
question = form.save(commit=False)
question.date_modify = timezone.now() # add current time to form
question.save()
return redirect('board_qna:detail', question_id=question.id)
else:
form = QuestionForm(instance=question) # fill form with current context
context = {'form': form}
return render(request, 'board_qna/question_form.html', context)
@login_required()
def question_delete(request, question_id):
question = get_object_or_404(Question, pk=question_id)
if request.user != question.user:
messages.error(request, '삭제 권한이 없습니다')
return redirect('board_qna:detail', question_id=question.id) # type: ignore
question.delete()
return redirect('board_qna:index')
board_qna/views 디렉토리에 answer.py 파일을 만들고, 아래와 같이 board_qna 앱의 답변과 관련된 view들을 모아준다.
from django.shortcuts import render, get_object_or_404, redirect
from django.utils import timezone
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from board_qna.models import Question, Answer
from board_qna.forms import AnswerForm
@login_required()
def answer_create(request, question_id):
"""view for create answer"""
question = get_object_or_404(Question, pk=question_id)
if request.method == "POST":
form = AnswerForm(request.POST)
if form.is_valid():
answer = form.save(commit=False) # temporal saving with commit=False option
answer.user = request.user # 'request.user' returns current login user
answer.date_create = timezone.now() # add time data to form
answer.question = question
answer.save()
return redirect('board_qna:detail', question_id=question.id) # type: ignore
else:
form = AnswerForm()
context = {'question': question, 'form': form}
return render(request, 'board_qna/question_detail.html', context)
@login_required()
def answer_modify(request, answer_id):
answer = get_object_or_404(Answer, pk=answer_id)
if request.user != answer.user:
messages.error(request, '수정 권한이 없습니다')
return redirect('board_qna:detail', question_id=answer.question.id) # type: ignore
if request.method == "POST":
form = AnswerForm(request.POST, instance=answer)
if form.is_valid():
answer = form.save(commit=False)
answer.date_modify = timezone.now()
answer.save()
return redirect('board_qna:detail', question_id=answer.question.id)
else:
form = AnswerForm(instance=answer)
context = {'answer': answer, 'form': form}
return render(request, 'board_qna/answer_form.html', context)
@login_required()
def answer_delete(request, answer_id):
answer = get_object_or_404(Answer, pk=answer_id)
if request.user != answer.user:
messages.error(request, '삭제 권한이 없습니다')
else:
answer.delete()
return redirect('board_qna:detail', question_id=answer.question.id) # type: ignore
각 모듈을 정리한 후에 기존의 views.py를 삭제해준다.
3. URL 매핑¶
board_qna/urls.py를 아래와 같이 수정하여 변경된 모듈로 view를 매핑해준다.
from django.urls import path
from .views import *
app_name = 'board_qna'
urlpatterns = [
path('', base.index, name='index'), # name parameter is to set name of url variable for template
path('<int:question_id>/', base.detail, name='detail'), # url for listing board_qna
path('question/create/', question.question_create, name='question_create'),
path('question/modify/<int:question_id>/', question.question_modify, name='question_modify'),
path('question/delete/<int:question_id>/', question.question_delete, name='question_delete'),
path('answer/create/<int:question_id>/', answer.answer_create, name='answer_create'),
path('answer/modify/<int:answer_id>/', answer.answer_modify, name='answer_modify'),
path('answer/delete/<int:answer_id>/', answer.answer_delete, name='answer_delete'),
]