sMuVsT
REST에서는 url끝에 /
을 사용하지 말라고 권장하고, DRF는 RESTful을 지향함에도 django를 따라 끝에 /
을 사용한다. 또한 path
구문 작성시 앞쪽엔 /
를 포함하지 않는다. 자세한 사항은 django 공식문서의 settings.APPEND_SLASH를 참고할 것
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/
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)
urlpatterns = [
...생략...
# 아래 path는 로그인 기능 사용할 수 있게하는 url이다.
# 이를 주석 처리하면 api모드에서 login 버튼이 사라진다.
path('api-auth/', include('rest_framework.urls', namespace='rest_framework'))
]
users/ GET(list)
users/ POST(create)
users/pk/ GET(retrieve)
users/pk/ PUT(update)
users/pk/ DELETE(destroy)
users/pk/ PATCH(partial_update)
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()
https://www.django-rest-framework.org/api-guide/viewsets/#modelviewset
처음 viewset 공부할 때
https://www.cdrf.co/
view와 관련된 소스들이 정리된 페이지
MIXINS
를 모아서 GENERICS
를 만들고 또 모아서 VIEWSETS
을 만듬.(2. 참고)
그 중에서도 ModelViewSet
은 모든 기능이 들어있다.
ModelViewSet
. 그래서 DefaultRouter가 다양한 url을 만들 수 있었던 것. 먼저 ModelViewSet
을 공부하는 것이 전체적인 윤곽을 보는 것에 도움을 주므로 이를 먼저 공부하자.GENERICS
를 만듬pk
가 있으면 detail, 없으면 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'),
]
class PostListAPIView(ListAPIView):
queryset = Post.objects.all()
serializer_class = PostSerializer
class PostRetreiveAPIView(RetrieveAPIView):
queryset = Post.objects.all()
serializer_class = PostSerializer
에서
ListAPIView
는 PostSerializer(instance=, many=True)
RetrieveAPIView
는 PostSerializer(instance=, many=False)
로 serialize를 하는데, many=True
면 인스턴스를 여러개 many=False
면 한 개를 serialize한다.
출력 내용, 포멧 등과 관련된 것은 모두 serializers.py
에서 작성
serializer는 blank를 허용하지 않는 필드를 필수 필드로 만든다. 따라서 models.py
에서 blank=True
가 없는 필드가 필수 필드이다.(단, default
값아 존재하거나, auto_now_add
등 자동으로 생성되는 필드는 필수필드가 아니다.)
% 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
이 나타나고 있음을 확인할 수 있다.
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을 사용할 수 있다.
Serialize는 Form과 비슷하다고 얘기한다.
Serialize는 Form과 모델 중 무엇에 더 비슷할까?
차이점
공통점
Serialization(직렬화), Validation(유효성점검) 기능을 제공함
DRF의 Serializer가 Serializaiont, Validation을 제공한다. 그래서 Serializer는 Form, Model과 비슷해보이는 것.
메모리 내부 vs 외부 - 환경이 다름
프로그램의 실행은 모든 것이 메모리 내부에서 이루어진다. 예를 들어 "홍길동"이라는 string을 메모리 외부로 보낼 때 파일,DB,Network 등을 이용할 것이다. 이 때 "홍길동"을 string 그대로 저장할 수 있는 것이 아니라, btyes로 저장된다. 즉, 메모리 외부에서는 bytestring으로 정보를 저장한다.
복원시 정보 유지
메모리 외부에 파일을 저장하고 다시 불러왔을 때, 메모리 내부에 있었을 때와 같은 모습으로 정보가 유지되길 원할 것이다. 따라서 메모리 외부로 정보를 저장할때는 직렬화, 불러 올때는 역직렬화를 통해서 내부와 외부에서의 정보의 형태 차이에 따른 정보 변형(예를 들어 프로그램 내부에선 type이 int였는데 저장하고 복원하니 string이 되는 경우)을 방지할 필요가 있다.
JSON은 가장 간단한 serialization 방법이다. 거의 타입 변환만 해주는 정도. number, string, object, list, null 이정도의 type만 존재.
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
GENERICS나 ViewSet은 Model를 1개만 사용한다. 이럴 땐 APIView
, GenericAPIView
를 사용한다.
GenericAPIView
: getqueryset, getobject 테이블 처리 메서드가 있어서 이것들을 재활용하는 경우 사용Generic
은 db에 관련된 처리를 할 때 붙이는 단어다.APIView
: 재활용할 것이 딱히 없다면 사용함1) List Field
2) serializer(many=True)
DRF에서 like 기능에 알맞은 메서드는 PATCH가 아닌 GET이다.
참고문헌
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')
ImageField
의 전체url이 나오는 것을 path만 나오도록 변경해보자.
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한다. 이 때 request
가 None
이라면 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
reqeust
를 None
으로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
Relational fields are used to represent model relationships. They can be applied to
ForeignKey
,ManyToManyField
andOneToOneField
relationships, as well as to reverse relationships, and custom relationships such asGenericForeignKey
.
- DRF 공식 문서
PrimaryKeyRelatedField
StringRelatedField
__str__
메서드를 사용하여 타겟(연결된 모델)을 나타낸다.더 자세히 알고 싶으면 위에 있는 공식문서 참고.
대안 : 직접 Dictionary를 만들어서 serializing한다.
ViewSet은 개별 GENERICS의 기능들을 모두 포함하고 있으며, router를 사용할 수 있다.
ViewSet.as_view(action=)
의 action
을 알아서 채워준다. action
은 ViewSet을 사용하는 경우 필수값이다.action
을 작성해야한다.get
메서드를 갖지 않는다. 다른 이름으로 GET
메서드 기능을 담당하는 메서드를 만들고, action
에서 get
을 다른 이름의 메서드로 매핑해준다.