Study/django

[udemy] Build REST APIs with Django REST Framework and Python 학습

bluebamus 2023. 10. 3.

* 이미 알고있는 지식 외, 필요한 부분만 요약 정리함

 

1. Validation

   - serializers에서의 validation은 3가지 방법이 있다.

      1) 하나의 필드를 대상으로 한 내장 함수

         - validate_***

class MovieSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    name = serializers.CharField()
    
    def validate_name(self, value):
        if len(value) < 2:
            raise serializers.ValidationError("Name is too short!")
        else:
            return value

      2) 전체 필드를 대상으로 한 내장 함수

         - validate

class MovieSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    name = serializers.CharField()
    description = serializers.CharField()
    
    def validate(self, data):
        id = data["id"]
        name = data["name"]
        description = data["description"]
        if data["name"] == data["description"]:
            raise serializers.ValidationError(
                "Title and Description should be different!"
            )
        else:
            return data

      3) 필드 자체에 validators 속성 추가

def name_length(value):
    if len(value) < 2:
        raise serializers.ValidationError("Name is too short!")


class MovieSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    name = serializers.CharField(validators=[name_length])

 

 * 필드 자체에 validators 혹은 에러 메시지를 넣고자 한다면, form이나 serializers의 필드보다 model에 넣는게 적합하다.

 

* 모델 필드에 에러 메시지 넣는 방법

   - field_name = models.Field(error_messages = {"key": "message"})

class GeeksModel(Model):
    geeks_field = models.CharField(
                    max_length = 200, 
                    unique = True
                    )

- reference :

https://programmers-sosin.tistory.com/entry/Django-Validator-%EC%9E%A5%EA%B3%A0-%EC%9C%A0%ED%9A%A8%EC%84%B1-%EA%B2%80%EC%A6%9D-validator-%EC%B6%94%EA%B0%80%ED%95%98%EA%B8%B0

 

Django Validator / 장고 유효성 검증 / validator 추가하기

Django 유효성 검증 장고에서 유효성을 검증하는 방법은 2가지가 있습니다. 1. Field를 정의할 때 필요한 옵션 인자로 주기 2. 따로 validator를 추가하기 validator를 따로 추가하여 유효성 검증하기 Valida

programmers-sosin.tistory.com

https://docs.djangoproject.com/en/4.2/ref/validators/

 

Django

The web framework for perfectionists with deadlines.

docs.djangoproject.com

 

2. SerializerMethodField

 - 읽기 전용 필드로 모델에 벗는 필드를 추가하고 싶거나, 모델에 있는 값을 변형, 특정 함수 수행 후의 결과 값을 새로운 필드의 값으로 넣고 싶을 때 사용한다.

Class UserSerializer(serializers.Serializer):
    full_name = JSONField
    first_name = serializers.SerializerMethodField('get_first_name')
    # first_name = serializers.SerializerMethodField(method_name = 'get_first_name')
	
    def get_first_name(self, obj): # 객체를 인자로 받음
    	return obj.full_name['first_name'] # 유저 객체의 full_name 속성에서 first_name 추출

 

- reference : 

https://leffept.tistory.com/319

 

[Django]DRF SerializerMethodField() 란?

SerializerMethodFiled() 란? 연결되어 있는 serializer 클래스에서 메서드를 호출하여 값을 가져올 수 있는 읽기 전용 필드이다. 객체의 serializer 된 표현에 모든 종류의 데이터를 추가하는데 사용할 수 있

leffept.tistory.com

https://velog.io/@oen/SerializerMethodField-%EA%B3%B5%EC%8B%9D%EB%AC%B8%EC%84%9C-%EB%B2%88%EC%97%AD

 

[Django] SerializerMethodField로 모델 필드 값을 변형해서 새로운 필드로 반환하기

이렇게 Video 모델에는 person이라는 필드만 있는데프론트팀에서 위와 같이객체의'original' 필드 값 중 'url' 키에 해당하는 값('a/b/c') 만 따로 name 이라는 필드에 추가해서 API를 보내달라는 요청이 했

velog.io

https://eunjin3786.tistory.com/268

 

[DRF] SerializerMethodField로 모델에서 변형된 JSON을 내려주기

[DRF] 모델과 ModelSerializer 만들기 에서 모델을 JSON으로 쉽게 바꿀 수 있도록 해주는 ModelSerializer를 알아봤는데요, 만약- 모델에 없는 필드인데 JSON에 특정 필드를 추가해서 내려주고 싶거나 - 모델

eunjin3786.tistory.com

 

