본문 바로가기

카테고리 없음

Django / template 문법 / F함수

오늘 메모할 내용은 장고 프레임워크 중 template라는 안에는 텍스트 파일(.html/.XML/.CSV 등..)이 모여있다. 웹프레임워크를 쓰니까 여러 주소를 사용하게된다. 

 

내가 원하는 주소에서 원하는 화면을 띄울 때, 내가 갖고있는 데이터를 어떻게 화면으로 가져올 수 있나 ??와 관련한 내용이라고 보면 된다. 

 

이 안에서는 변수와 태그가 대표적이다. 

템플릿 안에서 변수는 보통 {} 중괄호나 $ 달러표시 등을 통해 표현된다. 

태그는 이 안에서 반복문, 조건문 등의 제어 기능을 할 수 있도록 돕는다. 

 

연습 시작 !

  • Question 모델 인스턴스는 question 변수에 저장되어 템플릿에 전달됩니다.
  • Question 모델의 choice_set을 통해 해당 질문에 대한 모든 선택지(Choices)에 접근할 수 있습니다.
  • 각 Choice 모델 인스턴스는 text 속성(선택지 텍스트)과 votes 속성(해당 선택지에 대한 총 투표 수)을 가집니다.

 

 

 

 

1. question 객체의 question_text 속성 값을 표시하기

차근차근 해보자.

이 주소로 요청이 들어가면 

맨 처음엔 왼쪽 urls.py 그다음 polls안의 url.py로 간다.

 

question_id는 3으로 요청이 들어간거다. (내가 3 붙였음.)

 

그다음엔 views.detail. views.py 안에 있는 detail 이라는 함수를 찾아갈거다. 

여긴 views.py의 detail 상황.

 

question이라는 변수에 Question이라는 클래스를 받고 있다. 이것은 models.py 안에 있던 친구들이다.

 

 

models.py

이것은 받아오는 것이다. question은 question_id로 들어온 그 하나를 받는 것이고, question_list는 모두 다 받는다. 

그리고나서 context에 이 둘을 담아서 polls폴더 안에 있는 detail.html로 보내준다. 

 

 

그 안에 detail.html에 이렇게 코드를 작성하면 어떤 화면이 나올까?

 

 

 

 

내 DB속 Question 안의 id 3번의 text는 수락산 뭐시기다.

 

 

이런 상황에서 detail.html의 첫 번째 코드

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

 

이런 화면이 뜬다. 하나를 받았으므로, id 3에 해당하는 question_text를 h1태그에 담아 반환해준다.

 

 

 

2. Question 객체의 pub_date 날짜 표시하기

이것도 해보자. 

답은

더보기

 {{question.pub_date}} 

 

 

3. 템플릿에서 전달받은 question 변수의 id 값을 표시하기

더보기
{{question.id}}

 

4. question 객체에서 첫 번째 Choice의 텍스트 표시하기

 

더보기
{{question.choice_set.all.0.choice_text}}

 

5. question 객체의 첫 번째 Choice에 대한 투표 수 표시하기

 

더보기

{{question.choice_set.all.0.votes}}

6. question 객체에 속한 모든 Choice의 개수를 표시하는 메소드 호출하기

더보기
{{question.choice_set.all.count}

 

태그도 사용해보자. {% %} 이 안에 코드를 작성해준다. 조건문이나 반복문은 시작-종료태그를 둘 다 작성해줘야한다. 

 

 

- question_text에서 내용이 없다면 내용없음으로 표시, 있으면 text를 표시하도록 하자.

더보기
{% if not question.question_text %}
    <h3>내용없음</h3>
{% else %}
    <h3>{{ question.question_text }}</h3>
{% endif %}

- choice내 choice_text와 choice내 votes의 파일크기 표시

더보기
<ul>
{% for choice in question.choice_set.all %}
    <li>{{ choice.choice_text }} - {{ choice.votes|filesizeformat }}</li>
{% endfor %}
</ul>

- 선택지 목록 쉼표로 구분

더보기
<h3>선택지 목록 (쉼표로 구분)</h3>
<p>{{ question.choice_set.all|join:", " }}</p>

- 리스트형태로 choice_text 모두 표시

더보기
  <ul>
    {% for choice in question.choice_set.all %}
      <li>{{ choice.choice_text }}</li>
    {% endfor %}
  </ul>

- 리스트형태로 question_text 모두 표시

더보기
  <ol>
    {% for question in question_list %}
      <li>{{ question.question_text }}</li>
    {% endfor %}
  </ol>

ol태그는 순서대로 숫자가 앞에 붙는다.

 

 

 

F함수. 오늘의 마지막 !

 

<form action="{% url 'polls:vote' question.id %}" method="post">
  {% csrf_token %}
  <!-- csrf는 보안관련한 안전장치코드. -->
  <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 }}">{{ forloop.counter }}. {{ choice.choice_text }}</label><br>
      {% endfor %}
  </fieldset>
  <input type="submit" value="Vote">
  </form>

 

