역참조
: 외래 키를 사용해 참조하는 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 |