내일배움캠프 - DRF 3일차 개발일지

Dongwoo Kim·2022년 6월 19일
0

내일배움캠프 - DRF

목록 보기
4/12

스파르타 코딩클럽

내일배움캠프 AI 웹개발자양성과정 2회차

DRF 3일차 개발일지

0. 강의요약

  • 역참조
  • serializer
  • permission_classes

1. 역참조

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

  • 정참조 : object가 가진 외래 키의 object를 참조
  • 역참조 : 외래 키를 가진 object를 역으로 참조

1) OneToOneField

: 정참조와 같은 방식으로 사용가능

# models.py
class User(AbstractBaseUser):
    ...

class UserProfile(models.Model):
    user = models.OneToOneField(to=User)
    hobby = models.ManyToManyField("Hobby", verbose_name="취미")
    ...
    
# view.py
def get(self, request):
		user = request.user
		user_profile = user.userprofile # 역참조
        user2 = user_profile.user # 정참조, user == user2

2) ForiegnKey, ManyToManyField

: _set을 붙여 사용가능

relation_name을 설정했다면 relation_name으로 참조해야함

# models.py
class UserProfile(models.Model):
    user = models.OneToOneField(to=User)
    hobby = models.ManyToManyField("Hobby", verbose_name="취미")
    ...

class Hobby(models.Model):
    ...
  
# views.py
def get(self, request):
		user = request.user
		hobbys = user.userprofile.hobby.all() # 정참조
		for hobby in hobbys:
		    # exclde : 매칭 된 쿼리만 제외, filter와 반대
		    # annotate : 필드 이름을 변경해주기 위해 사용, 이외에도 원하는 필드를 추가하는 등 다양하게 활용 가능
		    # values / values_list : 지정한 필드만 리턴 할 수 있음. values는 dict로 return, values_list는 tuple로 ruturn
		    # F() : 객체에 해당되는 쿼리를 생성함
		    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}")

# result print
"""
hobby : 산책 / hobby members : ['user1']
hobby : 음악감상 / hobby members : ['user1', 'user2']
hobby : 스쿠버다이빙 / hobby members : ['user2']
hobby : 여행 / hobby members : ['user2']
"""

2. Serializer

: django의 object, queryset 인스턴스 등 복잡한 테이터들을 json같은 다른 콘텐츠 유형으로 쉽게 변환 할 수 있다.

Serializers allow complex data such as querysets and model instances to be converted to native Python datatypes that can then be easily rendered into JSON, XML or other content types.

https://www.django-rest-framework.org/api-guide/serializers/

1) 기본 설정

# serializer.py (사용할 앱 디렉토리 안에 생성)
from rest_framework import serializers

class UserSerializer(serializers.ModelSerializer):
   class Meta:
        # serializer에 사용될 model, field지정
        model = User
        # 모든 필드를 사용하고 싶을 경우 fields = "__all__"로 사용
        fields = ["username", "password", "fullname", "email"]

# 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
    # serializer에 queryset을 인자로 줄 경우 many=True 옵션을 사용해야 한다.
    serialized_user_data = UserSerializer(user).data
    return Response(serialized_user_data, status=status.HTTP_200_OK)

# return data
"""
{
    "username": "user",
    "password": "pbkdf2_sha256$320000$u5YnmKo9luab9csqWpzRsa$pKfqHnBiF5Rgdo1Mj9nxNOdhpAl9AhPVXFPXkbPz7Mg=",
    "fullname": "user's name",
    "email": "user@email.com"
}
"""

2) 외래 키

: 외래 키 관계에 있는 테이블이 있을 경우, 해당 테이블의 serializer를 생성해 함께 사용할 수 있다.

# serializer.py
from .models import User, UserProfile

class UserProfileSerializer(serializers.ModelSerializer):
   class Meta:
        model = UserProfile
        fields = "__all__"

class UserSerializer(serializers.ModelSerializer):
   userprofile = UserProfileSerializer()
   class Meta:
        # serializer에 사용될 model, field지정
        model = User
        # 모든 필드를 사용하고 싶을 경우 fields = "__all__"로 사용
        fields = ["username", "fullname", "email", "userprofile"]
        
# view.py
class UserView(APIView):
    permission_classes = [permissions.AllowAny]

    def get(self, request):
        all_users = User.objects.all()
        print(type(UserSerializer(all_users, many=True).data))

        return Response(UserSerializer(all_users, many=True).data)
        