detail.html을 좀 바꾼다. (tutorial 코드)

 

 

3번으로 아까처럼 요청을하면

http://127.0.0.1:8000/polls/3/ >> question_id가 3인 친구들이 모두 오는거다. input의 radio태그는 동그랗게 생겨서 1개를 선택할 수 있도록 한다. 

 

 

이러한 화면을 얻을 수 있다.

 

우리 저 polls_choice에는 votes라는 열이 있다. 저기에 사용자가 이 중 하나를 골라 vote 버튼을 눌렀을 때, 내 DB에 저 votes 숫자가 올라가도록 코드를 작성할 수 있다. 

 

 

 

 

 

##Tutorial CODE F함수
from django.db.models import F
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.urls import reverse

from .models import Choice, Question


# F함수가 속도가 훨씬 빠르다.
from django.db.models import F, Case, When, Value
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:
        #votes 값을 1 증가시키고 저장한다. (F함수 사용)
        selected_choice.votes = F("votes") + 1
        selected_choice.save()
        #votes 값을 1 증가시키고 저장한다. (F함수 미사용)
        # selected_choice.votes += 1
        # selected_choice.save()
        # 성공적으로 처리한 후 항상 HttpResponseRedirect를 반환.
        # POST data쓰면 데이터 두번 게시되는거 방지. 사용자가 단추 두 번 눌러도.
        return HttpResponseRedirect(reverse("polls:results", args=(question.id,)))

 

주목할 것은 저 F라는 친구다. 

from django.db.models import F, Case, When, Value

 

이 F가 여기서 큰역할 해준다. 

먼저 여기는 views.py다. 

 

 

<form action="{% url 'polls:vote' question.id %}" method="post">
  {% csrf_token %}
  <!-- csrf는 보안관련한 안전장치코드. -->
  <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 }}">{{ forloop.counter }}. {{ choice.choice_text }}</label><br>
      {% endfor %}
  </fieldset>
  <input type="submit" value="Vote">
  </form>

 

위에서 말한 바뀐 화면에서 사용자가 하나를 선택하고 Vote 버튼을 누르면, form에 입력된 데이터가 서버로 전송이 된다. 이 과정에서 맨 윗줄 action 속성에서 지정된 URL로 POST 요청이 들어간다. polls 안의 vote를 찾아가게 되는 것이다. 

 

그래서 서버에서는 view.py 안에있는 vote 함수를 실행한다. 

 

 

이거.

##Tutorial CODE F함수
from django.db.models import F
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.urls import reverse

from .models import Choice, Question


# F함수가 속도가 훨씬 빠르다.
from django.db.models import F, Case, When, Value
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:
        #votes 값을 1 증가시키고 저장한다. (F함수 사용)
        selected_choice.votes = F("votes") + 1
        selected_choice.save()
        #votes 값을 1 증가시키고 저장한다. (F함수 미사용)
        # selected_choice.votes += 1
        # selected_choice.save()
        # 성공적으로 처리한 후 항상 HttpResponseRedirect를 반환.
        # POST data쓰면 데이터 두번 게시되는거 방지. 사용자가 단추 두 번 눌러도.
        return HttpResponseRedirect(reverse("polls:results", args=(question.id,)))

 

 

 

그럼 여기에서는 먼저 selected_choice를 정의하는데, 이것은 post 요청으로 부터 받은 choice다.  하나를 받아오니까 get을 쓴 것이고, primary key로 선택요청을 받은 애를 가져오는 것이다. 

 

pk=request.POST["choice"] 여기를 주목해보자! POST는 알겠는데 뒤에 ["choice"]는 뭔가? 

 

사용자가 Vote를 눌렀을 때, 해당 선택지의 ID값이 choice여서 여기에 choice가 들어가는 것이다. 

 

여기 중간 input 태그의 id이름이 choice다. 

 

 

이렇게 잘 받아와서

    else:
        #votes 값을 1 증가시키고 저장한다. (F함수 사용)
        selected_choice.votes = F("votes") + 1
        selected_choice.save()

votes값을 증가시키고 끝난다.여기 이 코드는

 

    else:
        # votes 값을 1만큼 증가시키고 저장
        selected_choice.votes += 1
        selected_choice.save()

이렇게 F함수를 쓰지 않아도 똑같이 작동한다. 하지만 성능면에서 큰 차이가 있다.

 

F 함수를 쓰는게 더 빠르고, 여러 사용자가 동시에 요청을 보내도 거뜬하다고 한다. 

 

 

Django는 은근히 큰 프레임워크인 것 같다.