파이썬 장고 프레임워크를 사용해서 API 서버 만들기 (2)

PH_Lee·2024년 4월 9일

뷰(Views)와 템플릿(Templates)

polls/view.py

from .models import *
from django.shortcuts import render

def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    context = {'first_question': latest_question_list[0]}
    return render(request, 'polls/index.html', context)

Django Shell

>>> from polls.models import *
>>> Question.objects.order_by('-pub_date')[:5] 
>>> print(Question.objects.order_by('-pub_date')[:5].query) 
SELECT "polls_question"."id", "polls_question"."question_text", "polls_question"."pub_date" FROM "polls_question" ORDER BY "polls_question"."pub_date" DESC LIMIT 5

polls/templates/polls/index.html

<ul>
    <li>{{first question}}</li>
<ul>

템플릿(Templates)에서 제어문 사용하기

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

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}
    #context = {'questions': []}
    return render(request, 'polls/index.html', context)

상세(detail) 페이지 만들기

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('', views.index, name='index'),
    path('some_url', views.some_url),
    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>

상세(detail) 페이지로 링크 추가하기

polls/urls.py

from django.urls import path 
from . import views  

app_name = 'polls'

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>
         
    {% endfor %}
<ul>
{% else %}
<p>no questions</p>
{% endif %}

404 에러 처리하기

get은 찾을 수 없으면 500 에러가 발생함 -> 404로 바꿔야함

polls/views.py

from models.py import *
from django.http import HttpResponse
from django.http import Http404
from django.shortcuts import render , get_object_or_404

...
def detail(request, question_id):
    """
    try:
        question = Question.objects.get(pk=question_id)
    except Question.DoesNotExist:
        raise Http404("Question does not exist")
    """
    question = get_object_or_404(Question, pk=question_id)
    return render(request, 'polls/detail.html', {'question': question})

폼(Forms)

polls/views.py

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': '선택이 없습니다.'})
    else:
        selected_choice.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'),  
]

polls/templates/polls/detail.html

<form action="{% url 'polls:vote' question.id %}" method="post">
    {% 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

브라우저의 오류를 대비해야한다 -> 브라우저에서 올라가는 데이터가 삭제되거나 바뀔수 있기 때문

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:
        selected_choice.votes += 1
        selected_choice.save()
        return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))

polls/views.py

A서버와 B서버에서 동시에 명령을 주었을 경우 하나만 실행될 수 있음, 이를 방어

from django.urls import reverse
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:
        # A서버에서도 Votes = 1
        # B서버에서도 Votes = 1 
        selected_choice.votes = F('votes') + 1
        selected_choice.save()
        return HttpResponseRedirect(reverse('polls:index'))

결과(result) 조회 페이지

302 Redirect

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,)))

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

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 %}

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'),
]

Django Admin의 편집 페이지 커스터마이징

models.py의 pub_date = models.DateTimeField()으로 바꿔서 기본값 없애줌

polls/admin.py

from django.contrib import admin
from .models import Question, Choice

# Register your models here.
# CRUD Create Read Update Delete
admin.site.register(Choice)

# 좌우로 보이기 위해 TabularInline 사용
class ChoiceInline(admin.TabularInline):
    model = Choice
    extra = 3


class QuestionAdmin(admin.ModelAdmin):
    fieldsets = [
        ('질문 섹션', {'fields': ['question_text']}),
        # 숨기고 싶으면 collapse
        ('생성일', {'fields': ['pub_date'], 'classes': ['collapse']}),        
    ]
    readonly_fields = ['pub_date']
    # Choice 와 함께 편집하기 위해
    inlines = [ChoiceInline]

admin.site.register(Question, QuestionAdmin)

Django Admin의 목록 페이지 커스터마이징

polls/models.py

import datetime
from django.db import models
from django.utils import timezone
from django.contrib import admin


class Question(models.Model):
    question_text = models.CharField(max_length=200, verbose='질문')
    pub_date = models.DateTimeField(auto_now_add=True, verbose='생성일')  

# 목록에서 표시되는 이름을 보기 위해
    @admin.display(boolean=True, description='최근생성(하루기준)')
    def was_published_recently(self):
        return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
    
    def __str__(self):
        return f'제목: {self.question_text}, 날짜: {self.pub_date}

polls/admin.py

from django.contrib import admin
from .models import Choice, Question

admin.site.register(Choice)

class ChoiceInline(admin.TabularInline):
    model = Choice
    extra = 3


class QuestionAdmin(admin.ModelAdmin):
    fieldsets = [
        ('질문 섹션', {'fields': ['question_text']}),
        ('생성일', {'fields': ['pub_date'], 'classes': ['collapse']}),        
    ]
    readonly_fields = ['pub_date']
    inlines = [ChoiceInline]
    list_display = ['question', 'pub_date', 'was_published_recently']
    # 검색을 하기위해
    list_filter = ['pub_date']
    search_fields = ['question_text', 'choice__choice_text']
    
admin.site.register(Question, QuestionAdmin)
profile
새싹 개발자

0개의 댓글