일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- Django
- es6
- Algorithm
- AWS
- 파이썬 알고리즘
- Git
- PYTHON
- django ORM
- django widget
- java
- Baekjoon
- 백준
- MAC
- c++
- CSS
- 장고
- form
- DRF
- 알고리즘 문제
- 알고리즘 연습
- web
- javascript
- django rest framework
- 파이썬
- js
- 알고리즘 풀이
- API
- react
- HTML
- 알고리즘
- Today
- Total
수학과의 좌충우돌 프로그래밍
[Django]투표 기능 구현하기 본문
투표 기능 구현하기
학교내 고양이 관리하는 사이트 UOSCAT 의 기능 중 일부이다. 길고양이 이니 만큼 이름을 투표를 통해 지어주었고 그 투표구현하는 방법을 알아보자.
models.py
먼저 model 을 추가해줘야한다. 현재 있는 모델은 Cat 으로서 일반적인 Post 글에 해당한다. 각 Cat 마다 하나의 투표가 필요하므로 그 이름을 Vote 라고 한다. 그리고 Cat 과 Vote는 1대1 대응이다. 그리고 각 Vote마다 여러 개의 표가 존재하고 그 이름을 Choice 라고 하자. Vote와 Choice는 일대다 대응이다.
class Vote(models.Model):
created = models.DateTimeField(default=django.utils.timezone.now)
# 투표는 시간이 지나면 종료되야하므로 생성날짜를 현재로 지정
cat = models.OneToOneField(Cat,on_delete=models.CASCADE)
# Cat 과 one to one
def __str__(self):
return self.cat.name+"의 투표"
class Choice(models.Model):
vote = models.ForeignKey(Vote,on_delete=models.CASCADE)
# Vote 와 one to many
name = models.CharField(max_length=20)
# 투표 후보인 새로운 고양이 이름
count = models.IntegerField(default=0)
# 투표 수
def __str__(self):
return self.name
detail.html
투표에 대한 html 부분 이다. 투표 중이 아닐 때와 투표 중일 때 크게 두 부분으로 나뉜다.
-
투표 중이 아닐 때
{% if not cat.voting %}
에 해당 되는 부분- 투표를 시작하는 버튼이 존재하고 버튼을 누르면
vote_condition
url로 이동한다. 이 부분에 대해서는 뒤에서 설명하겠다.
-
투표 중일 때
- 먼저 각 투표 현황은 table, 표로 나타냈다.
- view에서 choices를 받아오는데 해당 cat 에 해당되는 모든 choice들이다. 즉 모든 투표 후보들이 오고 이를 순회하는 반복문으로서 표를 완성한다.
- 이름과 투표수, 선택 버튼이 순서대로 위치한다.
- 선택을 누를 경우, choice 해당 투표 후보의 id를 value로 넘기며
vote
url 로 이동한다. - 투표 테이블 아래에는 새 이름을 추가할 수 있는 input 과 버튼이 존재하고
add_name
url 로 이동한다. - 투표를 종료하는 버튼이 존재하고 버튼을 누르면
onclick="count_overlap()
이 발생하는데 js로 예외처리해주기 위해 존재하고 결국에는vote_condition
url로 이동한다.
{% if not cat.voting %}
<div class="row">
<p>새 이름을 지어줄까요?</p>
<a href="{% url 'vote_condition' cat.id %}" class="btn btn-success">네</a>
</div>
{% else %}
<p>투표 진행중</p>
<!-- 이름 투표 테이블 -->
<table class="table table-striped">
<thead>
<tr>
<td><B>이름</B></td>
<td><B>투표수</B></td>
<td><B>지지하기</B></td>
</tr>
</thead>
{% for choice in choices %}
<tbody>
<tr>
<td id="cat_name">{{choice.name}}</td>
<td>{{choice.count}}</td>
<td>
<form action="{%url 'vote' vote.id%}" method="POST">
{% csrf_token %}
<button name="choice" value="{{choice.id}}">선택</button>
</form>
</td>
</tr>
</tbody>
{% endfor %}
</table>
<!------------------->
<form method="POST" id="newname_form" action="{% url 'add_name' cat.id%}">
<input id="newname_input" type="text" name="add_name">
<input onclick="check_error()" class="btn btn-secondary" value="새 이름 추가하기">
</form>
<a href="#" class="btn btn-success" onclick="count_overlap()">투표 종료</a>
{% endif %}
</div>
urls.py
전체 url은 너무 많아서 해당 부분의 url만 가져왔다. 위의 detail.html
과 연관지어서 생각하면 각각 어느 함수를 실행할지 알 수 있다. 바로 views.py
를 알아보자.
urlpatterns = [
path('votecondition/<int:cat_id>',postapp.views.vote_condition, name='vote_condition'),
path('vote/<int:vote_id>', postapp.views.vote, name='vote'), path('addname/<int:cat_id>', postapp.views.add_name, name='add_name'),
]
views.py
세 개의 함수가 있고 각각에 대해서 알아보자.
- vote_condition
-
투표 상태를 바꿔주는 함수이다.
-
투표 중일 경우
-
if cat.voting
에 해당한다. -
투표를 끝내기 위해
cat.voting = False
로 바꿔준다. -
가장 많은 득표 수를 구해서 해당 투표에서, 가장 많은 득표수를 가진 Choice 들을 골라낸다. 이 때
get
이 아닌filter
를 사용한 이유는 득표수가 동률일 경우에는 예외 처리를 해줘야하기 때문이다.-
최대 득표수의 이름의 개수가 1개 초과일 경우
- 저장하지 않고 현재페이지로 다시 redirect 한다.
-
최대 득표수가 1개일 경우
- 첫번째 이름의 str 형으로 가져오고 투표는 끝내야 하므로
vote
를 삭제해준다.
- 첫번째 이름의 str 형으로 가져오고 투표는 끝내야 하므로
-
-
-
투표 중이지 않을 경우
- else 에 해당한다.
- 투표를 시작하기 위해
cat.voting = True
로 바꿔준다. - 원래 이름에 해당되는 이름으로 ,
count=0
으로 새로운 투표 후보를 생성하고 이를 저장한다.
-
최종적으로 cat의 정보를 모두 저장하고 현재 페이지로
redirect
한다.
def vote_condition(request,cat_id):
cat = Cat.objects.get(id=cat_id)
# 투표 종료
if cat.voting:
cat.voting = False
choices = Choice.objects.filter(vote_id=cat.vote.id)
max_count = 0
for choice in choices:
if max_count < choice.count:
max_count = choice.count
new_name = Choice.objects.filter(vote_id=cat.vote.id, count = max_count)
if (len(new_name) > 1):
return redirect('/detail/'+str(cat.pk))
cat.name = new_name[0].as_str()
vote = Vote.objects.get(cat_id=cat.id).delete()
# 투표 시작
else:
cat.voting = True
origin_name = Choice(vote_id=cat.vote.id, name=cat.name, count = 0)
origin_name.save()
cat.save()
return redirect('/detail/'+str(cat.pk))
- vote
- 현재 투표를 가져오고 입력한 값을 저장한다.
- 투표 시 받았던
choice.id
로 원하는 값을 찾고 1 증가 시켜서 저장한다. - 헌재 페이지로 redirect 한다.
def vote(request, vote_id):
vote = Vote.objects.get(pk=vote_id)
selection = request.POST['choice']
choice = Choice.objects.get(vote_id=vote_id, id=selection)
choice.count += 1
choice.save()
return redirect('/detail/'+str(vote.cat.id))
- add_name
- 이름 후보 choice 를 하나만들어준다.
- filter를 통해 현재 투표의 이름 후보들을 모두 가져와서
choice_all
에 저장한다. - 입력한 이름을 공백없이 가져와서 이름 후보의 이름으로 저장한다.
- 예외처리를 해줘야한다. 기존의 이름들을 순회하며 해당이름이 존재하거나 입력을 공백만 했다면 별도의 저장없이 현재 페이지로 redirect 한다.
- 그렇지 않으면 저장하고 redirect 한다.
def add_name(request, cat_id):
cat = Cat.objects.get(id = cat_id)
choice = Choice(vote_id = cat.vote.id, count=0)
choice_all = Choice.objects.filter(vote_id=cat.vote.id)
choice.name = request.POST['add_name'].strip()
for choice_one in choice_all:
if str(choice_one) == choice.name or len(choice.name) == 0:
return redirect('/detail/'+str(cat.id))
choice.save()
return redirect('/detail/'+str(cat.id))
'웹프로그래밍 > Django' 카테고리의 다른 글
[Django] 효율적인 url 관리 (2) (0) | 2019.05.04 |
---|---|
[Django] 기본세팅하기 (0) | 2019.05.01 |
[django] 글 수정 시 기존의 내용 불러오기 (0) | 2019.04.04 |
[Django] 17. 글 삭제 및 수정 구현하기 (4) | 2019.03.24 |
[Django] 많이들 하는 실수, html 파일의 위치 (0) | 2019.03.11 |