DRF 인강 정리

succeeding·2022년 3월 8일
1

DRF 작성 순서


sMuVsT

  • setting
  • Model
  • url
  • View
  • seralizer
  • Template

DRF의 url 끝처리


REST에서는 url끝에 /을 사용하지 말라고 권장하고, DRF는 RESTful을 지향함에도 django를 따라 끝에 /을 사용한다. 또한 path 구문 작성시 앞쪽엔 /를 포함하지 않는다. 자세한 사항은 django 공식문서의 settings.APPEND_SLASH를 참고할 것

DRF의 응답모드


DRF는 API, JSON 두 가지 모드로 응답을 보여준다.

DRF default mode
1) browser: api mode(==browsable api)
2) browser가 아닌 다른 client: json mode

자세한 것은 아래 참조
https://www.django-rest-framework.org/api-guide/format-suffixes/

Example


settings.py

DRF example에 나와있는 permission 설정

# settings.py
REST_FRAMEWORK = {
    # Use Django's standard `django.contrib.auth` permissions,
    # or allow read-only access for unauthenticated users.
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
    ]
}

위와 같이 설정하면 회원은 CRUD를 비회원은 R를 사용할 수 있으며, 이를 주석 처리하면, 모두가 CRUD를 사용할 수 있다.(AllowAny)

urls.py

urlpatterns = [
    ...생략...
    # 아래 path는 로그인 기능 사용할 수 있게하는 url이다.
    # 이를 주석 처리하면 api모드에서 login 버튼이 사라진다.
    path('api-auth/', include('rest_framework.urls', namespace='rest_framework'))
]

DRF Router


DefaultRouter vs SimpleRouter

  1. 공통점
    router가 만드는 6가지 url이 둘 다 동일함.
users/	GET(list)
users/	POST(create)

users/pk/	GET(retrieve)
users/pk/	PUT(update)
users/pk/	DELETE(destroy)
users/pk/	PATCH(partial_update)
  1. 차이점
    다음은 DefaultRouter만 생성한다.
API Root	ex:api2/
format suffix	ex:users.json, users.api, users/99.json, users/99.api

Example code에선 DefaultRouter를 사용하고 있다.

# urls.py
router = routers.DefaultRouter()

viewset


DRF 공식 문서 가이드

  • Tutorial : DRF를 맛보는 튜토리얼
  • API Guide : DRF의 주요 기능들
  • Topics : 메인 기능은 아니지만, 궁금해할 혹은 문제가 될만한 이슈들
  • community : 공부할 내용이 더 있음

view 참고 문헌

  1. https://www.django-rest-framework.org/api-guide/viewsets/#modelviewset
    처음 viewset 공부할 때

  2. https://www.cdrf.co/
    view와 관련된 소스들이 정리된 페이지

  • 활용법: 공식문서에서 개념잡고 오버라이딩 할 때 2. 참고

MIXINS를 모아서 GENERICS를 만들고 또 모아서 VIEWSETS을 만듬.(2. 참고)
그 중에서도 ModelViewSet은 모든 기능이 들어있다.

계층도

  1. View : Django의 GenericView
  2. APIView
  3. GenericAPIView
  4. GenericAPIView + (method name)MIXINS = (mehtod name)APIView
  5. ModelViewset
    1. 를 CRUD 기능으로 구분하면 아래와 같으며, 이 다섯가지를 모두 조합한 것이 ModelViewSet. 그래서 DefaultRouter가 다양한 url을 만들 수 있었던 것. 먼저 ModelViewSet을 공부하는 것이 전체적인 윤곽을 보는 것에 도움을 주므로 이를 먼저 공부하자.
    • Create
      • CreateAPIView(POST)
    • Read
      • ListAPIView(GET) : 리스트 조회 기능
      • RetrieveAPIView(GET) : 디테일 조회 기능
    • Update
      • UpdateVPIView(PUT, PATCH)
    • Delete
      • DestoryAPIView(DELETE)
  • 2~3개만 섞어서 또 다른 GENERICS를 만듬
  1. ViewSet은 DB의 초기상태를 볼 수 있다는 장점이 있어서, 먼저 ViewSet으로 전체를 보고 customizing은 개별 View로 하는 것을 권장.

