Skip to content

Commit

Permalink
#9 feat : add user profile model & profile api (#88)
Browse files Browse the repository at this point in the history
  • Loading branch information
0321minji authored Aug 3, 2024
1 parent 393c6b0 commit f0f4bf3
Show file tree
Hide file tree
Showing 9 changed files with 222 additions and 13 deletions.
27 changes: 24 additions & 3 deletions UpcyProject/settings/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,7 @@

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = env.bool('DEBUG', default=False)

ALLOWED_HOSTS = env.list('ALLOWED_HOSTS', default=['*'])
ALLOWED_HOSTS = ['localhost', '127.0.0.1', '.ngrok-free.app']

# Application definition

Expand All @@ -52,6 +51,7 @@

PROJECT_APPS = [
"drf_yasg",
'corsheaders',
"users.apps.UsersConfig",
"core.apps.CoreConfig",
"products.apps.ProductsConfig",
Expand All @@ -71,6 +71,13 @@
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'corsheaders.middleware.CorsMiddleware'

]
CORS_ORIGIN_ALLOW_ALL = True
CSRF_COOKIE_SECURE = False # 개발 중일 때는 False로 설정
CSRF_TRUSTED_ORIGINS = [
"https://3d49-165-132-5-152.ngrok-free.app",
]

AUTH_USER_MODEL = "users.User"
Expand All @@ -92,7 +99,7 @@
REST_USE_JWT = True

SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=10),
'ACCESS_TOKEN_LIFETIME': timedelta(days=1),
'REFRESH_TOKEN_LIFETIME': timedelta(days=28),
'ROTATE_REFRESH_TOKENS': False, # true면 토큰 갱신 시 refresh도 같이 갱신
'BLACKLIST_AFTER_ROTATION': True,
Expand Down Expand Up @@ -175,3 +182,17 @@
AWS_SECRET_ACCESS_KEY = os.getenv('AWS_SECRET_ACCESS_KEY')
AWS_STORAGE_BUCKET_NAME = os.getenv('AWS_STORAGE_BUCKET_NAME', 'upcy-bucket')
AWS_S3_REGION_NAME = os.getenv('AWS_S3_REGION_NAME', 'ap-northeast-2')
# SWAGGER_SETTINGS = {
# 'USE_SESSION_AUTH': False,
# 'SECURITY_DEFINITIONS': {
# 'BearerAuth': {
# 'type': 'apiKey',
# 'name': 'Authorization',
# 'in': 'header',
# 'description': "Bearer Token"
# }
# },
# 'SECURITY_REQUIREMENTS': [{
# 'BearerAuth': []
# }]
# }
4 changes: 4 additions & 0 deletions users/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,8 @@ class Intership(admin.ModelAdmin):

@admin.register(models.Freelancer)
class Freelancer(admin.ModelAdmin):
pass

@admin.register(models.UserProfile)
class UserProfile(admin.ModelAdmin):
pass
26 changes: 26 additions & 0 deletions users/migrations/0015_remove_user_profile_image_userprofile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Generated by Django 4.0 on 2024-08-03 06:03

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


class Migration(migrations.Migration):

dependencies = [
('users', '0014_alter_certification_proof_document_and_more'),
]

operations = [
migrations.RemoveField(
model_name='user',
name='profile_image',
),
migrations.CreateModel(
name='UserProfile',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('profile_image', models.URLField(blank=True, null=True)),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='users.user')),
],
),
]
18 changes: 18 additions & 0 deletions users/migrations/0016_userprofile_introduce.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 4.0 on 2024-08-03 06:29

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('users', '0015_remove_user_profile_image_userprofile'),
]