3. Nested relationships

 - 다른 곳에 정의된 시리얼라이저를 필드로 불러올 수 있다.

   - 필드가 다대다 관계인 경우 필드에 Many=True 플래그를 추가해야 한다.

   - 필드 명은 모델에서 정의된 related_name 혹은 외래키 필드와 체이닝을 사용할 수 있다.

      - xxx_set 형식의 참조명을 사용할 수 있는지 여부는 확인이 필요함

class WatchListSerializer(serializers.ModelSerializer):
    # reviews = ReviewSerializer(many=True, read_only=True)
    platform = serializers.CharField(source="platform.name")

    class Meta:
        model = WatchList
        fields = "__all__"


class StreamPlatformSerializer(serializers.ModelSerializer):
    watchlist = WatchListSerializer(many=True, read_only=True)

    class Meta:
        model = StreamPlatform
        fields = "__all__"

 - serializers.StringRelatedField(many=True)

   - 모델의 def __str__(self):에 선언된 필드로 출력된다.

    def __str__(self):
        return self.title

 - serializers.PrimaryKeyRelatedField(many=True, read_only=True)

   - pk 숫자로 출력된다.

 - serializers.HyperlinkedRelatedField(many=True,read_only=True,view_name="movie-details",)

   - 각 컨텐츠와 연관된 하이퍼링크가 출력된다. 

   - view_name에는 urls에 선언된 해당 url의 name을 입력한다.

   - view에서 해당 시리얼라이즈를 호출할 때 context에 request를 넣어줘야 한다.

   - views

serializer = StreamPlatformSerializer(
            platform, many=True, context={"request": request}
        )

   - serializers

watchlist = serializers.HyperlinkedRelatedField(
        many=True,
        read_only=True,
        view_name="movie-details",
    )

   - urls

path("<int:pk>/", WatchDetailAV.as_view(), name="movie-details"),

 - 여러개의 Nested relationships field를 사용하고 싶다면 해당 필드의  플래그에 source를 정의한다 

   - source의 값은 모델에서 정의된 related_name 혹은 외래키 필드와 체이닝을 사용한다.

 

 - reference :

https://www.django-rest-framework.org/api-guide/relations/#nested-relationships

 

Serializer relations - Django REST framework

relations.py Data structures, not algorithms, are central to programming. — Rob Pike Relational fields are used to represent model relationships. They can be applied to ForeignKey, ManyToManyField and OneToOneField relationships, as well as to reverse re

www.django-rest-framework.org

https://naon.me/posts/til69

 

Django Rest Framework 활용하기 6 - Nested Relationships - out.log

Udemy 강의를 들으면서, DRF 공식문서를 보면서, 그리고 구글링하면서 정리한 내용입니다. Django REST Framework - Level One Nested Relationships ForeignKey를 이용해 두 테이블을 연결하는 방법을 알아보자. 장고

naon.me

 

4. HyperLinked Model Serializer

 - HyperlinkedModelSerializer 클래스는 하이퍼링크를 사용하여 기본 키가 아닌 관계를 나타내는 점을 제외하면 ModelSerializer 클래스와 유사하다

   - serializer

class AccountSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Account
        fields = ['url', 'id', 'account_name', 'users', 'created']

   - views

serializer = AccountSerializer(queryset, context={'request': request})

   - 기본적으로 자동으로 생성되는 url은 현재 시리얼라이저 class 명을 기반으로 추적된다. 

      - 예를들어 StreamPlatformSerializer 라는 클래스에서 정의 했다면 detail의 url에 접근할 이름을 자동으로 다음과 같이 만든다.       

streamplatform-detail

         - 이러한 문제를 해결하기 위해 명시적으로 url name을 선언할 수 있다. name 대신 주소를 직접 입력해도 될것이다.

class Meta:
        model = StreamPlatform
        fields = "__all__"

        extra_kwargs = {
            "url": {
                "view_name": "steam-detail"
            },  # Replace 'custom-model-detail' with your desired view name
        }

 

 - reference :

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

 

Serializers - Django REST framework

 

www.django-rest-framework.org

 

5. 테스트를 위한 django 자체 로그인 메뉴 활성화

 - 프로젝트 폴더의 최상위 urls.py에 다음 코드를 추가한다.

path('api-auth/', include('rest_framework.urls')),

 

6. permission

 - 전체 퍼미션은 settings.py에서 정의한다.

   - REST_FRAMEWORK 변수는 하나만 선언이 가능하며 내부 정의 요소들은 추가가 가능하다.

 

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ]
}

   - 함수형은 데코레이터로 정의한다.

