데이터가 많아지면 한 페이지에서 모든 정보를 확인하기엔 불편함이 있기 때문에 원하는 갯수만큼 page를 나눠서 출력하도록 했다.
APIView
는 settings.py
에서 전역설정해도 적용이 안되기 때문에 직접 해야한다. 여러가지 pagination 방법 중 나는 PageNumberPagination
을 사용할 것이다.
pagination 부분은 따로 파일을 만들어서 저장하여 Mixin해서 사용한다.
class PaginationHandlerMixin(object):
@property
def paginator(self):
if not hasattr(self, '_paginator'):
if self.pagination_class is None:
self._paginator=None
else:
self._paginator=self.pagination_class()
else:
pass
return self._paginator
def paginate_queryset(self, queryset):
if self.paginator is None:
return None
return self.paginator.paginate_queryset(queryset, self.request, view=self)
def get_paginated_response(self, data):
assert self.paginator is not None
return self.paginator.get_paginated_response(data)
class ProductPagination(PageNumberPagination):
page_size=5
class ProductsAPIView(APIView, PaginationHandlerMixin):
permission_classes = [IsAuthenticatedOrReadOnly]
pagination_class=ProductPagination
def get(self, request):
products = Product.objects.all()
page=self.paginate_queryset(products)
if page is not None:
serializer=self.get_paginated_response(ProductSerializer(page, many=True).data)
else:
serializer = ProductSerializer(products, many=True)
return Response(serializer.data)
로그아웃은 토큰을 blacklist를 하는 방법을 사용하여 Logout을 구현한다.
Reference logout 2의 블로그를 참고하여 simple_jwt을 사용하면 간단히 구현이 가능하다.
from rest_framework_simplejwt.views import TokenBlacklistView
urlpatterns = [
...
path('logout/', TokenBlacklistView.as_view(), name='token_blacklist'),
...
]
하지만 문제가 발생했다.
Method Not Allowed (POST): /accounts/login/
해당 오류의 원인을 찾아 구글링을 하던 중 Reference logout 3의 블로그를 찾았고 url이 원인인 것 같아 기존 api/accounts/logout
을 api/accounts/auth/logout
으로 변경하니 작동이 잘 되었다.
하지만 TokenBlackListView를 사용하니 결과 메세지가 비어서 출력되어 이를 출력하기 위해 Reference logout 2의 방식으로 변경하기로 했다.
class LogoutAPIView(APIView):
permission_classes = [IsAuthenticated]
def post(self, request):
token = RefreshToken(request.data.get("refresh"))
token.blacklist()
return Response({"ok": "Bye!"}, status=status.HTTP_200_OK)
urls.py
도 이에 맞게 변경
위와 같이 바꾸어 blacklist
가 되면 해당 메세지가 출력되도록 구현하였다.
회원 정보 수정을 위해 product에서 한 것처럼 Custom Permission을 만들어서 사용하려 하는데
permission이 제대로 동작하지 않았다. product와 무엇이 다른지 알기 위해 해당 코드의 변수를 출력해보기로 했다.
class IsOwnerOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True
print(type(obj.username), obj.username)
print(type(obj), obj)
print(type(request.user), request.user)
print(obj.username == request.user)
return obj.username == request.user
모두 값으로는 admin이 나오지만 기존의 obj.username
와 request.user
의 타입이 달라서 True를 반환하지 못했고 obj
와 request.user
는 타입도 같지만 어째서인지 이도 False를 반환하였다.(obj
와 request.user
는 왜 False....)
그래서 나는 반환되는 request.user
를 string으로 바꾸는 방법으로 이를 해결하고 True를 반환하여 프로필 데이터를 수정하는 것을 구현했다.
return obj.username == request.user
=> return obj.username == str(request.user)