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 |