@api_view(['GET'])
@permission_classes([IsAuthenticated])
def example_view(request, format=None):
    content = {
        'status': 'request was permitted'
    }
    return Response(content)

   - 클래스형은 클래스 변수로 정의한다.

class ExampleView(APIView):
    permission_classes = [IsAuthenticated|ReadOnly]

 

 - reference : 

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

 

Permissions - Django REST framework

 

www.django-rest-framework.org

 

 - custom permission

   - .has_permission(self, request, view) .has_object_permission(self, request, view, obj) 두 함수를 오버라이딩해서 구현할 수 있다.

   - has_permission(request, view)
      - APIView 접근시, 체크.
      - 거의 모든 Permission 클래스에서 구현되며 로직에 따라 True/False 반환
   - has_object_permission(request, view, obj)
      - APIView의 get_object 함수를 통해 object 획득 시에 체크.
      - 브라우저를 통한 API 접근에서 CREATE/UPDATE Form 노출 시 체크
   - DjangoObjectPermissions에서 구현하며 로직에 따라 True/False 반환

   - 기본 코드 라인은 다음과 같다.

if request.method in permissions.SAFE_METHODS:
    # Check permissions for read-only request
else:
    # Check permissions for write request

   - 구현 예시는 다음과 같다.

class IsAdminOrReadOnly(permissions.IsAdminUser):

    def has_permission(self, request, view):
        if request.method in permissions.SAFE_METHODS:
            return True
        else:
            return bool(request.user and request.user.is_staff)


class IsReviewUserOrReadOnly(permissions.BasePermission):

    def has_object_permission(self, request, view, obj):
        if request.method in permissions.SAFE_METHODS:
            return True
        else:
            return obj.review_user == request.user or request.user.is_staff

 

 - reference : 

https://www.django-rest-framework.org/api-guide/permissions/#isadminuser

 

Permissions - Django REST framework

 

www.django-rest-framework.org

https://donis-note.medium.com/django-rest-framework-authentication-permission-%EC%9D%B8%EC%A6%9D%EA%B3%BC-%EA%B6%8C%ED%95%9C-cc9b183fd901

 

[Django Rest Framework]Authentication & Permission (인증과 권한)

DRF Authentication

donis-note.medium.com

https://velog.io/@nameunzz/DRF-permissions

 

DRF - permissions

AllowAny: 인증/비인증 모두 허용 (default)IsAuthenticated: 인증된 요청에 대해서만 view 호출IsAdminUser: Staff User에 대해서만 요청 허용 (User.is_staff가 True여야 함)IsAuthenticatedOrRea

velog.io

 

7.  ID / Password 기반 인증 (authentication)

 - 기본 인증 (BasicAuthentication)

 - settings.py에 REST_FRAMEWORK 변수에 정의할 수 있다.

REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": [
        "rest_framework.authentication.BasicAuthentication",
    ]
}

 - 예시 코드는 다음과 같다.

class ExampleView(APIView):
    authentication_classes = [SessionAuthentication, BasicAuthentication]
    permission_classes = [IsAuthenticated]

    def get(self, request, format=None):
        content = {
            'user': str(request.user),  # `django.contrib.auth.User` instance.
            'auth': str(request.auth),  # None
        }
        return Response(content)

 - 기본 인증은 id와 password로 이루어 진다. 

   - 방법은 headers에 key로 Authorization, value에 Basic id:password로 구성하면 되는데 id:password 부분은 base64로 인코딩 되어야 한다. 다른 툴이나 사이트를 이용해서 테스트 할 수 있다.

 

8. 기본 토큰 기반 인증 (Token authentication)

 - REST_FRAMEWORK 변수의 DEFAULT_AUTHENTICATION_CLASSES 항목을 변경한다.

REST_FRAMEWORK = {
    # "DEFAULT_AUTHENTICATION_CLASSES": [
    #     "rest_framework.authentication.BasicAuthentication",
    # ],
    "DEFAULT_AUTHENTICATION_CLASSES": [
        "rest_framework.authentication.TokenAuthentication",
    ]
}

   - INSTALLED_APPS 항목에 app을 추가한다.

INSTALLED_APPS = [
    ...
    'rest_framework.authtoken'
]

   - python manage.py migrate 를 실행시킨다.

   - postman의 설정을 다음과 같이 정의한다.

 - Token 생성 방법

   1) ObtainAuthToken 뷰를 통한 획득 및 생성 

      - 이미 rest_framework 패키지에 만들어진 ObtainAuthToken 클래스를 사용한다.

         - URL Pattern에 매핑 필요

      - 코드 원형