GENERICS


DRF url 명명법

pk가 있으면 detail, 없으면 list라고 짓는다.

  • 예시
    • 아래에서 comment는 생성하는 것임에도 list라고 이름 지었다. 일반적인 장고라면 comment-create라고 지었을 것. 하지만 절대적인 것은 아니다. like 기능의 경우 post-like라고 이름 지었다.
urlpatterns = [
    path('post/', views.PostListAPIVeiw.as_view(), name='post-list'),
    path('post/<int:pk>/', views.PostRetrieveAPIVeiw.as_view(), name='post-detail'),
    path('comment/', views.CommentCreateAPIVeiw.as_view(), name='comment-list'),
]

GENERICS의 logic

  1. getting data from db
  2. serializing db
  3. response to client

ListAPIView와 RetrieveAPIView의 차이점

class PostListAPIView(ListAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer


class PostRetreiveAPIView(RetrieveAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

에서
ListAPIViewPostSerializer(instance=, many=True)
RetrieveAPIViewPostSerializer(instance=, many=False)로 serialize를 하는데, many=True면 인스턴스를 여러개 many=False면 한 개를 serialize한다.

serializers.py


출력 내용, 포멧 등과 관련된 것은 모두 serializers.py에서 작성

필수 Field 확인하기

serializer는 blank를 허용하지 않는 필드를 필수 필드로 만든다. 따라서 models.py에서 blank=True가 없는 필드가 필수 필드이다.(단, default값아 존재하거나, auto_now_add 등 자동으로 생성되는 필드는 필수필드가 아니다.)

  • 장고shell 에서 확인하기
% python manage.py shell

>>> from api2.serializers import *
>>> 
>>> PostListSerializer()
PostListSerializer():
    id = IntegerField(label='ID', read_only=True)
    title = CharField(label='TITLE', max_length=50)
    image = ImageField(allow_null=True, label='IMAGE', max_length=100, required=False)
    like = IntegerField(label='LIKE', required=False)
    category = PrimaryKeyRelatedField(allow_null=True, queryset=Category.objects.all(), required=False)

title을 제외한 나머지에서 required=False 이 나타나고 있음을 확인할 수 있다.

python console에서 django 사용하기

Serializer의 Field 속성을 보는데 이용할 수 있다.
pycharm pro 에서는 기본으로 제공하는 기능이어서 아래 내용 코드의 내용 필요없이 그저 python console 탭을 누르면 된다.

PyDev console
>>> import os, django
>>> os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')
'mysite.settings'
>>> django.setup()

terminal에서 python manage.py shell을 사용하면 자동완성 기능을 사용하기 어렵다. python console은 자동완성 기능을 제공하므로 좀 더 편리하게 shell을 사용할 수 있다.

Serialization, 직렬화 / 역직렬화


Serialize는 Form과 비슷하다고 얘기한다.
Serialize는 Form과 모델 중 무엇에 더 비슷할까?

Form vs Model

  • 차이점

    • 다루는 것
      Form : html <form>
      Model : DB Table
  • 공통점
    Serialization(직렬화), Validation(유효성점검) 기능을 제공함

DRF의 Serializer가 Serializaiont, Validation을 제공한다. 그래서 Serializer는 Form, Model과 비슷해보이는 것.

직렬화가 필요한 이유

  1. 메모리 내부 vs 외부 - 환경이 다름
    프로그램의 실행은 모든 것이 메모리 내부에서 이루어진다. 예를 들어 "홍길동"이라는 string을 메모리 외부로 보낼 때 파일,DB,Network 등을 이용할 것이다. 이 때 "홍길동"을 string 그대로 저장할 수 있는 것이 아니라, btyes로 저장된다. 즉, 메모리 외부에서는 bytestring으로 정보를 저장한다.

  2. 복원시 정보 유지
    메모리 외부에 파일을 저장하고 다시 불러왔을 때, 메모리 내부에 있었을 때와 같은 모습으로 정보가 유지되길 원할 것이다. 따라서 메모리 외부로 정보를 저장할때는 직렬화, 불러 올때는 역직렬화를 통해서 내부와 외부에서의 정보의 형태 차이에 따른 정보 변형(예를 들어 프로그램 내부에선 type이 int였는데 저장하고 복원하니 string이 되는 경우)을 방지할 필요가 있다.

JSON

JSON은 가장 간단한 serialization 방법이다. 거의 타입 변환만 해주는 정도. number, string, object, list, null 이정도의 type만 존재.

logic

GENERICS가 알아서 다 처리해주는 과정이지만 APIView, GenericAPIView를 커스터마이징할 때 알아야 할 내용들이기도 하니, 그 안에 logic을 자세히 살펴보자.
1. getting data from db --> instance
2. seralizing db --> dict
3. response to client --> bytes

# 1. 과정
<instance> = <ModelClasss>()

# (1. -> 2. 과정) : Serializing or Read operation of Serializer
	# GET 메서드 처리시 사용
<dict> = <SerializerClass>(instance=<instance>).data
	

# (2. -> 3. 과정)
JSONRenderer().render(<dict>)
# 3. 과정
BytesIO(<bytes>) # 받은 <bytes>를 바로 <dict>으로 parsing할 수 없음

# (3. -> 2. 과정)
<dict> = JSONParser().parse(BytesIO(<bytes>))

# (2. -> 1. 과정) : Deserializing or Write oepration of Serializer
	# POST, UPDATE, DELETE, PATCH 메서드 처리시 사용
<SerializerObject> = <SerializerClass>(data=<dict>)
<SerializerObject>.is_valid()	# 유효성검사 필수!!!
<instance> = <ModelClass>(**<SerializerObject>.validated_data)
<instance>.save() # create(), update() method도 가능

Deserializing 과정에선 클라이언트에서 받은 데이터에 대해서 반드시 유효성 검사를 해야한다.
<SerializerObject>.is_valid()

  • 이후 에러가 나면 <SerializerObject>.errors 에 데이터가 저장되고,
  • 유효하면 <SerializerObject>.validated_data에 데이터가 저장된다.

참고
공식문서 : https://www.django-rest-framework.org/api-guide/serializers/#serializers

2개의 Model를 사용하는 API


GENERICS나 ViewSet은 Model를 1개만 사용한다. 이럴 땐 APIView, GenericAPIView를 사용한다.

  • GenericAPIView : getqueryset, getobject 테이블 처리 메서드가 있어서 이것들을 재활용하는 경우 사용
    • 참고로 DRF에서 Generic은 db에 관련된 처리를 할 때 붙이는 단어다.
  • APIView : 재활용할 것이 딱히 없다면 사용함

List Array

1) List Field
2) serializer(many=True)

