본문 바로가기

django/정리

[Django] CEOS vote_project 정리

#개발 준비

ERD

bariBari23/django-vote-17th: 파트장/데모데이 투표 (github.com)

 

GitHub - bariBari23/django-vote-17th: 파트장/데모데이 투표

파트장/데모데이 투표. Contribute to bariBari23/django-vote-17th development by creating an account on GitHub.

github.com

https://documenter.getpostman.com/view/25425757/2s93z9d3Md

 

vote_project

The Postman Documenter generates and maintains beautiful, live documentation for your collections. Never worry about maintaining API documentation again.

documenter.getpostman.com


# accounts/models.py

class User(AbstractUser):

    part_list = (
        ('프론트엔드', '프론트엔드'),
        ('백엔드', '백엔드')
    )

    team_list = (
        ('RePick', 'Repick'),
        ('바리바리', '바리바리'),
        ('Hooking', 'Hooking'),
        ('Dansupport', 'Dansupport'),
        ('TherapEase', 'TherapEase')
    )

    name = models.CharField(max_length=8)
    email = models.EmailField(unique=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    part = models.CharField(max_length=8, choices=part_list, default='백엔드')
    team = models.CharField(max_length=16, choices=team_list, default='바리바리')
    team_vote = models.BooleanField(default=False)
    part_vote = models.BooleanField(default=False)

    USERNAME_FIELD = 'username'
    REQUIRED_FIELDS = ['password', 'name', 'email', 'part', 'team']

    def __str__(self):
        return self.name
  • 회원가입에 필요한 필드는 아이디, 비밀번호, 이메일, 파트, 이름, 팀입니다.
  • 아이디, 이메일은 중복될 수 없습니다. 회원가입 과정 중에 중복 체크는 자유롭게 하셔도 됩니다.
  • (중복체크 API를 따로 제작 혹은 회원가입 완료 시에 한 번에 체크)
  • 파트는 (프론트엔드, 백엔드) 중 하나를 선택할 수 있게 해주시면 됩니다.
  • 은 (RePick, 바리바리, Hooking, Dansupport, TherapEse) 중 하나를 선택할 수 있게 해주시면 됩니다.

위 요구사항을 바탕으로 AbstractUser를 상속받는 User model을 만들었다.

team_vote와 part_vote는 각 팀/파트 투표에 참여했는지 확인하기 위해 넣었다. 다만 파트장 투표는 여러 사람을 뽑을 수 있도록 기능을 수정해서 part_vote는 쓸모 없어진듯...

파트와 팀은 정해진 리스트에서 choice하도록 만들었고, default값을 설정해주어야 하길래 우리 팀/파트로 해주었다.


#accounts/views.py

#로그아웃 함수
class LogoutView(APIView):

    def post(self, request):
        response = Response({
            "message": "로그아웃 성공"
        }, status=status.HTTP_202_ACCEPTED)
        response.delete_cookie('refresh')
        response.delete_cookie('access')

        return response

로그아웃은 cookie의 token을 삭제하는 식으로 구현해주었다. 아예 token을 blacklist처리해버리는 방법을 하려다, 간단하게 구현하였다.

blacklist

내 database를 보다가 갑자기 만든 적 없는 blacklist에 여러 token이 올라와져 있는것을 보았다. blasklistedtoken에는 아무것도 없었는데...

로그인을 할때마다 새롭게 생성되었다. 찾아보니 token에 로그인할때마다 생성되는 refresh token이 들어가고 있었다.


#votes/models.py

class Team_Vote(models.Model):
    team_list = (
        ('RePick', 'RePick'),
        ('바리바리', '바리바리'),
        ('Hooking', 'Hooking'),
        ('Dansupport', 'Dansupport'),
        ('TherapEase', 'TherapEase')
    )
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='team_user')
    team = models.CharField(max_length=16, choices=team_list)

    def __str__(self):
        return f'{self.user.name} : {self.team}'

팀과 파트장 투표 모델은 투표한 user와 선택한 team/파트장의 정보를 넣었다.

다만 Part_Vote 모델도 team_list처럼 정해진 리스트 내에서 투표하도록, 오타 같은 일이 없게 만들었는데 시간 낭비였던 것 같다.

team_list작성이 제일 오래걸린 듯.


#votes/views.py