# rest_framework/authtoken/views.py
class ObtainAuthToken(APIView):
   def post(self, request, *args, **kwargs):
       # ...
       token, created = Token.objects.get_or_create(user=user)
       return Response({'token':token.key})

    2) Signal을 통한 자동 생성

      - created나 updated 둘다 save를 호출하기 때문에 created=False를 기본으로 두고 True일 경우에만 토큰 생성

from django.conf import settings
from django.db.models.signals import post_save
from django.dispatch imort receiver
from rest_framework.authtoken.models import Token
@receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_auth_token(sender,instance=None,created=False,**kwargs):
    if created:
       Token.objects.create(user=instance)

    3) Management 명령을 토한 생성

# 생성된 Token을 변경하지는 않음. 필수X
python3 manage.py drf_create_token <username>
# 강제로 재생성
python3 manage.py drf_create_token -r <username>

   4) admin 페이지에서 생성

   - postman에서 테스트 하기

   - 효율적인 save() 오버라이딩 코드 예시

class RegistrationSerializer(serializers.ModelSerializer):
    password2 = serializers.CharField(style={"input_type": "password"}, write_only=True)

    class Meta:
        model = User
        fields = ["username", "email", "password", "password2"]
        extra_kwargs = {"password": {"write_only": True}}

    def save(self):
        password = self.validated_data["password"]
        password2 = self.validated_data["password2"]

        if password != password2:
            raise serializers.ValidationError({"error": "P1 and P2 should be same!"})

        if User.objects.filter(email=self.validated_data["email"]).exists():
            raise serializers.ValidationError({"error": "Email already exists!"})

        account = User(
            email=self.validated_data["email"], username=self.validated_data["username"]
        )
        account.set_password(password)
        account.save()
        # account = User.objects.create_user(
        #     email=self.validated_data["email"],
        #     username=self.validated_data["username"],
        #     password=self.validated_data["password"],
        # )
        print("account : ", account)
        return account

 

 - 일반 code 방법으로 Token 생성 : View

   - 생성 예제

from rest_framework.authtoken.models import Token

token = Token.objects.create(user=...)
print(token.key)

   - 가져오기 예제

token = Token.objects.get(user=account).key

 

 

 - reference : 

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

 

Authentication - Django REST framework

 

www.django-rest-framework.org

https://donis-note.medium.com/django-rest-framework-token-2618d914f018

 

[Django Rest Framework] Token

DRF Token

donis-note.medium.com

https://velog.io/@duo22088/DRF-Token-Authentication

 

(DRF) Token Authentication

DRF에서 지원하는 Token Authentication 을 사용해봅니다.

velog.io

https://velog.io/@joje/Token%EC%9D%B8%EC%A6%9D-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0

 

Token인증 적용하기

DRF에서 지원하는 기본 인증을 활용해본다.

velog.io

https://ssungkang.tistory.com/entry/Django-Token-%EC%9D%B8%EC%A6%9D-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0-TokenAuthentication

 

[Django] Token 인증 적용하기, TokenAuthentication

DRF 에서 지원하는 인증은 다음과 같습니다. rest_framework.authentication.SessionAuthentication rest_framework.authentication.BasicAuthentication rest_framework.authentication.TokenAuthentication 초기에 username/password 으로 Token 발

ssungkang.tistory.com

 