like 기능 수정


DRF에서 like 기능에 알맞은 메서드는 PATCH가 아닌 GET이다.

  • serializer는 Read, Write 두 가지 operation이 있다.
  • 두 operation 차이점 중 하나는 Write에는 유효성 검사가 포함된다.
  • like 기능은 사용자가 입력하는 데이터가 없기 때문에 굳이 유효성 검사를 하지 않아도 된다.
  • GET은 Read operation을 호출하고, PATCH는 Wirte operation을 호출한다.
  • 따라서 GET메서드가 효율적이다.

Pagaination


참고문헌

Serializer에서 ForeignKey


Django의 models.ForeginKey는 해당 모델을 Meta로 불러온 Serializer에선 DRF의 PrimaryKeyRelatedField로 변경된다.

예시)

# models.py
class Post(models.Model):
    category = models.ForeignKey('Category', ...)
    
# serializers.py
class PostListSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        fields = [..., 'category']

# Django Console
>>> PostListSerializer()
PostListSerializer():
    (...)
    category = PrimaryKeyRelatedField(...)

Serializer에서 ForeginKey로 연결된 모델 중 특정 필드에 접근하려면, 클래스 변수에 source 키워드 호출 사용하여 오버라이딩한다.

예시)

# seralizers.py
class PostListSerializer(serializers.ModelSerializer):
    category = serializers.CharField(source='category.name')

    class Meta:
        model = Post
        fields = [..., 'category']
        
