지난주에 이어 오늘은 정참조와 역참조, serializer, permission_classes에 대해 공부하였다.
정참조와 역참조
정참조 : 참조하려는 객체가 다른 객체의 ForeignKey를 가지고 있거나 1:1(One-to-One) 관계일 경우
역참조 : 다른 객체가 ForeignKey를 가지고 있거나 N:N(Many-to-Many) 관계일 경우, 해당 객체를 참조하고 있는 다른 객체를 참조하려는 경우
예시를 위해 user 앱의 models.py를 살펴보자.


해당 모델들에 대한 관계는 해당 사진과 같다.
이제 views.py에서 hobby에 대한 정보를 가져와서 조회하려고 할 때, 정참조와 역참조를 사용하면 다음과 같이 표현이 가능하다.
def get(self, request):
user = request.user # 로그인한 유저 정보
# 역참조를 사용했을 때
hobbys = user.userprofile.hobby.all()
# 역참조를 사용하지 않았을 때
user_profile = UserProfile.objects.get(user=user)
hobbys = user_profile.hobby.all()
역참조를 사용할 때의 예시를 살펴보자.
모델을 확인하면 알 수 있다시피 user 안에는 hobby가 존재하지 않는 것을 볼 수 있는데, 이때 역참조를 사용하면 참조되고 있는 userprofile 내에 있는 hobby를 가져오게 할 수 있다.
이때, 일반적으로 역참조를 사용하게 되면 역참조 시 사용할 이름 뒤에 _set을 붙여야 한다.
하지만 Many-to-Many 관계로 지정되어 있는 object이거나, 모델에서 related_name을 설정해준다면 _set을 사용하지 않아도 된다.
그리고 정참조는 여태까지 모델을 불러와서 사용했던 것처럼 해당 모델에 있는 object를 가져와서 사용해주면 된다.
다음으로는 DRF에서 가장 중요하다고도 할 수 있는 serializer에 대해 알아보자.
Serializer
- django의 object, queryset 등 복잡한 데이터들을 json과 같은 다른 유형으로 쉽게 변환해주는 것
- serializer Meta class : model - 사용될 모델 테이블 / fields - 해당 모델 테이블에서 사용될 필드
우선 serializer를 사용하기 위해 해당 앱 폴더 내에 serializers.py 파일을 생성해주었다.
그리고 해당 모델들에 대한 serializer 설정을 해주었다.
# user/serializer.py
from rest_framework import serializers
from user.models import User
# 로그인한 사용자의 기본 정보
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ["username", "email", "fullname", "join_date"]
# user/views.py
from rest_framework.response import Response
from rest_framework import status
from user.serializers import UserSerializer
# 로그인 한 사용자 보여주기
def get(self, request):
user = request.user # 로그인한 유저 정보
return Response({"message": "로그인한 사용자 정보"}, UserSerializer(request.user).data, status=status.HTTP_200_OK)
해당 코드는 serializer 설정을 할 때 사용하는 기본적인 설정이다.
사용하고자 하는 모델과 필드들을 불러와서 model과 fields에 적어주면 serializer 설정을 해줄 수 있다.
그리고 views.py에서 해당 serializer class 이름을 import 해준 뒤 사용해주면 된다.
해당 코드에서 return으로 로그인된 사용자의 정보를 UserSerializer에 설정된 내용대로 반환해주면 해당 serializer 내에 있는 필드들인 username, email, fullname, join_date를 반환받을 수 있다.
이러한 기본적인 설정 외에도 외래키 관계에 있는 테이블이 있을 경우에는 해당 외래키 관계에 있는 테이블에 대해 serializer class를 만들어주고 사용해주면 된다.
# user/serializer.py
from rest_framework import serializers
from user.models import User
from user.models import UserProfile
# 로그인한 사용자의 상세 정보
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model = UserProfile
fields = "__all__" # 전체 필드 정보
# fields = ["introduction", "birthday", "age"]
# 로그인한 사용자의 기본 정보
class UserSerializer(serializers.ModelSerializer):
userprofile = UserProfileSerializer()
class Meta:
model = User
fields = ["username", "email", "fullname", "join_date", "userprofile"]
여기서 만약 직접 원하는 필드를 추가하고, 다른 serializer와 함께 사용을 하고자 한다면 다음과 같이 설정을 해주면 된다.
# user/serializer.py
from rest_framework import serializers
from user.models import User
from user.models import UserProfile
from user.models import Hobby
class HobbySerializer(serializers.ModelSerializer):
# serializers.SerializerMethodField()를 사용해 원하는 필드를 생성한다.
same_hobby_users = serializers.SerializerMethodField()
def get_same_hobby_users(self, obj):
user_list = []
for user_profile in obj.userprofile_set.all():
user_list.append(user_profile.user.username)
return user_list
class Meta:
model = Hobby
fields = ["name", "same_hobby_users"]
class UserProfileSerializer(serializers.ModelSerializer):
# 외래 키 관계로 이어져 있는 필드는 Serializer를 바로 호출할 수 있다.
hobby = HobbySerializer(many=True)
class Meta:
model = UserProfile
fields = "__all__"
class UserSerializer(serializers.ModelSerializer):
# One-to-one 관계에서는 fk처럼 사용 가능하다.
userprofile = UserProfileSerializer()
class Meta:
model = User
fields = ["username", "password", "fullname", "email", "userprofile"]
serializers.SerializerMethodField()를 사용하여 자신이 원하는 필드를 생성할 수 있다.
해당 기능을 사용하려면 해당 기능이 담긴 변수명에 대해 함수를 하나 만들어서 def get_변수명(slef, obj): 함수를 사용하여 추가할 내용을 적어주면 된다.
그리고 생성된 해당 필드에 대한 내용을 Meta 안에 있는 fields에 적어주면 사용할 수 있게 된다.
그리고 UserProfileSerializer 안에서 만들어진 hobby 필드를 사용하기 위해 해당 serializer를 불러올 때 many=Ture를 적어주는 것을 볼 수 있다.
이것은 해당 테이블에 있는 여러 개의 값을 리스트 형태로 가져오기 위해 사용하는 것이다.
다음은 permission_classes에 대해 알아보도록 하자.
DRF에서 permission_classes를 사용하려면 from rest_framework import permissions을 임포트 해준 뒤 사용해주면 된다.
여기서 기본적으로 제공하는 permission에는 다음과 같은 것들이 있다.
# views.py
from rest_framework.views import APIView
from rest_framework import permissions
class View(APIView):
permission_classes = [permissions.AllowAny] # 누구나 view 조회 가능
permission_classes = [permissions.IsAdminUser] # admin만 view 조회 가능
permission_classes = [permissions.IsAuthenticated] # 로그인 된 사용자만 view 조회 가능
해당 내용은 주석을 보면 알 수 있다.
AllowAny는 조회 시 누구나 view를 조회할 수 있도록 해 주고, IsAdminUser는 관리자(admin)만 view를 조회할 수 있도록 해주며, IsAuthenticated는 로그인된 사용자(user)만 view를 조회할 수 있도록 해준다.
여기서 자신이 직접 permission_classes를 만들고 싶다면 다음과 같이 해주면 된다.
우선 장고 프로젝트 파일 내에 permissions.py 파일을 만들어주었다.