9.  JWT Authentication

 - 여러 패키지들이 있어며 pyjwt로 직접 구현하는 방법도 있다 ( reference 참조)

 - simple jwt를 사용한다. (https://django-rest-framework-simplejwt.readthedocs.io/en/latest/)

 - 공식 사이트에 가면, 여러 url과 제공되는 기능들을 확인할 수 있다.

 - 해당 패키지를 사용하면, DB에 토큰을 저장하지 않고 토큰에 관련한 정보를 담아 전송하여 인증에 사용한다.

 - access token, refresh token 두 개의 토큰이 발행되며 각 토큰의 제한시간은 access token은 5분, refresh token은 24시간이다.

 

 - api 설정

path("api/token/", TokenObtainPairView.as_view(), name="token_obtain_pair"),
path("api/token/refresh/", TokenRefreshView.as_view(), name="token_refresh"),

 - postman 테스트

 - 결과

 - 토큰 사용 방법

   - Headers탭을 누르고 Key에 Authorization, Value에 Bearer {access token} 이렇게 입력한다.

 - 만료된 access token을 refresh token을 이용해 재발급 받기

   - body 탭을 누르고 x-www-form-urlencoded를 선택한다. key에는 refresh, value에는 refresh token을 입력한다.

 - 주요 settings 설정

   - ACCESS_TOKEN_LIFETIME
      - access token이 유효한 기간을 지정하는 datetime.timedelta 객체

   -  REFRESH_TOKEN_LIFETIME
      -  refresh token이 유효한 기간을 지정하는 datetime.timedelta 객체

   -  ROTATE_REFRESH_TOKENS
      -  True로 설정할 경우, refresh token을 보내면 새로운 access token과 refresh token이 반환된다. 

   -  BLACKLIST_AFTER_ROTATION
      -  True로 설정될 경우, 기존에 있던 refresh token은 blacklist가된다.

 

 - 코드를 통해 simplejwt의 refresh token과 access token을 추출하는 방법

   - 참조 : https://django-rest-framework-simplejwt.readthedocs.io/en/latest/creating_tokens_manually.html

from rest_framework_simplejwt.tokens import RefreshToken

def registration_view(request):
   .
   refresh = RefreshToken.for_user(account)
            data['token'] = {
                                'refresh': str(refresh),
                                'access': str(refresh.access_token),
                            }
    return Response(data, status=status.HTTP_201_CREATED)

 

 - reference : 

https://django-rest-framework-simplejwt.readthedocs.io/en/latest/

 

Simple JWT — Simple JWT 5.2.2.post30+gfaf92e8 documentation

Simple JWT provides a JSON Web Token authentication backend for the Django REST Framework. It aims to cover the most common use cases of JWTs by offering a conservative set of default features. It also aims to be easily extensible in case a desired feature

django-rest-framework-simplejwt.readthedocs.io

https://velog.io/@duo22088/DRF-JWT-인증

 

 

 

 

(DRF) JWT 인증

SimpeJWT 를 이용해보자.

velog.io

https://gaussian37.github.io/python-rest-JWT-Authorization/

 

DRF에서 JWT(JSON Web Token) 사용하는 방법

gaussian37's blog

gaussian37.github.io

   - 토큰 취약점

https://velog.io/@thelm3716/JWTvul

 

JWT (JSON Web Token) 취약점 알아보기

JWT(JSON Web Token)의 취약점을 알아봅니다.

velog.io

https://hyotwo.tistory.com/162

 

[웹] JWT(JSON Web Token)에 대한 다양한 공격 기법 - 1

JWT란 JSON 웹 토큰(Web Token)의 약자이며 암호화된 토큰값으로 JSON 데이터를 주고 받을 때 사용한다. 모든 JSON 데이터를 다 주고 받고 할 수 있지만, 일반적으로 JWT를 사용하는 웹 서버를 보면 보통

hyotwo.tistory.com

https://liebe97.tistory.com/20

 

JWT 정리 2 - JWT 사용 시 고려해야 할 사항

JWT 이 보안의 완벽한 해결책은 아닙니다 Payload 정보 JWT 의 payload 정보는 누구나 쉽게 디코딩할 수 있기 때문에 사용자의 아이디나 비밀번호 등 개인 정보를 담아 인코딩한다면 정보가 유출될 우

liebe97.tistory.com

 - pyjwt

https://seongonion.tistory.com/112

 

[Django] DRF에서 JWT 사용하기 - PyJWT 이용

DRF를 통해 프로젝트 진행 중 JWT를 발급해야 할 상황이 생겼다. 토이 프로젝트이고 돈도 없다보니 데이터베이스를 AWS MySQL 프리티어로 사용하고 있는데, 아무래도 이게 성능도 안좋고 느리다보니

seongonion.tistory.com

https://juneyr.dev/2018-01-28/making-token-pyjwt

 

토큰으로 토큰을 만들자 - pyjwt 사용기

json web token 너 부르기 좀 껄끄럽다

juneyr.dev

https://velog.io/@2cong/bcrypt%EC%99%80-PyJWT

 

bcrypt와 PyJWT

bcrypt를 이용하여 password 암호화 하기와 PyJWT를 이용하여 토큰 발행하기 이 글을 보기 전에 인증과 인가부터 보고오기 콘다 생성 pip install bcrypt python 인터프리터 실행 import bcrypt bcrypt에서 암호화

velog.io

https://velog.io/@masterkorea01/Django-JWT-%EC%9D%B8%EC%A6%9D-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%A7%81%EC%A0%91-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0

 

Django JWT 인증 시스템 직접 구현하기

왜 WHY? > 난 내가 직접 커스텀 하는 걸 선호한다.토큰 발급jwt_token.py토큰 인증 시스템token_auth.py커스텀한 토큰 인증 시스템 붙이기settings.py아주 간단하구만

velog.io

 

10. Throttling - 호출 횟수 제한

 - Throttle 이란 특정 조건 하에 전체, 혹은 개별 API의 최대 호출 회수를 결정하는 클래스이다.

 - 기본 코드

REST_FRAMEWORK = {
	"DEFAULT_THROTTLE_CLASSES": [
        "rest_framework.throttling.AnonRateThrottle",
        "rest_framework.throttling.UserRateThrottle",
    ],
    "DEFAULT_THROTTLE_RATES": {
        "anon": "1/day",
        "user": "3/day",
    }
}

 - 모든 View에 대해 접속 제한이 설정된다. (모든 접근에 대해 total count 합계로 산정한다.)

   - anon은 ip 기반, user은 로그인한 사용자 기반이다.

 

 - 전역 설정을 view 개별 설정으로 바꾸는 방법 (모든 접근에 대해 total count 합계로 산정한다.)

   - setting 내용을 변경한다.

REST_FRAMEWORK = {
	# "DEFAULT_THROTTLE_CLASSES": [
    #     "rest_framework.throttling.AnonRateThrottle",
    #     "rest_framework.throttling.UserRateThrottle",
    # ],
    "DEFAULT_THROTTLE_RATES": {
        "anon": "1/day",
        "user": "3/day",
    }
}

   - 각 view에 접속 제한 범위에 대한 변수 값을 정의한다

class ReviewDetail(generics.RetrieveUpdateDestroyAPIView):
   .
   throttle_classes = [AnonRateThrottle]

 

 - custom throttling 만들기 - 각 view별로 접근 제한을 설정할 수 있다.

   - throttling 파일을 생성한다.

from rest_framework.throttling import UserRateThrottle


class ReviewCreateThrottle(UserRateThrottle):
    scope = 'review-create'


class ReviewListThrottle(UserRateThrottle):
    scope = 'review-list'

   - setting 업데이트

"DEFAULT_THROTTLE_RATES": {
        "anon": "5/day",
        "user": "10/day",
        "review-create": "2/day",
        "review-list": "100/day",
        "review-detail": "100/day",
    },

   - view에 적용

class ReviewList(generics.ListCreateAPIView):
   .
   throttle_classes = [ReviewListThrottle]

 - 여러개의 throttling 을 선언하면, 설정된 값 중 가장 낮은 제한을 기준으로 설정된다.

throttle_classes = [ReviewListThrottle, AnonRateThrottle]

 - ScopedRateThrottle 설정으로 제한하는 방법 - 코드 수를 줄일 수 있다.

   1) setting을 이용한 전역 설정

      - setting 설정

REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': [
        'rest_framework.throttling.ScopedRateThrottle',
    ],
    'DEFAULT_THROTTLE_RATES': {
        "anon": "5/day",
        "user": "10/day",
        "review-create": "2/day",
        "review-list": "100/day",
        "review-detail": "100/day",
    },
}

      - view 설정

class ReviewDetail(generics.RetrieveUpdateDestroyAPIView):
   .
   throttle_scope = "review-detail"

   2) 각 class별 설정

      - setting에서 DEFAULT_THROTTLE_CLASSES 설정을 하지 않은 상태로 둔다.

      - view 설정

throttle_classes = [ScopedRateThrottle]
throttle_scope = "review-detail"

 - DEFAULT_THROTTLE_RATES에 선언된 변수를 특정 view에 같이 설정하면 합산되어 count가 계산된다.

 

 

 - reference : 

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

 

Throttling - Django REST framework

 

www.django-rest-framework.org

https://velog.io/@joje/Throttling-%EC%B5%9C%EB%8C%80-%ED%98%B8%EC%B6%9C-%ED%9A%9F%EC%88%98-%EC%A0%9C%ED%95%9C%ED%95%98%EA%B8%B0

 

Throttling (최대 호출 횟수 제한하기)

DRF에서 지원하는 Throttling 설정을 통해 시간당 최대 횟수를 제한할 수 있다. 또한 APIView에 scope를 지정함으로써 scope별로 다르게 설정도 가능하다.

velog.io

https://seoyoung2.github.io/django/2020/08/19/Throttling.html

 

[DRF] Throttling (최대 호출횟수 제한) - LunaLunaఇ

Throttling Throttle 는 특정 조건 하에 최대 호출 횟수를 결정하는 클래스다. 이와 관련된 용어로는 Rate(지정 기간 내의 최대 호출 횟수), Scope(각 Rate에 대한 별칭)가 있다. Throttle에 대해 자세히 살펴

seoyoung2.github.io

https://www.lostcatbox.com/2020/01/23/DRF10+11/ 

 

DRF 기본편 10+11 · lostcatbox

