Django

  • Views(뷰)와 Templates(템플릿)
    • Views(뷰) : models에 정의한 내용을 활용
    • Templates(템플릿) : HTML을 활용하여 뷰와 연결해 데이터를 보다 잘 표현해주게 하는 것.
# polls/views.py
from .models import *
from django.shortcuts import render

def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    context = {'questions': latest_question_list} # question이라는 변수에 latest_question_list를 담는다!
    # render()은 Django의 내장 함수 중 하나로, HTTP 요청을 받아 해당 요청에 대해 원하는 템플릿 파일을 렌더링하여 응답하는 기능을 가지고 있습니다.
    # 첫번째 인자 : 요청(request) 객체, 두번째 인자 : 템플릿 파일의 경로, 세번째 인자 : 연결할 변수(context)
    return render(request, 'polls/index.html', context)
    
# order_by 의 쿼리문
print(Question.objects.order_by('-pub_date')[:5].query) 
SELECT "polls_question"."id", "polls_question"."question_text", "polls_question"."pub_date" FROM "polls
<!-- polls/templates/polls/index.html -->
<!-- 템플릿 -->
{% if questions %}
<ul>
    {% for question in questions %}
        <li>{{question}}</li>
    {% endfor %}
</ul>
{% else %}
<p>no questions</p>
{% endif %}
  • 상세 페이지
# polls/views.py
def detail(request, question_id):
    question = Question.objects.get(pk=question_id)
    return render(request, 'polls/detail.html', {'question': question})
    
# polls/urls.py
from django.urls import path
from . import views

urlpatterns = [
	...
    path('<int:question_id>/', views.detail, name='detail'),
]
<!-- polls/templates/polls/detail.html -->
<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %} <!-- 메소드 표현에 ()를 쓰지 않는다! -->
    <li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>
  • 상세 페이지에 링크 추가하기
# polls/urls.py
from django.urls import path 
from . import views  

app_name = 'polls' # 여기서 지정해준 앱이름이 index.html에서 쓰인다.

urlpatterns = [     
    path('', views.index, name='index'),
    path('some_url', views.some_url), 
    path('<int:question_id>/', views.detail, name='detail'),
]
<!-- polls/templates/polls/index.html -->
{% if questions %}
<ul>
    {% for question in questions %}
         <li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
         <!-- polls:detail app_name:name 관계를 사용해야 정확한 url 및 링크 사용 가능!!! -->
    {% endfor %}
<ul>
{% else %}
<p>no questions</p>
{% endif %}
  • 404 에러 처리하기
# polls/views.py

from django.http import Http404
from django.shortcuts import render , get_object_or_404

def detail(request, question_id):
	# 직접 404 에러가 발생했을 때 정의내리는 방법
    """
    try:
        question = Question.objects.get(pk=question_id)
    except Question.DoesNotExist:
        raise Http404("Question does not exist")
    """
    # Django 라이브러리에서 만든 shortcut 방식 get_object_or404() : Question 모델에서 get을 시도하고 pk=question_id가 없으면 404 에러를 띄워라!
    question = get_object_or_404(Question, pk=question_id)
    return render(request, 'polls/detail.html', {'question': question})
  • Forms(폼)
# polls/views.py
from django.db.models import F

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):
        return render(request, 'polls/detail.html', {'question': question, 'error_message': f"선택이 없습니다. id={request.POST['choice']}"})
    else:
    	# F() 는 DB에서 값을 읽어줌!
        selected_choice.votes = F('votes') + 1
        selected_choice.save()
        return HttpResponseRedirect(reverse('polls:index'))
        
# polls/urls.py
from django.urls import path 
from . import views

app_name = 'polls'
urlpatterns = [
    path('', views.index, name='index'), 
    path('<int:question_id>/', views.detail, name='detail'),
    path('<int:question_id>/vote/', views.vote, name='vote'), # 투표 페이지
]
<form action="{% url 'polls:vote' question.id %}" method="post">
    {% csrf_token %} <!-- csrf_token 전달 방법 -->
    <h1>{{ question.question_text }}</h1>
    {% 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 %}

<input type="submit" value="Vote">
</form>


폼을 제출할 토큰이 없는데 제출하려고 할 때 
서버에서 그려준 폼에서만 값을 제출할 수 있도록 하는 역할

 

  • 결과 페이지
# polls/views.py

from django.shortcuts import get_object_or_404, render

def vote(request, question_id):
	...
    else:
        selected_choice.votes = F('votes') + 1
        selected_choice.save()
        return HttpResponseRedirect(reverse('polls:result', args=(question.id,)))
        # 투표 후 result 화면으로 redirect 해주기.
        # index와 달리 어떤 질문에 대한 result인지 전달이 필요하기에 args=(question.id,)가 필요. ,는 다른 인자도 들어갈 수 있기에 꼭 써주어야 한다.

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

# polls/urls.py
from django.urls import path
from . import views

app_name = 'polls'
urlpatterns = [
    path('', views.index, name='index'),
    path('<int:question_id>/', views.detail, name='detail'),
    path('<int:question_id>/vote/', views.vote, name='vote'),
    path('<int:question_id>/result/', views.result, name='result'), # result에 대한 url 추가
]
<!-- polls/templates/polls/result.html -->

<h1>{{ question.question_text }}</h1><br>
{% for choice in question.choice_set.all %}

    <label>
        {{ choice.choice_text }} -- {{ choice.votes }}
    </label>
    <br>
{% endfor %}
  • admin의 편집 페이지 커스텀
from django.contrib import admin
from .models import Choice, Question

admin.site.register(Choice)

# TabularInline 좌우로 노출, StakedInline 상하로 노출
class ChoiceInline(admin.TabularInline):
    model = Choice
    extra = 3 # 디폴트 값으로 Choice를 3개 노출
    
# Choice 오브젝트를 Question에서 노출시키기 위해서는 클래스로 정의해주어야 한다.

class QuestionAdmin(admin.ModelAdmin):
    fieldsets = [
        ('질문 섹션', {'fields': ['question_text']}),
        ('생성일', {'fields': ['pub_date'], 'classes': ['collapse']}),
        # 'classes': ['collapse'] : pub_date field를 화면에 접은 상태로 노출. 누르면 펼처짐
    ]
    readonly_fields = ['pub_date'] # 'classes': ['collapse'] : pub_date field를 화면에 접은 상태로 노출. 누르면 펼처짐
    inlines = [ChoiceInline] # inlines는 다른 field도 수정 가능하게끔 해줌. Choice 오브젝트를 Question에서 노출시킴.
	list_filter = ['pub_date'] # 날짜를 기준으로 filter를 생성해줌
    search_fields = ['question_text', 'choice__choice_text'] # question_text, choice__choice_text를 검색 결과로 사용
admin.site.register(Question, QuestionAdmin)

 

'데브코스 > Week 5' 카테고리의 다른 글

Week 5 - 5  (0) 2024.04.12
Week 5 - 4  (0) 2024.04.11
Week 5 - 3  (0) 2024.04.10
Week 5 - 1  (0) 2024.04.08

+ Recent posts