Skip to content

Commit

Permalink
1차 작업내용 배포 (#4)
Browse files Browse the repository at this point in the history
* [test]: 컨테이너간 포트포워딩 테스트

* [test]: 컨테이너 포트포워딩 테스트2

* Create User, Candidate models

* Create serializer

* Create Views

* merge

* [etc]: branch merge

* [test]: 자동배포 에러 해결을 위한 테스트 코드 추가

* [test]: 자동배포 에러 해결을 위한 테스트 코드 추가

* [test]:자동배포 에러 해결을 위한 테스트 코드 추가

* [etc]: migration 내용 추가

* [test]:자동배포 에러 해결을 위한 테스트 코드 추가

* [fix]: 오타 수정

* [test]:자동배포 에러 해결을 위한 테스트 코드 추가

* [test]:자동배포 에러 해결을 위한 테스트 코드 추가

* [test]:자동배포 에러 해결을 위한 테스트 코드 추가

* [test]:자동배포 에러 해결을 위한 테스트 코드 추가

* [test]: migrations 파일 추가

* [test]: prod.py setting pymysql 추가

* [feat]: CORS 셋팅

* [etc]: migrate 파일 추가

Co-authored-by: YOONJI KIM <[email protected]>
  • Loading branch information
seungwooKim99 and stritegdc authored Dec 5, 2021
1 parent 1dd88b0 commit 1a55c2c
Show file tree
Hide file tree
Showing 13 changed files with 375 additions and 3 deletions.
Binary file added .DS_Store
Binary file not shown.
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ WORKDIR /app

# dependencies for psycopg2-binary
RUN apk add --no-cache mariadb-connector-c-dev
RUN apk update && apk add libpq
RUN apk update && apk add python3 python3-dev mariadb-dev build-base && pip3 install mysqlclient


# By copying over requirements first, we make sure that Docker will cache
# our installed requirements rather than reinstall them on every build
COPY requirements.txt /app/requirements.txt
RUN pip install -r requirements.txt

# Now copy in our code, and run it
COPY . /app/
COPY . /app/
2 changes: 2 additions & 0 deletions Dockerfile.prod
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ ENV PYTHONUNBUFFERED 1
# install psycopg2 dependencies
RUN apk update && apk add python3 python3-dev mariadb-dev build-base && pip3 install mysqlclient


# install dependencies
RUN python3 -m pip install --upgrade pip
COPY ./requirements.txt .
RUN pip wheel --no-cache-dir --no-deps --wheel-dir /usr/src/app/wheels -r requirements.txt

Expand Down
49 changes: 49 additions & 0 deletions api/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Generated by Django 3.0.8 on 2021-12-02 11:17

import api.models
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

initial = True

dependencies = [
]

operations = [
migrations.CreateModel(
name='Candidate',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_date', models.DateTimeField(auto_now_add=True)),
('updated_date', models.DateTimeField(auto_now=True)),
('name', models.CharField(max_length=50)),
('votes', models.IntegerField(default=0)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='User',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('created_date', models.DateTimeField(auto_now_add=True)),
('updated_date', models.DateTimeField(auto_now=True)),
('email', models.EmailField(max_length=255, unique=True, verbose_name='email')),
('userid', models.CharField(max_length=50, unique=True)),
('voteDone', models.BooleanField(default=False)),
('voting_for', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='voters', to='api.Candidate')),
],
options={
'abstract': False,
},
managers=[
('objects', api.models.UserManager()),
],
),
]
67 changes: 67 additions & 0 deletions api/models.py
Original file line number Diff line number Diff line change
@@ -1 +1,68 @@
from django.db import models
from django.contrib.auth.models import (BaseUserManager, AbstractBaseUser)

# Create your models here.


class Base(models.Model):
created_date = models.DateTimeField(auto_now_add=True)
updated_date = models.DateTimeField(auto_now=True)

class Meta:
abstract = True


class Candidate(Base):
name = models.CharField(max_length=50, null=False)
votes = models.IntegerField(default=0)


class UserManager(BaseUserManager):
use_in_migrations = True

def create_user(self, email, userid, password=None):
if not "email":
raise ValueError('Users must have an email address')

user = self.model(
email=self.normalize_email(email),
userid=userid,
)
user.set_password(password)
user.save(using=self._db)
return user

def create_superuser(self, email, userid, password):
superuser = self.create_user(
email=self.normalize_email(email),
userid=userid,
password=password)
superuser.is_admin = True
superuser.is_superuser = True
superuser.save(using=self._db)
return superuser


class User(AbstractBaseUser, Base):
email = models.EmailField(
verbose_name='email',
max_length=255,
unique=True,
)
userid = models.CharField(max_length=50, unique=True, null=False)
voteDone = models.BooleanField(default=False)
voting_for = models.ForeignKey(Candidate, on_delete=models.CASCADE, related_name='voters', null=True)

objects = UserManager()

USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []

def __str__(self):
return self.email

def has_perm(self, perm, obj=None):
return True

def has_module_perms(self, app_label):
return True
84 changes: 84 additions & 0 deletions api/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
from rest_framework import serializers
from rest_framework_jwt.settings import api_settings
from django.contrib.auth.backends import ModelBackend
from django.contrib.auth import authenticate
from django.contrib.auth.models import update_last_login
from django.contrib.auth import get_user_model
from .models import *

# JWT 사용을 위한 설정
JWT_PAYLOAD_HANDLER = api_settings.JWT_PAYLOAD_HANDLER
JWT_ENCODE_HANDLER = api_settings.JWT_ENCODE_HANDLER

User = get_user_model()

# 회원가입.
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['userid', 'email', 'password']