Word count: 1.4kReading time: 8 min  2020/01/23   Share     

www.lostcatbox.com

 

11. Filtering 

 - django 코드만을 사용한 serching 기능 구현

class UserReview(generics.ListAPIView):
    # queryset = Review.objects.all()
    serializer_class = ReviewSerializer
    # permission_classes = [IsAuthenticated]
    # throttle_classes = [ReviewListThrottle, AnonRateThrottle]

    # def get_queryset(self):
    #     username = self.kwargs['username']
    #     return Review.objects.filter(review_user__username=username)

    def get_queryset(self):
        username = self.request.query_params.get('username', None)
        return Review.objects.filter(review_user__username=username)

 - django-filter를 사용한 기능 구현

 - 패키지 정보 : https://django-filter.readthedocs.io/en/stable/ 

 - 패키지 설치 : 

pip install django-filter

 - setting 설정

INSTALLED_APPS = [
    ...
    'django_filters',
    ...
]

 - setting을 이용한 전역 설정

REST_FRAMEWORK = {
    'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend']
}

 - view에 개별 설정

from django_filters.rest_framework import DjangoFilterBackend

class UserListView(generics.ListAPIView):
    ...
    filter_backends = [DjangoFilterBackend]

 - view에서 필터 정의 방법

   - 검색에 사용될 모델 필드명을 정의한다.

   - 해당 필터들은 index가 되어 있어야 효율적인 검색이 이루어 진다.

class ProductList(generics.ListAPIView):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ['category', 'in_stock']

 - filterset_fields의 검색 인자

   - '^' 검색으로 시작합니다.
   -  '=' 정확히 일치합니다.
   -  '@' 전체 텍스트 검색. (현재는 Django의 PostgreSQL 백엔드만 지원됩니다.)
   -  '$' 정규식 검색

 

 - url에도 직접 검색 유형을 정의할 수 있다. 해당 내용은 공식 문서를 통해 확인 가능하다.

 

 - 검색 결과를 ordering 할 수 있다.

class UserListView(generics.ListAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    filter_backends = [filters.OrderingFilter]
    ordering_fields = ['username', 'email']

 

* 여러 인자 획득
 - filtering 을 하는데 필요한 인자들을 request 를 이용해 획득할 수 있다

 - self.request.user
   - 현재 로그인 중인 유저에 접근할 수 있다
   -  로그인이 안 되어 있을 시에는 AnnoymousUser 인스턴스를 획득한다

 -  self.request.GET
   -  요청한 get 인자들을 획득한다

 -  self.request.query_params
   -  self.request.GET 와 같은 값을 얻는다
   -  보다 더 가독성이 높기 때문에 DRF 에서 지원하고 있다

 -  self.kwargs
   -  URL Capture 된 인자를 획득한다

 

 - reference :

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

 

Filtering - Django REST framework

 

www.django-rest-framework.org

 

12. pagination

- PageNumberPagination은 데이터를 일정 크기의 페이지로 나누고 클라이언트가 특정 페이지를 요청할 수 있게 합니다.
-  LimitOffsetPagination은 클라이언트가 반환할 항목 수와 데이터 컬렉션 내에서 시작 지점을 지정할 수 있습니다.
-  CursorPagination은 큰 데이터 세트에 대해 더 효율적인 커서 기반의 페이지네이션 시스템을 제공합니다.

 

 - LimitOffsetPagination 설정 방법

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
    'PAGE_SIZE': 100
}

 - postman에서 호출 방법

 - view 개별적으로 pagination 설정, PageNumberPagination, LimitOffsetPagination, CursorPagination 커스텀

 - 각 클래스를 상속받아 개별 pagination을 정의한다

 - pagination.py 생성

   - 각 클래스별 사용되는 클래스 변수의 기능들은 공식 페이지 혹은 다음 사이트 정보를 참고한다

      - https://kimdoky.github.io/django/2018/07/19/drf-Pagination/

from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination


class WatchListPagination(PageNumberPagination):
    page_size = 3
    page_query_param = 'p'
    page_size_query_param = 'size'
    max_page_size = 10
    last_page_strings = 'end'


class WatchListLOPagination(LimitOffsetPagination):
    default_limit = 5
    max_limit = 10
    limit_query_param = 'limit'
    offset_query_param = 'start'


class WatchListCPagination(CursorPagination):
    page_size = 5
    ordering = 'created'
    cursor_query_param = 'record'

- view 파일에 정의

class WatchListGV(generics.ListAPIView):
    queryset = WatchList.objects.all()
    serializer_class = WatchListSerializer
    pagination_class = WatchListCPagination

 

 - reference :

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

 

