Django - 제너릭 뷰(설문조사 2)

윤형·2024년 9월 8일

Django

목록 보기
4/12

뷰 추가

"polls/views.py"

from django.http import HttpResponse
from django.template import loader
from .models import Question
from django.http import Http404
from django.shortcuts import get_object_or_404, render

def index(request):
    latest_question_list = Question.objects.order_by("-pub_data")[:5]
    temlplate = loader.get_template("polls/index.html")
    context ={
        "latest_question_list":latest_question_list,
    }
    return HttpResponse(temlplate.render(context,request))

def detail(request, question_id):
    question = get_object_or_404(Question,pk=question_id)
    return render(request, "polls/detail.html", {"question": question})

def results(request, question_id):
    respone = "You're looking at the result of question %s"
    return HttpResponse(respone % question_id)

def vote(request, question_id):
    return HttpResponse("You're voting on question %s" % question_id)

"-pub_data"에서 -은 내림차순을 뜻함
loader.get_templeate("polls/index.html")에서 polls/index.html은 직접 디렉토리를 만들고 그 안에 html문법을 통해 형태를 만들어야 한다.

"polls/urls.py"

from django.urls import path
from .import views

app_name = "polls"
urlpatterns = [
	# ex: /polls/
    path("", views.index, name="index"),
    # ex: /polls/5/
    path("<int:question_id>/",views.detail, name="detail"),
    path("<int:question_id>/results/",views.results,name= "results"),
    path("<int:question_id>/vote/",views.vote, name = "vote"),
]

path()를 호출해 만들어둔 뷰와 연결을 시켜 준다.
polls/urls.py 파일에 app_name을 추가하여 어플리케이션의 이름공간을 설정한다. 이름 공간을 설정하면 같은 이름을 가진 URL의 충돌을 방지할 수 있다.
ex) detail -> polls:detail


이제는 polls디렉토리 안에 templates디렉토리를 생성하고 안에 polls디렉토리를 만들고 그 하위에 index.html을 작성한다.
"polls/templates/polls/index.html"

{% if latest_question_list %}
    <ul>
    {% for question in latest_question_list %}
        <li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
    {% endfor %}
    </ul>
{% else %}
    <p>No polls are available.</p>
{% endif %}

"detail.html"

<form action="{% url 'polls:vote' question.id %}" method="post">
{% csrf_token %}
<fieldset>
    <legend><h1>{{ question.question_text }}</h1></legend>
    {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
    {% for choice in question.choice_set.all %}
        <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}">
        <label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br>
    {% endfor %}
</fieldset>
<input type="submit" value="Vote">
</form>
  1. 데이터 서버측을 변경하기 때문에 method = "post" 로 한다.
  2. 교차사이트 요청 위조를 방지하기 위해 {% csrf_token %}를 붙이고 시작함
  3. forloop.counter 는 for 태그가 반복을 한 횟수를 나타낸다
  4. radio 타입은 사용자가 선택하는 버튼을 생성함
  5. label for속성의 값은 버튼의 id와 일치해야 연결됨

"polls/view.py" 중 vote부분을 수정한다.

from django.db.models import F
from django.http import HttpResponse, HttpResponseRedirect
from .models import Question, Choice
from django.urls import reverse