# response reusult
[
    {
        "username": "admin",
        "fullname": "",
        "email": "",
        "userprofile": {
            "id": 1,
            "intro": "admin's profile",
            "age": 29,
            "interest": "게임",
            "user": 1
        }
    },
    {
        "username": "dongwoo",
        "fullname": "김동우",
        "email": "esdx@daum.net",
        "userprofile": {
            "id": 2,
            "intro": "dongwoo's profile",
            "age": 29,
            "interest": "movie",
            "user": 2
        }
    }
]

3) SerializerMethodField

: 원하는 필드를 추가할 수 있다.

# models.py
class Hobby(models.Model):
    hobby = models.CharField(max_length=10)

    def __str__(self):
        return f"{self.hobby}"

class UserProfile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    intro = models.TextField()
    age = models.IntegerField()
    hobby = models.ManyToManyField(Hobby)

    def __str__(self):
        return f"{self.user.username}'s profile"
        
# serializer.py
class HobbySerializer(serializers.ModelSerializer):
   same_hobby_people = serializers.SerializerMethodField()
   # obj : hobby 객체
   def get_same_hobby_people(self, obj):
      people = obj.userprofile_set.all()
      name_list = [ person.user.username for person in people ]
      return name_list

   class Meta:
      model = Hobby
      fields = ['hobby', 'same_hobby_people']

class UserProfileSerializer(serializers.ModelSerializer):
   hobby = HobbySerializer(many=True)
   class Meta:
        model = UserProfile
        fields = ['age', 'hobby']

class UserSerializer(serializers.ModelSerializer):
   userprofile = UserProfileSerializer()
   class Meta:
        # serializer에 사용될 model, field지정
        model = User
        # 모든 필드를 사용하고 싶을 경우 fields = "__all__"로 사용
        fields = ["username", "fullname", "email", "userprofile"]
        
# response result
[
    {
        "username": "admin",
        "fullname": "",
        "email": "",
        "userprofile": {
            "age": 29,
            "hobby": [
                {
                    "hobby": "게임",
                    "same_hobby_people": [
                        "dongwoo",
                        "admin"
                    ]
                },
                {
                    "hobby": "코딩",
                    "same_hobby_people": [
                        "dongwoo",
                        "admin",
                        "soojin"
                    ]
                }
            ]
        }
    },
    {
        "username": "dongwoo",
        "fullname": "김동우",
        "email": "esdx@daum.net",
        "userprofile": {
            "age": 29,
            "hobby": [
                {
                    "hobby": "크로스핏",
                    "same_hobby_people": [
                        "dongwoo"
                    ]
                },
                {
                    "hobby": "게임",
                    "same_hobby_people": [
                        "dongwoo",
                        "admin"
                    ]
                },
                {
                    "hobby": "코딩",
                    "same_hobby_people": [
                        "dongwoo",
                        "admin",
                        "soojin"
                    ]
                }
            ]
        }
    },
    {
        "username": "soojin",
        "fullname": "수진",
        "email": "esdx@naver.com",
        "userprofile": {
            "age": 29,
            "hobby": [
                {
                    "hobby": "코딩",
                    "same_hobby_people": [
                        "dongwoo",
                        "admin",
                        "soojin"
                    ]
                }
            ]
        }
    }
]

3. permission_classes

: views.py의 접근 권한을 설정할 수 있다.

기본적으로 class단위로 설정 가능

1) 기본 permissions_classes

  • permissions.AllowAny : 모든 사용자를 대상으로 접근 허용
  • permissions.IsAuthenticated : 로그인 된 사용자를 대상으로 접근 허용

2) 커스텀 permissions_classes

# permissions.py
from rest_framework.permissions import BasePermission
from datetime import timedelta
from django.utils import timezone

class RegistedMoreThanAWeekUser(BasePermission):
    """
    가입일 기준 1주일 이상 지난 사용자만 접근 가능
    """
    message = '가입 후 1주일 이상 지난 사용자만 사용하실 수 있습니다.'
    
    def has_permission(self, request, view):
        return bool(request.user and request.user.join_date < (timezone.now() - timedelta(days=7)))
        
# views.py
from .permissions import RegistedMoreThanAWeekUser

# 유저 뷰 기능
class UserView(APIView):
    permission_classes = [RegistedMoreThanAWeekUser]

    def get(self, request):
        all_users = User.objects.all()
        return Response(UserSerializer(all_users, many=True).data)
        
# result
{
    "detail": "가입 후 1주일 이상 지난 사용자만 사용하실 수 있습니다."
}

스터디영상

https://youtu.be/ZryEmr5gaNM

profile
kimphysicsman

0개의 댓글