class TeamVoteView(APIView):

    def get(self, request, format=None):
        vote = Team_Vote.objects.all()
        serializer = TeamVoteSerializer(vote, many=True)
        vote_count = Team_Vote.objects.filter().values('team').annotate(total=Count('team')).order_by('-total')
        return Response({'message': "투표 조회", 'vote_count':vote_count, 'data': serializer.data}, status=HTTP_200_OK)


    def post(self, request, format=None):
        user = self.request.user
        serializer = TeamVoteSerializer(data=request.data)
        if serializer.is_valid():
            # if Team_Vote.objects.filter(user=user, team=serializer.validated_data['team']):
            #     user_update = get_object_or_404(User, name=user.name)
            #     user_update.team_vote = False
            #     user_update.save()
            #     delete_vote = Team_Vote.objects.get(user=user, team=serializer.validated_data['team'])
            #     delete_vote.delete()
            #     return Response({'message': '투표 취소', 'data': serializer.data, 'user':user_update.team_vote}, status=HTTP_201_CREATED)
            # else:
            #     if user.team == serializer.validated_data['team']:
            #         return Response({'message': '내 팀은 투표 할 수 없습니다!', 'data': serializer.errors},
            #                         status=HTTP_400_BAD_REQUEST)
            #     elif Team_Vote.objects.filter(user=user):
            #         return Response({'message': '투표는 한 번만!', 'data': serializer.errors},
            #                         status=HTTP_400_BAD_REQUEST)
            #     else:
            #         user_update=get_object_or_404(User, name=user.name)
            #         user_update.team_vote=True
            #         user_update.save()
            #         serializer.save(user=self.request.user, team=serializer.validated_data['team'])
            #         return Response({'message': '투표 성공', 'data': serializer.data, 'user':user_update.team_vote}, status=HTTP_201_CREATED)
            if user.team == serializer.validated_data['team']:
                return Response({'message': '내 팀은 투표 할 수 없습니다!', 'data': serializer.errors},
                                status=HTTP_400_BAD_REQUEST)
            elif Team_Vote.objects.filter(user=user):
                return Response({'message': '투표는 한 번만!', 'data': serializer.errors},
                                status=HTTP_400_BAD_REQUEST)
            else:
                user_update=get_object_or_404(User, name=user.name)
                user_update.team_vote=True
                user_update.save()
                serializer.save(user=self.request.user, team=serializer.validated_data['team'])
                return Response({'message': '투표 성공', 'data': serializer.data, 'user':user_update.team_vote}, status=HTTP_201_CREATED)
        return Response({'message': '투표 실패', 'data': serializer.errors}, status=HTTP_400_BAD_REQUEST)

 

  • 후보는 득표 순으로 내림차순 정렬되어 보여집니다
  • 투표 방법에 대해서는 제약이 없습니다. 한 아이디당 한 번만 투표하게 만드셔도 좋고, 투표 버튼 누르는 대로 득표수가 올라가도 상관없습니다.
  • 로그인하지 않은 사용자는 투표 페이지에 접근할 수는 있되, 투표는 불가능합니다.
  • 파트장 투표 : 본인의 파트에 해당하는 파트장 투표만 할 수 있습니다.
  • 데모데이 투표 : 본인이 속한 팀을 제외하고 투표를 할 수 있습니다.

vote_count = Team_Vote.objects.filter().values('team').annotate(total=Count('team')).order_by('-total')

득표수를 return하기 위해 'tema'값을 기준으로 득표수를 세고 vote_count리스트에 담았다.

order_by()함수를 이용해 득표수 변수인 total을 기준으로 내림차순으로 정렬하였다.

 

투표 기능에서 처음에는 투표 취소기능이 있는 것으로 만들었다.

그래서 팀 투표 데이터에 user정보와 team투표 정보가 같은 것이 있을 때를 가정했는데...기능이 없어져서 그대로 없어졌다. 아쉬워서 주석처리만 했다.🥲

그리고 같은 팀 투표와 여러번 투표는 안되도록 만들었다.

 

class PartVoteView(APIView):
    def get(self, request, format=None):
        vote = Part_Vote.objects.all()
        serializer = PartVoteSerializer(vote, many=True)
        vote_count = Part_Vote.objects.filter().values('part').annotate(total=Count('part')).order_by('-total')

        return Response({'message': "투표 조회", 'vote_count':vote_count, 'data': serializer.data}, status=HTTP_200_OK)

    def post(self, request, format=None):
        user = self.request.user
        serializer=PartVoteSerializer(data=request.data)
        if serializer.is_valid():
            if Part_Vote.objects.filter(user=user, part=serializer.validated_data['part']):
                user_update = get_object_or_404(User, name=user.name)
                user_update.part_vote = False
                user_update.save()
                delete_vote = Part_Vote.objects.get(user=user, part=serializer.validated_data['part'])
                delete_vote.delete()
                return Response({'message': '투표 취소', 'data': serializer.data,'user': user_update.part_vote}, status=HTTP_201_CREATED)
            else:
                user_update = get_object_or_404(User, name=user.name)
                user_update.part_vote = True
                user_update.save()
                serializer.save(user=self.request.user, part=serializer.validated_data['part'])
                return Response({'message':'투표 성공', 'data': serializer.data,'user': user_update.part_vote}, status=HTTP_201_CREATED)
        return Response(serializer.errors, status=HTTP_400_BAD_REQUEST)

 

파트장 투표는 여러명 투표가 가능하도록 만들었다.

마찬가지로 get method를 통해 vote_count에는 유저와 유저당 득표수, data에는 투표한 사람과 투표받은 사람을 담아서 return한다. 이거 만들 때 익명으로 할지 확실하게 정해지지 않아서 그냥 모두 보냈다.


api문서

api문서 작성 후 AWS로 배포했다.


오류

 

1. https를 설정하고 나면 http~~로 연결하면 안된다.

http~~는 보안상 get만 가능하다. api문서를 로컬 주소로 만들다보니까 프론트랑 오류가 있었다..

 

2. 배포시 migrations 파일 오류

배포하기 전에 model수정을 굉장히 많이 했는데, docker에서 migrations파일과 중간에 충돌이 난 듯했다.

제대로 데이터베이스가 만들어지지 않았는데, migrations파일을 삭제하고 다시 만들어주었더니 문제가 해결됐다.

 

3. 변수 이름

생각보다 오타나 소통 오류로 인한 변수 오류가 있었다...

문서를 꼼꼼하게 작성하자.

 

바리바리 짱