생성된 permissions.py 안에 관리자(admin)는 모든 권한이 있고 인증된 사용자는 조회만 가능하도록 만들어주는 permission을 직접 커스텀하여 만들어주도록 하였다.
# drf_project/permissions.py
from rest_framework.permissions import BasePermission
from rest_framework.exceptions import APIException
from rest_framework import status
class GenericAPIException(APIException):
def __init__(self, status_code, detail=None, code=None):
self.status_code=status_code
super().__init__(detail=detail, code=code)
class IsAdminOrIsAuthenticatedReadOnly(BasePermission):
# admin 사용자는 모두 가능, 로그인 사용자는 조회만 가능
SAFE_METHODS = ('GET', )
message = '접근 권한이 없습니다.'
def has_permission(self, request, view):
user = request.user
if not user.is_authenticated:
response = {"detail": "서비스를 이용하기 위해 로그인 해주세요.",}
raise GenericAPIException(status_code=status.HTTP_401_UNAUTHORIZED, detail=response)
if user.is_authenticated and user.is_admin:
return True
elif user.is_authenticated and request.method in self.SAFE_METHODS:
return True
return False
해당 permission을 커스텀하여 만들어줄 경우 def has_permission(self, request, view):를 사용하여 모든 요청(GET, POST, PUT, DELETE)에 대해 호출이 가능하도록 설정을 해주어야 한다.
해당 로그인된 사용자가 인증이 되지 않았다면 detail 메시지와 함께 401 에러를 발생하도록 설정해주었다.
그리고 인증된 사용자에 대한 조건으로 인증된 사용자가 관리자(admin)라면 True를, 일반 유저라면 요청(GET)에 의한 조건으로 True를 둘 다 아니라면 False를 반환해주도록 하였다.
-
오늘은 정참조와 역참조, serializer, permission에 대해 공부했다.
다소 이해가 되지 않는 부분이 많고 어려웠지만 강의 내용을 계속 확인하면서 리마인드 했고, 팀장님의 도움을 통해 공부하면서 조금씩 이해하고자 노력했다.
그래도 아직은 어려운감이 있는 것 같다.
그리고 마지막 permission 설정 부분에서 오류가 발생했는데, 이 오류에 대해 확인해보다가 해결하지 못하는 부분이 있어서 해결해봐야겠다.
:T
'TIL 및 WIL > TIL (Today I Learned)' 카테고리의 다른 글
| [TIL] 2022.06.22 (Django 심화, DRF 5) (0) | 2022.06.23 |
|---|---|
| [TIL] 2022.06.21 (Django 심화, DRF 4) (0) | 2022.06.21 |
| [TIL] 2022.06.16 (Django 심화, DRF 2) (0) | 2022.06.16 |
| [TIL] 2022.06.15 (Django 심화, DRF 1) (0) | 2022.06.15 |
| [TIL] 2022.06.14 (Django 추천 시스템 팀 프로젝트(끝)) (0) | 2022.06.14 |