def vote(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    try:
        selected_choice = question.choice_set.get(pk=request.POST["choice"])
    except (KeyError, Choice.DoesNotExist):
        # Redisplay the question voting form.
        return render(
            request,
            "polls/detail.html",
            {
                "question": question,
                "error_message": "You didn't select a choice.",
            },
        )
    else:
        selected_choice.votes = F("votes") + 1
        selected_choice.save()
        # Always return an HttpResponseRedirect after successfully dealing
        # with POST data. This prevents data from being posted twice if a
        # user hits the Back button.
        return HttpResponseRedirect(reverse("polls:results", args=(question.id,)))
        # ex) polls/3/results

기능별 정리

기능설명
질문 가져오기get_object_or_404(Question, pk=question_id)를 사용하여 주어진 question_id에 해당하는 질문 객체를 가져옵니다. 질문이 존재하지 않으면 404 오류를 반환합니다.
선택된 선택지 가져오기request.POST["choice"]를 통해 사용자가 선택한 라디오 버튼의 값을 가져옵니다. 이 값을 사용하여 해당 선택지를 찾습니다.
예외 처리- KeyError: 사용자가 선택을 하지 않았을 때 발생합니다.
- Choice.DoesNotExist: 존재하지 않는 선택지 ID를 사용했을 때 발생합니다.
오류 메시지 표시예외가 발생하면 질문 폼을 다시 렌더링하고, "You didn't select a choice."라는 오류 메시지를 표시합니다.
투표 수 증가선택된 선택지의 투표 수를 1 증가시킵니다. F("votes")를 사용하여 데이터베이스에서 직접 값을 증가시키고, 이를 저장합니다.
리다이렉트투표 완료 후 결과 페이지로 리다이렉트합니다. reverse("polls:results", args=(question.id,))를 사용하여 URL을 생성합니다.

result도 코드를 수정한다.

def results(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    return render(request, "polls/results.html", {"question": question})

results.html 템플릿도 생성해 코드를 작성한다.

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

<ul>
{% for choice in question.choice_set.all %}
    <li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
{% endfor %}
</ul>

<a href="{% url 'polls:detail' question.id %}">Vote again?</a>

제너릭 뷰

제너릭 뷰는 주로 CRUD(Create, Read, Update, Delete) 작업을 효율적으로 수행하기 위해 사용된다.

제너릭 뷰의 특징

  1. 재사용성: 제너릭 뷰는 일반적인 패턴을 캡슐화하여 여러 뷰에서 재사용할 수 있도록 한다.
  2. 간결성: 복잡한 로직을 간단한 클래스나 함수 호출로 대체할 수 있어 코드가 더 간결해진다.
  3. 유연성: 필요에 따라 제너릭 뷰를 상속하고, 메서드를 오버라이드하여 특정 요구사항에 맞게 조정할 수 있다.

주로 사용되는 제너릭 뷰

  1. ListView: 데이터베이스에서 여러 개의 객체를 가져와 목록으로 표시한다.
  2. DetailView: 특정 객체의 세부 정보를 표시한다.
  3. CreateView: 새 객체를 생성하는 폼을 제공한다.
  4. UpdateView: 기존 객체를 수정하는 폼을 제공한다.
  5. DeleteView: 객체를 삭제하는 확인 페이지를 제공한다.

polls/urls.py 를 다음과 같이 수정한다

from django.urls import path

from . import views

app_name = "polls"
urlpatterns = [
    path("", views.IndexView.as_view(), name="index"),
    path("<int:pk>/", views.DetailView.as_view(), name="detail"),
    path("<int:pk>/results/", views.ResultsView.as_view(), name="results"),
    path("<int:question_id>/vote/", views.vote, name="vote"),
]

polls/views.py 를 다음과 같이 수정한다.

from django.db.models import F
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
from django.views import generic

from .models import Choice, Question


class IndexView(generic.ListView):
    template_name = "polls/index.html"
    context_object_name = "latest_question_list"

    def get_queryset(self):
        """Return the last five published questions."""
        return Question.objects.order_by("-pub_date")[:5]


class DetailView(generic.DetailView):
    model = Question
    template_name = "polls/detail.html"


class ResultsView(generic.DetailView):
    model = Question
    template_name = "polls/results.html"


def vote(request, question_id):
    # same as above, no changes needed.
    ...

indexView클래스

  • 상속: generic.ListView를 상속받아 리스트 형태의 데이터를 처리하는 뷰입니다.
  • template_name: 사용할 템플릿 파일의 경로를 지정합니다. 여기서는 polls/index.html을 사용합니다.
  • context_object_name: 템플릿에서 사용할 컨텍스트 변수의 이름을 정의합니다. 이 경우 "latest_question_list"라는 이름으로 최신 질문 목록을 전달합니다.
  • get_queryset 메서드: 이 메서드는 반환할 쿼리셋을 정의합니다. 여기서는 최근 5개의 게시된 질문을 가져오도록 설정되어 있습니다. 질문은 pub_date(게시 날짜)를 기준으로 내림차순으로 정렬됩니다.

DetailView 클래스

  • 상속: generic.DetailView를 상속받아 특정 객체의 상세 정보를 처리하는 뷰입니다.
  • model: 이 뷰가 사용할 모델을 지정합니다. 여기서는 Question 모델을 사용합니다.
  • template_name: 사용할 템플릿 파일의 경로를 지정합니다. polls/detail.html이 사용됩니다. 이 템플릿은 특정 질문의 세부 정보를 표시합니다.
profile
제가 관심있고 공부하고 싶은걸 정리하는 저만의 노트입니다.

0개의 댓글