AI 웹 개발 과정/DRF 특강

DRF 특강 | 3일차

만 기 2022. 6. 17. 22:28

 

역참조

: 외래 키를 사용해 참조하는 object를 역으로 찾을 수 있다.

 

  • 왜래 키 지정 시 related_name 옵션을 사용해 역참조 시 사용될 이름을 지정할 수 있다.
  • releated_name을 지정하지 않는다면 기본적으로 tablename_set 형태로 지정된다. 
  • onetoone 은 예외로 _set 이 붙지 않는다. 
  • 소문자로 참조 : UserProfile -> userprofile 또는 userprofile_set
  •  - ex) `hobby.userprofile_set` → hobby를 참조하고 있는 UserProfile 테이블의 object를 져옴
     - models.py에서 releated_name을 `user_hobby` 로 지정했다면 `hobby.user_hobby`와 같이 사용

userprofile은 user 를 참조 (원투원)

userprofile은 hobby를 참조 (매니투매니)

=> hobby 종류마다 어떤 user들이 있는지 역참조

 

예시

class UserView(APIView):
    # permission_classes = [permissions.AllowAny] # 모두 허용
    permission_classes = [permissions.IsAuthenticated] # 로그인된 사용자만
    # permission_classes = [permissions.IsAdminUser] # 관리자만
    # 사용자 정보 조회
    def get(self, request):
        user = request.user
        
        # 역참조를 사용했을때
        # OneToOne 필드는 예외로 _set이 붙지 않는다.
        hobbys = user.userprofile.hobby.all()
        # hobbys = str(hobbys)

        # 역참조를 사용하지 않았을때
        # user_profile = UserProfile.objects.get(user=user)
        # hobbys = user_profile.hobby.all()

        # for hobby in hobbys:
		    # exclde : 매칭 된 쿼리만 제외, filter와 반대
		    # annotate : 필드 이름을 변경해주기 위해 사용, 이외에도 원하는 필드를 추가하는 등 다양하게 활용 가능
		    # values / values_list : 지정한 필드만 리턴 할 수 있음. values는 dict로 return, values_list는 tuple로 ruturn
		    # F() : 객체에 해당되는 쿼리를 생성함. (스트링을 쿼리로 바꿔준다)
            # user__username : user 프로필 안에 있는 username
            hobby_members = hobby.userprofile_set.exclude(user=user).annotate(username=F('user__username')).values_list('username', flat=True)
            hobby_members = list(hobby_members)
            print(f"hobby : {hobby.name} / hobby members : {hobby_members}")

 

 

 

Serializers

  • django의 object, queryset 인스턴스 등 복잡한 테이터들을 json같은 다른 콘텐츠 유형으로 쉽게 변환 할 수 있다.
  • serializer Meta class
    • serializer에서 사용되는 설정 파일이다.
    • model에 사용 될 테이블을 적어주고, fields에 사용될 필드를 적어준다.

 

serializers.py 파일 생성

 

- user/serializers.py

# serializers 임포트
from rest_framework import serializers

# 사용할 모델 임포트
from user.models import User as UserModel
from user.models import UserProfile as UserProfileModel
from user.models import Hobby as HobbyModel
 
class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = UserModel
        fields = '__all__'  # 모든필드 가져온다
        # fields = ["username","email","join_date"] # 필요한 필드만 가져온다

 

user/views.py

# user/serializers.py 임포트
from user.serializers import UserSerializer	

class UserView(APIView):
    permission_classes = [permissions.IsAuthenticated]
    def get(self, request):
        user = request.user
        return Response(UserSerializer(user).data)

 

OneToOne 관계 모델 Serializers

class UserProfileSerializer(serializers.ModelSerializer):
    class Meta:
        model = UserProfileModel
        fields = ["age","birthday","hobby"]

class UserSerializer(serializers.ModelSerializer):
    userprofile = UserProfileSerializer()	# OneToOne 관계에서는 object 로 가져온다.
    class Meta:
        model = UserModel
        # fields = '__all__'  # 모든필드 가져온다
        fields = ["username","email","join_date","userprofile"]

 

 

ManyToMany 관계 모델 Serializers

class HobbySerializer(serializers.ModelSerializer):
    class Meta:
        model = HobbyModel
        fields = ["name"]

class UserProfileSerializer(serializers.ModelSerializer):
    # 매니투매니 관계이므로 쿼리셋으로 return  # input data 쿼리셋이면 many=True
    hobby = HobbySerializer(many=True)   
    class Meta:
        model = UserProfileModel
        fields = ["age","birthday","hobby"]

 

 

SerializerMethodField()

원하는 변수를 만들어서 fields 에 추가할 수 있다.

same_hobby_users 변수에 serializer.SerializerMethodField 적용

함수 def 에 변수 이름앞에 get_ 을 붙여 정의한다.

obj (HobbyModel)를 이용해서 외래키 역참조 할 수 있다.

해당 hobby를 선택한 user 보기

class HobbySerializer(serializers.ModelSerializer):
    same_hobby_users = serializers.SerializerMethodField()
    def get_same_hobby_users(self,obj):
        # obj : hobby model 의 object
        
        # user_list = []
        # for user_profile in obj.userprofile_set.all():
        #     user_list.append(user_profile.user.username)

        # return user_list

        # for 축약
        return [user_profile.user.username for user_profile in obj.userprofile_set.all()]

    class Meta:
        model = HobbyModel
        fields = ["name","same_hobby_users"]
        # fields = ["name", "userprofile_set"]  # 역참조 이름 바로 사용 가능.

 

 

 

custom permission

메인프로젝트 폴더에 permissions.py 생성 

DRF_day2/permission.py

from rest_framework.permissions import BasePermission
from datetime import datetime, timedelta
from django.utils import timezone

# join_date 가 DatetimeField 일때는 timezone을 사용한다
# (datatime)user.join_data > datetime.now() => X
# (datatime)user.join_data > timezone.now() => O

class RegistedMoreThanAWeekUser(BasePermission):
    def has_permission(self, request, view):
        user = request.user
        if not user or not user.is_authenticated:
            return False

        return bool(user.join_date < (datetime.now().date() - timedelta(days=7)))

join_date 가 DateTimeField 일때, timezone 과 timedelta 임포트 하고

bool(user.join_date < (datetime.now().date() - timedelta(days=7))) 

현재 날짜에서 7일을 뺀 날 보다 가입일(join_date)이 더 작으면(이전이면) True 반환.

 

 

user/views.py

from DRF_day2.permissions import RegistedMoreThanAWeekUser

# Create your views here.
class UserView(APIView):
    # permission_classes = [permissions.AllowAny] # 모두 허용
    # permission_classes = [permissions.IsAuthenticated] # 로그인된 사용자만
    # permission_classes = [permissions.IsAdminUser] # 관리자만
    permission_classes = [RegistedMoreThanAWeekUser]

permissions.py 파일의 RegistedMoreThanAWeekUser class 임포트

permission_classes = [RegistedMoreThanAWeekUser] 로 권한 설정

 

 

 

admin은 모든 권한이 있고, 인증 된 사용자는 조회만 가능하도록 설정하기 (method 별로 권한 설정)

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:     # 로그인 + SAFE_METHODS
            return True
        
        return False

 

 

 

'AI 웹 개발 과정 > DRF 특강' 카테고리의 다른 글

DRF 복습 퀴즈  (2) 2022.06.23
DRF 특강 | 5일차  (0) 2022.06.21
DRF 특강 | 4일차  (0) 2022.06.20
DRF 특강 | 2일차  (2) 2022.06.16
DRF 특강 | 1일차  (0) 2022.06.15