반응형
우선, 어렵고 실수했던 부분을 집고 가야 할 것 같다.
1. 유저 인증 및 확인을 ID값 즉 PK 값으로 찾았다.
JWT를 이용하여 로그인 로그아웃 구현해 놓고 정작 유저 정보를 가지고 오고 수정하는 곳에서는 JWT 토큰을 사용하는 게 아니라 유저 PK값을 사용해서 구현하였다.
구현 후 토큰 없이 GET,POST를 보내면 아래와 같이 출력된다.
2. 다대다 관계에서의 DB 수정이 어려웠다.
유저 객체 안에 Skills와 Roles라는 모델이 다대다 관계로 외래키로 묶여 있는데 이 부분을 어떻게 수정하고 불러와야 하는지 어려웠다.
전체코드
Views.py
from django.utils import timezone
from rest_framework_simplejwt.tokens import AccessToken
from rest_framework.permissions import IsAuthenticated
from rest_framework_simplejwt.authentication import JWTAuthentication
from drf_yasg.utils import swagger_auto_schema
from drf_yasg import openapi
from rest_framework.generics import GenericAPIView
from rest_framework import mixins
from user.models import User, Skill, Role, UserImage
from user.serializers import UserInfoSerializer
class UserInfoAPI(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, GenericAPIView):
queryset = User.objects.all()
serializer_class = UserInfoSerializer
authentication_classes = [JWTAuthentication]
permission_classes = [IsAuthenticated]
def get_user_instance(self):
auth_header = self.request.headers.get('Authorization')
access_tokne = auth_header.split(' ')[1]
decoded = AccessToken(access_tokne)
user_id = decoded['user_id']
self.user_instance = User.objects.get(id=user_id)
return self.user_instance
@swagger_auto_schema(security=[{"Bearer": []}])
def get_object(self):
user = self.get_user_instance()
return user
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
@swagger_auto_schema(request_body=openapi.Schema(
type=openapi.TYPE_OBJECT,
properties={
'name': openapi.Schema(type=openapi.TYPE_STRING,description='name'),
'nickname': openapi.Schema(type=openapi.TYPE_STRING, description='User nickname (max length: 15).'),
'birthday': openapi.Schema(type=openapi.TYPE_STRING, format=openapi.FORMAT_DATE, description='User birthday, format (e.g., "1990-01-01").'),
'skills_list': openapi.Schema(type=openapi.TYPE_ARRAY, items=openapi.Schema(type=openapi.TYPE_STRING), description='List of user skills.'),
'roles_list': openapi.Schema(type=openapi.TYPE_ARRAY, items=openapi.Schema(type=openapi.TYPE_STRING), description='List of user roles.'),
'profile_img': openapi.Schema(type=openapi.TYPE_STRING, description='URL to user profile image.'),
}
))
def patch(self, request, *args, **kwargs):
user = self.get_user_instance()
updated_at = timezone.now().strftime('%Y-%m-%dT%H:%M:%SZ')
nickname =request.data.get('nickname')
name = request.data.get('name')
birthday =request.data.get('birthday')
skills_list = request.data.get('skills_list')
roles_list = request.data.get('roles_list')
profile_img =request.data.get('profile_img')
validated_skills = []
for skill_name in skills_list:
skill = Skill.objects.filter(name=skill_name).first()
if skill:
validated_skills.append(skill)
user.skills.set(validated_skills)
validated_roles = []
for role_name in roles_list:
role = Role.objects.filter(name=role_name).first()
if roles_list:
validated_roles.append(role)
user.roles.set(validated_roles)
if profile_img:
new_profile_image = UserImage.objects.create(image=profile_img)
user.user_image = new_profile_image
user.updated_at = updated_at
user.nickname = nickname
user.name = name
user.birthday = birthday
user.save()
return self.partial_update(request, *args, **kwargs)
Serializers.py
class UserInfoSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = [
"id",
"password",
"created_at",
"updated_at",
"email",
"name",
"nickname",
"birthday",
"user_image",
"profile_img",
"skills_list",
"roles_list",
]
skills_list = serializers.SerializerMethodField("get_skills_list")
roles_list = serializers.SerializerMethodField("get_roles_list")
profile_img = serializers.SerializerMethodField("get_profile_img")
def get_profile_img(self, obj):
profile_img = UserImage.objects.get(id=obj.user_image_id)
return profile_img.image.url
def get_skills_list(self, obj):
skills_queryset = Skill.objects.filter(user=obj)
skills_list = [skill.name for skill in skills_queryset]
return skills_list
def get_roles_list(self, obj):
roles_queryset = Role.objects.filter(user=obj)
roles_list = [role.name for role in roles_queryset]
return roles_list
코드 설명
Views.py
from rest_framework.permissions import IsAuthenticated
from rest_framework_simplejwt.authentication import JWTAuthentication
authentication_classes = [JWTAuthentication]
permission_classes = [IsAuthenticated]
인증 클래스는 JWT인증을 사용하고 권한은 인증된 사용자만 가질 수 있도록 한다는 말이다.
from rest_framework_simplejwt.tokens import AccessToken
def get_user_instance(self):
auth_header = self.request.headers.get('Authorization')
access_tokne = auth_header.split(' ')[1]
decoded = AccessToken(access_tokne)
user_id = decoded['user_id']
self.user_instance = User.objects.get(id=user_id)
return self.user_instance
JWT 토큰을 이용해서 user_id값을 얻어오는 메서드이다.
- 헤더에 들어있는 JWT토큰을 가지고 온다.
- JWT 토큰의 경우 공백으로 'Bearer JWT' or 'Tokne JWT'처럼 공백으로 분리돼서 넘어오기 때문에 공백 분리를 한 후 뒤에 JWT 값만 사용해 준다.
- 가지고 온 액세스토큰의 인스턴스를 생성하고 user_id 값을 추출한다.
validated_skills = []
for skill_name in skills_list:
skill = Skill.objects.filter(name=skill_name).first()
if skill:
validated_skills.append(skill)
user.skills.set(validated_skills)
validated_roles = []
for role_name in roles_list:
role = Role.objects.filter(name=role_name).first()
if roles_list:
validated_roles.append(role)
user.roles.set(validated_roles)
if profile_img:
new_profile_image = UserImage.objects.create(image=profile_img)
user.user_image = new_profile_image
이 부분이 약간 어려웠던 부분인데 다대다 관계에서 skill의 name이 넘어오면 추가, 수정, 삭제해주는 부분이다.
(유저 이미지는 일대일 관계이다.)
- for문을 돌면서 입력받은 name을 이용해서 Skill 모델에서 그 이름을 가진 오브젝트를 가지고 온다.
- 가지고 온 오브젝트를 validated_skills 리스트에 추가해 준다.
- validated_skills를 user.skills.set()에 넣어준다.
이렇게 해주는 이유는 우선 다대다 관계를 수정하기 위해서 user.skills.set()을 사용해주어야 하는데 여기에 들어가는 매개 변수가 리스트형 인스턴스, 쿼리 셋, Pk값이기 때문이다.
Serializers.py
skills_list = serializers.SerializerMethodField("get_skills_list")
roles_list = serializers.SerializerMethodField("get_roles_list")
profile_img = serializers.SerializerMethodField("get_profile_img")
def get_profile_img(self, obj):
profile_img = UserImage.objects.get(id=obj.user_image_id)
return profile_img.image.url
def get_skills_list(self, obj):
skills_queryset = Skill.objects.filter(user=obj)
skills_list = [skill.name for skill in skills_queryset]
return skills_list
def get_roles_list(self, obj):
roles_queryset = Role.objects.filter(user=obj)
roles_list = [role.name for role in roles_queryset]
return roles_list
user와 외래키로 연결되어 있는 객체들을 나타내기 위해서 각 모델에서 user가 obj인 필터링 해서 for 문으로 모델의 name 값을 리스트로 만들어서 리턴해 준다.
반응형
'Django, DRF' 카테고리의 다른 글
DRF 유저 정보로 JWT 토큰 발급 (0) | 2023.10.11 |
---|---|
Django JWT를 이용한 소셜로그인 (백엔드) (0) | 2023.10.11 |
DRF Image DB 저장, 스웨거 적용 (0) | 2023.10.10 |
Django 인기글 구현 (1) | 2023.09.26 |
클라우드 타입으로 Django, postgreSQL 서버 배포 (1) | 2023.08.09 |