# Django Console
>>> PostListSerializer()
PostListSerializer():
    category = CharField(source='category.name')

URL 출력 포맷 수정

ImageField의 전체url이 나오는 것을 path만 나오도록 변경해보자.

FileField

ImageField를 살펴보고자 하니 FileField를 상속하고 있어서 그것을 먼저 살펴보자. rest_framework/fields.py에 들어가면 serliazer에 쓰이는 DRF의 fileds의 클래스 코드가 있다.

  • to_internal_value 메서드는 Write mode(or Deserializing)에서 사용되며 to_represtation 메서드는 Read mode(or Serializing)에서 사용된다.
  • to_represtation에서 request.build_absolute_uri(url)은 전체 url을 return한다. 이 때 requestNone이라면 path만 나오는 url을 return한다.
class FileField(Field):
    ...

    def to_internal_value(self, data):...

    def to_representation(self, value):
        if not value:
            return None

        use_url = getattr(self, 'use_url', api_settings.UPLOADED_FILES_USE_URL)
        if use_url:
            try:
                url = value.url
            except AttributeError:
                return None
            request = self.context.get('request', None)
            if request is not None:
                return request.build_absolute_uri(url)
            return url

        return value.name

reqeustNone으로

rest_framework/genereics.py에서 get_serializer_context메서드를 오버라이딩한다.

# rest_framework/genereics.py
class GenericAPIView(views.APIView):
    ...
    def get_serializer_context(self):
        """
        Extra context provided to the serializer class.
        """
        return {
            'request': self.request,
            'format': self.format_kwarg,
            'view': self
        }

request': self.request에서 request': None으로 오버라이딩하면 된다.

이전, 다음 포스트


Django의 Model API에서 기본으로 제공하는 메서드 get_next_by_FOO, get_previous_by_FOO을 사용하면 쉽게 다룰수 있다. 이 메서드들은 다음 혹은 이전 필드가 존재하지 않는 경우 DoesNotExist 예외를 발생시킨다.

참고 문서
https://docs.djangoproject.com/en/4.0/ref/models/instances/#django.db.models.Model.get_next_by_FOO

Serializer relations

Relational fields are used to represent model relationships. They can be applied toForeignKey, ManyToManyField and OneToOneField relationships, as well as to reverse relationships, and custom relationships such as GenericForeignKey.
- DRF 공식 문서

Relational fields

  • PrimaryKeyRelatedField
    자주 사용되는 Realational fields.
  • StringRelatedField
    모델의 __str__메서드를 사용하여 타겟(연결된 모델)을 나타낸다.

더 자세히 알고 싶으면 위에 있는 공식문서 참고.

Serializer가 꼭 필요할까?


대안 : 직접 Dictionary를 만들어서 serializing한다.

GENERICS를 ViewSet으로 바꾸기


ViewSet은 개별 GENERICS의 기능들을 모두 포함하고 있으며, router를 사용할 수 있다.

  • router를 사용하는 경우
    알아서 url을 만들어주며 urlConf 과정에서 ViewSet.as_view(action=)action을 알아서 채워준다. action은 ViewSet을 사용하는 경우 필수값이다.
  • router를 사용하지 않는 경우
    ViewSet은 테이블 단위로 기능을 모아준다. 수동으로 url과 action을 작성해야한다.
    • ViewSet은 get메서드를 갖지 않는다. 다른 이름으로 GET 메서드 기능을 담당하는 메서드를 만들고, action에서 get을 다른 이름의 메서드로 매핑해준다.

0개의 댓글