users앱을 더하여서 API와 url 연결로 넘어가도록 했어요.
...
urlpatterns = [
path('admin/', admin.site.urls),
path('api/v1/rooms/', include('rooms.urls')),
path('api/v1/users/', include('users.urls')),
]
...
users/urls.py
from django.urls import path
from . import views
app_name = "users"
urlpatterns = [
path('me/', views.MeView.as_view()),# 나의 계정 정보
path('<int:pk>/', views.user_detail), # 다른 유저의 정보
]
본인의 계정정보를 확인하고 수정하는 API를 작성 해볼게요.
필요한 것들을 임포트 해줍니다. APIView
로 작성하니 관련 된 것들을 상단에 일단 깔아 둘게요.
이번에 정말 중요한 녀석중에 하나인데요. permission_classes
APIView를 사용하게 되면 쓸수 있는 카드중 하나에요. 클래스에 어떤 허가?를 준다는 말일까요?
뺑뻉 돌려 얘기했는데 로그인한 상태일 경우 해당 API, MeView클래스에 접근 가능하다는 소리에요.
users/views.py
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated # 인증한 유저만 해당 메소드에 접근 가능하도록함.
from rest_framework.decorators import api_view
from rest_framework.views import APIView
from rest_framework import status
from .models import User
from .serializers import ReadUserSerializer, WriteUserSerializer
class MeView(APIView):
permission_classes = [IsAuthenticated] # 인증된 유저만 접근 가능
def get(self, request):
return Response(ReadUserSerializer(request.user).data) # stats 키워드가 없으면 기본값으로 200을 뱉어네요.
def put(self, request):
serializer = WriteUserSerializer(request.user, data=request.data, partial=True)
# partial=True옵션 안쓰면 PUT메서드 사용시 하나 혹은 전체가 아닌 일부만 달랑 보내면 오류발생.
if serializer.is_valid():
serializer.save()
return Response()
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQEUST)
@api_view(['GET'])
def user_detail(request, pk):
try:
user = User.objects.get(pk=pk)
return Response(ReadUserSerializer(user).data)
except User.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
기존 Serializer 클래스를 상속 받은 UserSerializer를 ModelSerializer로 상속받고 이름도 RelatedUserSerializer로 변경할게요.
RelatedUserSerializer
의 목적은 관계를 맺어서 정보를 표현하기 위한 용도로 일단 만들게요.
그렇 해당 관련은 방을 등록한 Host의 정보와 연결하는거에요.
즉, 아래에 ReadRoomSerializer
클래스 변수에서 사용하게 만들어서 중첩 시리얼라이저로 쓰는거조.
# rooms/serializers.py
...
from users.serializers import RelatedUserSerializer
class ReadRoomSerializer(serializers.ModelSerializer):
user = RelatedUserSerializer()
class Meta:
...
반면에 ReadUserSerializer는 비밀번호까지 있는 만큼 유저의 더 프라이빗한 정보까지 보여주도록 하는 용도로 만들게요.
user/serializers.py
기존 room API에서 RoomSerializer를 Serializer로 작성해서 뚱뚱했는데요. 날씬하고 편리하게 만들어주기 위해 모두 아래와 같이 ModelSerializer를 상속 받아서 작성할게요.
from rest_framework import serializers
from .models import User
class RelatedUserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = (
'username',
'first_name',
'last_name',
'email',
'avatar',
'superhost',
)
class ReadUserSerializer(serializers.ModelSerializer):
class Meta:
model = User
exclude = (
'groups',
'user_permissions',
'password',
'last_login',
'is_superuser',
'is_staff',
'is_active',
'date_joined',
)
class WriteUserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('username', 'first_name', 'last_name', 'email') # Profile을 변경할때 사용할 API에 사용
def validate_first_name(self, value):
print(value)
return value.upper() # 특정필드를 오버라이딩하여 사용할 수 있음
room/serializer.py
기존 엄청나게 길었던 녀석인WriteUserSerializer
는 ModelSerializer를 상속 받아 작성할게요.
메타클래스를 작성해서 Room클래스를 model에 넣어주고
exclude 메타클래스 변수를 작성해서 4개 빼고 모두 나타나도록 만들어 줄게요.
from rest_framework import serializers
from users.serializers import RelatedUserSerializer
from .models import Room
class ReadRoomSerializer(serializers.ModelSerializer):
user = RelatedUserSerializer()
class Meta:
model = Room
exclude = ("modified",)
class WriteRoomSerializer(serializers.ModelSerializer):
class Meta:
model = Room
exclude = ("user", "modified", "created")
def validate(self, data):
if self.instance:
check_in = data.get("check_in", self.instance.check_in)
check_out = data.get("check_out", self.instance.check_out)
else:
check_in = data.get("check_in")
check_out = data.get("check_out")
if check_in == check_out:
raise serializers.ValidationError("Not enough time between changes")
return data