operations = [
migrations.AddField(
model_name='userprofile',
name='introduce',
field=models.TextField(blank=True, null=True),
),
]
8 changes: 6 additions & 2 deletions users/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class User(AbstractBaseUser, PermissionsMixin):
phone = models.CharField(max_length = 15, blank=True, null=True)
code = models.CharField(max_length=5, blank=True, null=True)
nickname = models.CharField(max_length=20, blank=True)
profile_image = models.ImageField(upload_to=get_upload_path, blank=True, null=True)
#profile_image = models.ImageField(upload_to=get_upload_path, blank=True, null=True)
agreement_terms = models.BooleanField(default = False)
follows = models.ManyToManyField("users.User", related_name='followers', blank=True)
is_superuser = models.BooleanField(default=False)
Expand Down Expand Up @@ -97,7 +97,11 @@ def clean(self):
raise ValidationError('메일 형식이 올바르지 않습니다.')
# if not user_type_is_valid(self):
# raise ValidationError('유저 타입이 잘못되었습니다.')

class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
profile_image = models.URLField(blank=True, null=True)
introduce=models.TextField(blank=True,null=True)

#Reformer profile 모델
class ReformerProfile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='reformer_profile')
Expand Down
21 changes: 19 additions & 2 deletions users/selectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
from django.http import Http404, HttpResponseBadRequest
# from django.db.models import QuerySet, Q, F
# from django.contrib.auth import authenticate

from users.models import User, ReformerProfile
from django.core.exceptions import ObjectDoesNotExist
from users.models import User, ReformerProfile,UserProfile

class UserSelector:
def __init__(self):
Expand All @@ -22,6 +22,23 @@ def get_user_by_email(email: str) -> User:
def check_password(user: User, password: str):
return user.check_password(password)

@staticmethod
def get_user_profile_by_email(email:str) -> UserProfile:
try:
user = User.objects.get(email=email)
user_profile = UserProfile.objects.get(user=user)

data = {
"nickname": user.nickname,
"profile_image": user_profile.profile_image,
"introduce": user_profile.introduce,
}
return data
except ObjectDoesNotExist:
return {
"error": "User or UserProfile not found"
}

class ReformerSelector:
def __init__(self):
pass
Expand Down
19 changes: 18 additions & 1 deletion users/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from django.core.files.uploadedfile import InMemoryUploadedFile
from django.core.files import File
from django.core.files.base import ContentFile
from users.models import User, ReformerProfile, Certification, Competition, Internship, Freelancer
from users.models import User, ReformerProfile, Certification, Competition, Internship, Freelancer, UserProfile
from users.selectors import UserSelector
# from core.exceptions import ApplicationError
from core.utils import s3_file_upload_by_file_data
Expand Down Expand Up @@ -74,6 +74,23 @@ def login(self, email: str, password: str):

return data

def user_profile_image_register(self,user:User,img:ImageFile):
img_url=s3_file_upload_by_file_data(
upload_file=img,
region_name=settings.AWS_S3_REGION_NAME,
bucket_name=settings.AWS_STORAGE_BUCKET_NAME,
bucket_path=f'profile/{user.pk}/img'
)

user_profile=UserProfile(user=user,profile_image=img_url)
user_profile.save()
data={
"email":user_profile.user.email,
"nickname":user_profile.user.nickname,
"img":user_profile.profile_image,
}
return data

def reformer_profile_register(self,user:User, nickname:str, market_name:str,market_intro:str,links:str,
work_style:list[str],special_material:list[str]):

Expand Down
4 changes: 3 additions & 1 deletion users/urls.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from django.urls import path
from rest_framework_simplejwt.views import TokenRefreshView, TokenVerifyView
from .views import UserSignUpApi,UserLoginApi,ReformerProfileApi,CertificationCreateApi,CompetitionCreateApi,IntershipCreateApi,FreelancerCreateApi
from .views import *
app_name = "users"

urlpatterns =[
Expand All @@ -15,4 +15,6 @@
path('intership_register/',IntershipCreateApi.as_view(),name='intership'),
path('freelancer_register/',FreelancerCreateApi.as_view(),name='freelancer'),
path('reformer_profile/<int:user_id>/',ReformerProfileApi.as_view(),name='profile'),
path('profile/img/',UserProfileImageApi.as_view(),name='profile_img'),
path('profile/',UserDetailApi.as_view(),name='profile'),
]
108 changes: 104 additions & 4 deletions users/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from users.models import User
from users.services import UserService
from users.selectors import ReformerSelector
from users.selectors import ReformerSelector,UserSelector
from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema

