나중에 보려 만든 Django 실시간 투표 웹서비스
실시간 투표 웹서비스
A. 모든 URL들을 App 단위로 나누어 각 App폴더 내의 urls.py에 권한을 위임
ex. /lotto/ 가 반복적으로 들어가는 url들은 모두 lottos app > urls.py에 맡기자.
1. 각 앱 폴더에 urls.py 생성
2. 권한 위임 작업
(@ website > urls.py)from django.urls import path, include from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('polls/',include('polls.urls')) # 권한 위임 작업 ]
(@ app > urls.py)
from django.urls import path from . import views urlpatterns = [ path('',views.index, name='index'), ]
(@ views.py)
def index(request): return HttpResponse('Hello world!')
- 만드려는 DB 구조
B. 설문조사 주제 (Question) & 보기(Choice)를 연결할 필요가 있음.
1. DB tables 생성
(polls > models.py)from django.db import models from django.utils import timezone import datetime # Create your models here. class Question(models.Model): question_text = models.CharField(max_length=200) # 주제 pub_date = models.DateTimeField('Date published') # ‘date published’라는 이름으로 관리자 페이지에서 보여질 항목명 def __str__(self): # 하나의 행. 인스턴스생성시 자동 실행되는 함수. return self.question_text def was_published_recently(self): # 현재보다 하루 이내 올라왔는가 확인 now = timezone.now() return now >= self.pub_date > now - datetime.timedelta(days=1) # 하루가 몇 milliseconds인지 class Choice(models.Model): # DB Table for 설문조사 주제별 선택지 (+ 선택지마다의 득표 수) # Question table의 pk를 fk로 세팅 # on_delete=models.CASCADE : Question(질문) 항목 삭제 시 관계된 선택지들도 모두 자동 삭제 question = models.ForeignKey(Question, on_delete=models.CASCADE) # 설문조사 주제의 id 값 choice_text = models.CharField(max_length=200) # 설문조사 주제에 대한 선택지 텍스트 votes = models.IntegerField(default=0) # 해당 선택지의 득표 수 def __str__(self): # 하나의 행 return self.choice_text
class 내부 메소드 작동원리
2. DB table 업데이트 및 저장(makemigrations, migrate)
$python manage.py makemigrations
$python manage.py migrate
3. 저장 후 확인(shell)
$python manage.py shell
$from polls.models import Question, Choice
$Question.objects.all()
# 수정 전: 관리자페이지에서 <QuerySet [<Question: Question object (1)>]> 가 출력됨. -> "Question: __" 형태로 바꿔주기 위해, models.py에서 __str__함수를 1번 코드와 같이 수정해줄 것. 수정 후: <QuerySet [<Question: What is the worst treatement?>]>
$from django.utils import timezone
$q = Question(question_text="What's the best treatment?", pub_date=timezone.now())
$q.save() # DB에 저장
+shell command 추가:
# all: DB의 모든 행 꺼내기
list = Question.objects.all()
# filter: 특정 조건을 맞추는 행들 리스트 형태로 꺼내기
rows = Question.objects.filter(id=1)
# get: 단일 행을 꺼낼 경우
row = Question.objects.get(question_text__startswith = 'What')
# Question에 연결된 Choice들 중 Treat문자열로 시작하는 보기들만 보여줘라
c = q.choice_set.filter(choice_text__startswith='Treat')
# delete: 변수 삭제
c.delete()
# count : 카운트
q.choice_set.count()
# (상단 DB구조 이미지 참조) Choice 테이블의 question(fk)와 연결되어있는 테이블의 pub_date의 year이 2021인것만 보여줘라
Choice.objects.filter(question__pub_date__year=2021)
4. 특정 주제에 대한 여러 보기 만들기
a. (테스팅용)cmd창에서 Question 인스턴스1 생성.
from django.utils import timezone
current_year = timezone.now().year
Question.objects.get(pub_date__year=current_year)
Question.objects.get(pk=1)
q = Question.objects.get(pk=1)
q.was_published_recently()
b. (테스팅용)cmd창에서 인스턴스1(q)에 연결된 해당하는 보기(Choice table) 만들기.
q.choice_set.create(choice_text='Treatment A', votes=0)
q.choice_set.create(choice_text='Treatment B', votes=0)
c = q.choice_set.create(choice_text='Treatment C', votes=0)
c. 생성한 질문 및 보기 tables admin.py에서 register 해주기
(@admins.py)
from django.contrib import admin
from .models import Question, Choice
admin.site.register(Question)
admin.site.register(Choice)
C. Error page handling
- 존재하지 않는 /id/로 접속할 경우 404error을 발생시키자.
방법1.from django.http import HttpResponse, Http404 def detail(request, question_id): try: q = Question.objects.get(pk=question_id) except Question.DoesNotExist: # Question 내 question_id가 없어 DoesNotExist에러가 뜰 경우 raise Http404('Question {} does not exist'.format(question_id)) return render(request, 'polls/detail.html',{'question':q})
방법2. Table에 존재하지 않는 id가 입력되었을 경우 에러 발생: "get_object_or_404"
from django.shortcuts import render, get_object_or_404 def detail(request, question_id): # q = Question.objects.get(pk=question_id) q = get_object_or_404(Question, pk=question_id) # 꺼낼 테이블 이름, 꺼내려는 기준 # get_objects_or_404도 존재 return render(request, 'polls/detail.html',{'question':q})
D. 결과 확인을 위한 urls.py & views.py 수정
설계 및 예상 결과물)
http://127.0.0.1:8000/polls/ : 모든 Question가 나열되고, 누르면 아래로 route.
http://127.0.0.1:8000/polls/3/
http://127.0.0.1:8000/polls/3/results/ : 위에서 vote submit 후 보여주는 결과창
http://127.0.0.1:8000/polls/3/vote/ : POST 요청을 처리하는 개별적인 주소가 될 것임.
구현 코드)
(@urls.py)from django.urls import path from . import views app_name = 'polls' # 나중에 detail이라는 name이 다른 앱에 존재할 수도 있으니까!. 추후 html에서 해당 name 사용시, "polls:detail"이라 명시 가능. urlpatterns = [ path('',views.index, name='index'), # /polls/ # 설문조사 페이지에서 # polls/3/ path('<int:question_id>/',views.detail, name='detail'), # 설문조사 주제 detail page # /polls/3/results/ path('<int:question_id>/results', views.results, name='results'), # /polls/3/vote/ : 보여주려는 용도 X. post요청을 처리하려는 개별적인 주소. path('<int:question_id>/vote/',views.vote, name='vote'), ]
(@views.py)
from django.shortcuts import render, get_object_or_404, redirect from django.http import HttpResponse, Http404 from .models import Question, Choice # Create your views here. def index(request): latest_question_list = Question.objects.order_by('-pub_date')[:5] # 마이너스기호: 내림차순. context = {'latest_question_list':latest_question_list} return render(request, 'polls/index.html',context) def detail(request, question_id): # q = Question.objects.get(pk=question_id) q = get_object_or_404(Question, pk=question_id) # 꺼낼 테이블 이름, 꺼내려는 기준 return render(request, 'polls/detail.html',{'question':q}) def results(request, question_id): response = "You are now at results {}" return HttpResponse(response.format(question_id)) def vote(request, question_id): # input tag로 들어갈 value = request.POST('choice_select') # name으로 꺼내고 돌려받는 건 value question = get_object_or_404(Question, pk=question_id) # return HttpResponse('vote') try: # pk=999가 입력되었을 때, 애러 뻥! selected_choice = question.choice_set.get(pk=request.POST['choice_select']) # request.POST['choice_select'] : value값(ex.8)을 리턴함. # print(request.POST) 로 확인해볼 것 # 선택된 답안 selected_choice에 저장. # detail.html의 <input type="radio" name="choice_select" value="{{ choice.id }}">에서 날라온 값 # form으로 제출된 POST Request 전체에서 'choice_select'가 name인 HTML 태그의 value를 꺼내는 코드 # request.POST 는 {~~~, 'choice_select':7} 와 같은 dictionary 형태 except: # request.POST['choice_select']값이 없을 경우, error_message를 가지고 details.html로 되돌아감 context = {'question': question, 'error_message': "You didn't select a choice."} return render(request, 'polls/detail.html', context) else: # 에러가 발생하지 않았을 경우, else 실행... selected_choice.votes += 1 # vote수 1씩 증가 selected_choice.save() # 실제 DB 저장 return redirect('polls:results', question_id = question.id)
E. Template 파일 생성
(@index.html){% if latest_question_list %} <!-- latest_question_list가 있다먄 --> <ul> {% for question in latest_question_list %} <li> <!--하드코딩: <a href='/polls/{{ question.id }}/'>{{ question.question_text }}</a> --> <a href='{% url "polls:detail" question_id=question.id %}'>{{ question.question_text }}</a> <!-- 자동 url 생성. name이 detail인 url의 question_id에 question.id를 넣어라 --> </li> {% endfor %} </ul> {% else %} <p>No polls are available.</p> {% endif %}
(@detail.html)
Question Choices를 form tag로 보여주기<h2>{{ question.question_text }}</h2> {% if error_message %} <p><strong>{{ error_message }}</strong></p> {% endif %} <form action="{% url 'polls:vote' question.id %}" method="post"> {% csrf_token %} <!-- form에는 반드시 넣어줘야해 --> {% for choice in question.choice_set.all %} <!-- for loop마다 숫자를 세어주려고: forloop.counter --> <input type="radio" name="choice_select" id="choice{{ forloop.counter }}" value="{{ choice.id }}"> <label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label> <!-- label눌러도 인식되도록...사용자 입장에서 편하게! --> <br> {% endfor %} <input type="submit" value="Vote"> </form>
Author And Source
이 문제에 관하여(나중에 보려 만든 Django 실시간 투표 웹서비스), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@yeonny0723/Django-실시간-투표-웹서비스저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)