Pagination - Django REST framework

pagination.py Django provides a few classes that help you manage paginated data – that is, data that’s split across several pages, with “Previous/Next” links. — Django documentation REST framework includes support for customizable pagination styl

www.django-rest-framework.org

https://kimdoky.github.io/django/2018/07/19/drf-Pagination/

 

 

 

Django REST Framework - Pagination

on July 19, 2018 under django 13 minute read “Django provides a few classes that help you manage paginated data – that is, data that’s split across several pages, with “Previous/Next” links.” “Django는 페이지가 매겨진 데이터, 즉

kimdoky.github.io

https://wikidocs.net/197566

 

06) 페이지네이션, 필터링, 그리고 정렬

[TOC] Django REST Framework에서 페이지네이션, 필터링, 그리고 정렬을 지원하는 API를 만드는 것은 매우 쉽습니다. 이러한 기능은 대량의 데이터를 반환하는…

wikidocs.net

https://velog.io/@jewon119/TIL00.-DRF-Pagination-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0

 

TIL126. DRF : Pagination 적용하기

📌 이 포스팅에서는 Django Rest Framework에서 제공하는 PageNumberPagination, LimitOffsetPagination, CursorPagination를 적용시키는 방법에 대해 정리하였습니다. 🌈 Pagination 적용하기 > ##

velog.io

https://ssungkang.tistory.com/entry/Django-DRF-Pagination

 

[Django] DRF Pagination

실제 서비스의 경우에는 레코드의 개수가 많을 것이고 이 경우 하나의 API 요청으로 모든 레코드를 받는 것은 오랜 시간이 걸리게 됩니다. 따라서 이런 경우 페이지를 나눠서 요청을 해야합니다.

ssungkang.tistory.com

https://23life.tistory.com/80

 

Djagno REST Framework _ Pagination 사용하기

모든 요청을 한 페이지에 담는다면? 보기도 힘들뿐더러 시간도 오래 걸릴 테다. 따라서 페이지를 나눠 요청을 보내게 되는데... 우리의 친절한 DRF는 이러한 pagination 기능을 제공한다! www.django-rest

23life.tistory.com

https://velog.io/@holawan/DRF-Pagination

 

DRF Pagination

한정된 네트워크 자원을 효율적으로 활용하기 위해 쿼리의 결과값으로 리턴된 리소스를 분할하여 전달 하는 것을 의미한다.콘텐츠를 여러 페이지로 나누고, 이전 혹은 다음 페이지로 넘어가거

velog.io

 

13. 응답 형식 변경 - renderers 

 - 하나의 API 인자에 따라 같은 내용을 PDF, XLSX, JSON, HTML 등등 다양한 형태의 응답 포맷을 지원하고 싶을땐 DRF의 Renderer 를 이용할 수 있다.

 

 - 기본 설정

REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': [
        'rest_framework.renderers.JSONRenderer',
        'rest_framework.renderers.BrowsableAPIRenderer',
    ]
}

 - view에서 설정하는 방법

from django.contrib.auth.models import User
from rest_framework.renderers import JSONRenderer
from rest_framework.response import Response
from rest_framework.views import APIView

class UserCountView(APIView):
    """
    A view that returns the count of active users in JSON.
    """
    renderer_classes = [JSONRenderer]

    def get(self, request, format=None):
        user_count = User.objects.filter(active=True).count()
        content = {'user_count': user_count}
        return Response(content)

 - template가 있는 경우 

class UserDetail(generics.RetrieveAPIView):
    """
    A view that returns a templated HTML representation of a given user.
    """
    queryset = User.objects.all()
    renderer_classes = [TemplateHTMLRenderer]

    def get(self, request, *args, **kwargs):
        self.object = self.get_object()
        return Response({'user': self.object}, template_name='user_detail.html')

 

- reference : 

https://velog.io/@duo22088/DRF-Renderer-를-통한-다양한-응답

 

(DRF) Renderer 를 통한 다양한 응답

REST 프레임워크에는 다양한 미디어 유형으로 응답을 반환할 수 있는 여러 내장 렌더러 클래스가 포함되어 있습니다.

velog.io

https://hyun-am-coding.tistory.com/entry/8-Renderers

 

8. Renderers

Renderers TemplateResponse 인스턴스를 클라이언트로 반환하려면 먼저 인스턴스를 렌더링해야 합니다. 렌더링 프로세스는 템플릿과 컨텍스트를 중간으로 표현하여 클라이언트에 제공할 수 있는 최종

hyun-am-coding.tistory.com

 

댓글