Expand Down Expand Up @@ -127,7 +127,62 @@ def post(self, request):
'status': 'success',
'data': output_serializer.data,
}, status = status.HTTP_200_OK)

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

class UserProfileInputSerializer(serializers.Serializer):
img=serializers.ImageField()

class UserProfileOutputSerializer(serializers.Serializer):
email=serializers.CharField()
nickname=serializers.CharField()
img=serializers.URLField()
@swagger_auto_schema(
request_body=UserProfileInputSerializer,
security=[],
operation_description='유저 프로필 이미지를 등록하는 API 입니다.',
operation_id='유저 프로필 이미지 등록 API',
responses={
"200": openapi.Response(
description="OK",
examples={
"application/json": {
"status": "success",
"email":"[email protected]",
"nickname":"sdptech",
"img": "https://upcy-bucket.s3.ap-northeast-2.amazonaws.com/profile/1/img/20240803152516_d10b2b3828f7403387ea.webp",
}
}
),
"400": openapi.Response(
description="Bad Request",
),
},
)
def post(self,request):
serializers=self.UserProfileInputSerializer(data=request.data)
serializers.is_valid(raise_exception=True)
data=serializers.validated_data

service=UserService()
profile_data=service.user_profile_image_register(
user=request.user,
img=data.get('img')
)
output_serializer = self.UserProfileOutputSerializer(data = profile_data)
output_serializer.is_valid(raise_exception=True)
try:
# data=service.user_profile_image_register(
# user=request.user,
# img=data.get('img'),
# )
return Response({'status':'succes',
'data':output_serializer.data},status=status.HTTP_200_OK)
except Exception as e:
print(f"Failed to upload img to s3:{e}")
return None
class ReformerProfileApi(APIView):
permission_classes = (AllowAny,)

Expand All @@ -137,8 +192,12 @@ class ReformerProfileInputSerializer(serializers.Serializer):
market_intro=serializers.CharField()
links=serializers.CharField()

work_style=serializers.CharField()
special_material=serializers.CharField()
work_style = serializers.ListField(
child=serializers.CharField(), allow_empty=True
)
special_material = serializers.ListField(
child=serializers.CharField(), allow_empty=True
)

class ReformerProfileOuputSerializer(serializers.Serializer):
market_intro=serializers.CharField()
Expand Down Expand Up @@ -432,4 +491,45 @@ def post(self,request):
'status':'success',
},status=status.HTTP_200_OK)
except Exception as e:
return Response({'status': 'error', 'message': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
return Response({'status': 'error', 'message': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

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

class UserDetailOutputSerializer(serializers.Serializer):
nickname=serializers.CharField()
introduce=serializers.CharField()
profile_image=serializers.URLField()

@swagger_auto_schema(
security=[],
operation_id='유저 기본정보 조회 API',
operation_description="유저의 기본 정보인 닉네임, 프로필 이미지, 소개글을 반환하는 API 입니다.",
responses={
"200":openapi.Response(
description="OK",
examples={
"application/json":{
"status":"success",
"data":{"nickname": "test",
"introduce": "introduce~~~",
"profile_image": "https://upcy-bucket.s3.ap-northeast-2.amazonaws.com/profile/1/img/20240803152516_d10b2b3828f7403387ea.webp"}
}
}
),
"400":openapi.Response(
description="Bad Request",
),
}
)

def get(self,request):
user=UserSelector.get_user_profile_by_email(request.user.email)

serializers=self.UserDetailOutputSerializer(user)

return Response({
'status':'success',
'data':serializers.data,
},status=status.HTTP_200_OK)

0 comments on commit f0f4bf3

Please sign in to comment.