def create(self, validated_data):
user = User.objects.create(
email=validated_data['email'],
userid=validated_data['userid'],
password=validated_data['password'],
)
#user.set_password(validated_data['password'])
user.save()
return user


class LoginBackend(ModelBackend): # 준환님 readme.md 참고. 이거 안하면 계속 userid=None 나옴..
def authenticate(self, request, userid=None, password=None, **kwargs):
try:
user = User.objects.get(userid=userid)
#if user.check_password(password):
if user.password == password:
return user
return None

except User.DoesNotExist:
return None


# 로그인
class UserLoginSerializer(serializers.ModelSerializer):
userid = serializers.CharField(max_length=50)
password = serializers.CharField(max_length=150, write_only=True)
token = serializers.CharField(max_length=255, read_only=True)

class Meta:
model = User
fields = ['userid', 'password', 'token']

def validate(self, data):
userid = data.get("userid", None)
password = data.get("password", None)
user = authenticate(userid=userid, password=password)

if user is None:
return {'userid': 'None'} # password가 안맞아도 해당 에러.
try:
payload = JWT_PAYLOAD_HANDLER(user)
jwt_token = JWT_ENCODE_HANDLER(payload)
update_last_login(None, user)

except User.DoesNotExist:
raise serializers.ValidationError(
'User does not exist'
)
return {
'userid': user.userid,
'token': jwt_token
}


class CandidateSerializer(serializers.ModelSerializer):
voters = UserSerializer(many=True, read_only=True)
class Meta:
model = Candidate
fields = ['name', 'votes', 'voters']



21 changes: 21 additions & 0 deletions api/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from django.urls import path, include
from . import views
from .views import *
from rest_framework import routers
from rest_framework_jwt.views import verify_jwt_token
from rest_framework_jwt.views import obtain_jwt_token
from rest_framework_jwt.views import refresh_jwt_token

router = routers.DefaultRouter()
router.register(r'users', UserViewSet)
router.register(r'candidates', CandidateViewSet)

urlpatterns = router.urls

urlpatterns += [
path('users/login', views.LoginView.as_view()),
path('users/vote/<int:id>', views.VoteView.as_view()),
path('token', obtain_jwt_token),
path('users/token/refresh', refresh_jwt_token),
path('users/token/verify', verify_jwt_token),
]
63 changes: 63 additions & 0 deletions api/views.py
Original file line number Diff line number Diff line change
@@ -1 +1,64 @@
from .models import User, Candidate
from .serializers import *
from rest_framework.views import APIView
from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticated, AllowAny
from rest_framework.response import Response
from rest_framework import status
from rest_framework.generics import get_object_or_404
from rest_framework.filters import OrderingFilter

# Create your views here.

User = get_user_model()


class UserViewSet(viewsets.ModelViewSet):
permission_classes = (AllowAny,)
queryset = User.objects.all()
serializer_class = UserSerializer


class CandidateViewSet(viewsets.ModelViewSet):
permission_classes = (AllowAny,)
queryset = Candidate.objects.all()
serializer_class = CandidateSerializer
filter_backends = [OrderingFilter]
# ordering_fields = ['-votes'] # 정렬 허용 리스트
ordering = ['-votes'] # default 정렬 지정


class LoginView(APIView):
permission_classes = (AllowAny,)

def post(self, request, format=None):
serializer = UserLoginSerializer(data=request.data)
if serializer.is_valid():
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


class VoteView(APIView):
permission_classes = (IsAuthenticated,)

def get_object(self, id):
candidate = get_object_or_404(Candidate, pk=id)
return candidate

def get(self, request, id, format=None):
candidate = self.get_object(id)
user = request.user
if user.voteDone:
return Response({"message":"voteDone"}, status=status.HTTP_403_FORBIDDEN)

user.voting_for = candidate
user.voteDone = True
candidate.votes += 1
user.save()
candidate.save()
return Response({"message":"Successfully Voted to " + user.voting_for.name}, status=status.HTTP_200_OK)





46 changes: 46 additions & 0 deletions like-that-house-vote/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import os
import environ
import datetime

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
Expand Down Expand Up @@ -40,10 +41,55 @@
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.sites',
'api',
'rest_framework',
'rest_auth.registration',
'allauth',
'allauth.account',
'corsheaders',
]

SITE_ID = 1

AUTH_USER_MODEL = 'api.User'

REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated', # 인증된 회원만 허용
'rest_framework.permissions.AllowAny', # 모든 접근 허용
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
# 'rest_framework.authentication.SessionAuthentication',
# 'rest_framework.authentication.BasicAuthentication',
),
}


JWT_AUTH = {
'JWT_SECRET_KEY': SECRET_KEY,
'JWT_ALGORITHM': 'HS256', # 암호화 알고리즘
'JWT_ALLOW_REFRESH': True, # jwt 갱신 여부
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7), # wt 토큰 유효기간
'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=28), # jwt 토큰 갱신 유효기간
}

CORS_ORIGIN_ALLOW_ALL = True # 모든 호스트 허용

AUTHENTICATION_BACKENDS = (
# 'django.contrib.auth.backends.ModelBackend',
'api.serializers.LoginBackend',
)

ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_EMAIL_VERIFICATION = "mandatory"

REST_USE_JWT = True
ACCOUNT_LOGOUT_ON_GET = True

MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
Expand Down
2 changes: 2 additions & 0 deletions like-that-house-vote/settings/prod.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from .base import * # noqa
import pymysql
pymysql.install_as_MySQLdb()

DEBUG = False
ALLOWED_HOSTS = env.list('DJANGO_ALLOWED_HOSTS')
Expand Down
Loading

0 comments on commit 1a55c2c

Please sign in to comment.