drf tutorial - 주요 정리
- 참고 :
https://www.django-rest-framework.org/tutorial/quickstart/
Quickstart - Django REST framework
We're going to create a simple API to allow admin users to view and edit the users and groups in the system. Create a new Django project named tutorial, then start a new app called quickstart. # Create the project directory mkdir tutorial cd tutorial # Cre
www.django-rest-framework.org
https://www.django-rest-framework.org/tutorial/1-serialization/
1 - Serialization - Django REST framework
This tutorial will cover creating a simple pastebin code highlighting Web API. Along the way it will introduce the various components that make up REST framework, and give you a comprehensive understanding of how everything fits together. The tutorial is f
www.django-rest-framework.org
https://www.django-rest-framework.org/tutorial/2-requests-and-responses/
2 - Requests and responses - Django REST framework
From this point we're going to really start covering the core of REST framework. Let's introduce a couple of essential building blocks. REST framework introduces a Request object that extends the regular HttpRequest, and provides more flexible request pars
www.django-rest-framework.org
https://www.django-rest-framework.org/tutorial/3-class-based-views/
3 - Class based views - Django REST framework
We can also write our API views using class-based views, rather than function based views. As we'll see this is a powerful pattern that allows us to reuse common functionality, and helps us keep our code DRY. We'll start by rewriting the root view as a cla
www.django-rest-framework.org
https://www.django-rest-framework.org/tutorial/4-authentication-and-permissions/
4 - Authentication and permissions - Django REST framework
Currently our API doesn't have any restrictions on who can edit or delete code snippets. We'd like to have some more advanced behavior in order to make sure that: Code snippets are always associated with a creator. Only authenticated users may create snipp
www.django-rest-framework.org
https://www.django-rest-framework.org/tutorial/5-relationships-and-hyperlinked-apis/
5 - Relationships and hyperlinked APIs - Django REST framework
At the moment relationships within our API are represented by using primary keys. In this part of the tutorial we'll improve the cohesion and discoverability of our API, by instead using hyperlinking for relationships. Right now we have endpoints for 'snip
www.django-rest-framework.org
https://www.django-rest-framework.org/tutorial/6-viewsets-and-routers/
6 - Viewsets and routers - Django REST framework
REST framework includes an abstraction for dealing with ViewSets, that allows the developer to concentrate on modeling the state and interactions of the API, and leave the URL construction to be handled automatically, based on common conventions. ViewSet c
www.django-rest-framework.org
- serializer 다루기
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser
import io
# Snippet DB 인스턴스 생성
snippet = Snippet(code='foo = "bar"\n')
snippet.save()
# Snippet DB 인스턴스 생성
snippet = Snippet(code='print("hello, world")\n')
snippet.save()
# Snippet DB 인스턴스 시리얼라이즈 과정 1 (DB 인스턴스 -> Python Dictionary)
serializer = SnippetSerializer(snippet)
serializer.data
# {'id': 2, 'title': '', 'code': 'print("hello, world")\n', 'linenos': False, 'language':
# 'python', 'style': 'friendly'}
# Snippet DB 인스턴스 시리얼라이즈 과정 2 (Python Dictionary -> JSON 데이터)
content = JSONRenderer().render(serializer.data)
content
# b'{"id": 2, "title": "", "code": "print(\\"hello, world\\")\\n", "linenos": false,
# "language": "python", "style": "friendly"}'
# JSON 데이터 디시리얼라이즈 과정 1 (JSON 데이터 -> Python Dictionary)
stream = io.BytesIO(content)
data = JSONParser().parse(stream)
# JSON 데이터 디시얼라이즈 과정 2 (Python Dictionary -> DB 인스턴스)
serializer = SnippetSerializer(data=data)
serializer.is_valid()
# True
serializer.validated_data
# OrderedDict([('title', ''), ('code', 'print("hello, world")\n'), ('linenos', False),
# ('language', 'python'), ('style', 'friendly')])
serializer.save()
# <Snippet: Snippet object>
# DB 인스턴스와 같이 쿼리셋도 시리얼라이즈 가능 (many=True)
serializer = SnippetSerializer(Snippet.objects.all(), many=True)
serializer.data
# [OrderedDict([('id', 1), ('title', ''), ('code', 'foo = "bar"\n'), ('linenos', False),
# ('language', 'python'), ('style', 'friendly')]), OrderedDict([('id', 2), ('title', ''),
# ('code', 'print("hello, world")\n'), ('linenos', False), ('language', 'python'),
# ('style', 'friendly')]), OrderedDict([('id', 3), ('title', ''),
# ('code', 'print("hello, world")'), ('linenos', False), ('language', 'python'),
# ('style', 'friendly')])]
- Request objects
- 두 가지의 데이터 접근 방법을 제공한다.
request.POST # Only handles form data. Only works for 'POST' method.
request.data # Handles arbitrary data. Works for 'POST', 'PUT' and 'PATCH' methods.
- api_view를 사용한 함수형 api view
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
@api_view(['GET', 'POST'])
def snippet_list(request):
"""
List all code snippets, or create a new snippet.
"""
if request.method == 'GET':
snippets = Snippet.objects.all()
serializer = SnippetSerializer(snippets, many=True)
return Response(serializer.data)
elif request.method == 'POST':
serializer = SnippetSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
@api_view(['GET', 'PUT', 'DELETE'])
def snippet_detail(request, pk):
"""
Retrieve, update or delete a code snippet.
"""
try:
snippet = Snippet.objects.get(pk=pk)
except Snippet.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
if request.method == 'GET':
serializer = SnippetSerializer(snippet)
return Response(serializer.data)
elif request.method == 'PUT':
serializer = SnippetSerializer(snippet, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
elif request.method == 'DELETE':
snippet.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
- format 추가하기
- json 혹은 api format으로 요청할 수 있다.
- 웹브라우저로 요청할 경우에만 api format이 지원되고, 다른 장비로 접근하면 json으로만 응답한다.
def snippet_list(request, format=None):
def snippet_detail(request, pk, format=None):
# urls.py update
from django.urls import path
from rest_framework.urlpatterns import format_suffix_patterns
from snippets import views
urlpatterns = [
path('snippets/', views.snippet_list),
path('snippets/<int:pk>/', views.snippet_detail),
]
urlpatterns = format_suffix_patterns(urlpatterns)
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from django.http import Http404
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
class SnippetList(APIView):
"""
List all snippets, or create a new snippet.
"""
def get(self, request, format=None):
snippets = Snippet.objects.all()
serializer = SnippetSerializer(snippets, many=True)
return Response(serializer.data)
def post(self, request, format=None):
serializer = SnippetSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class SnippetDetail(APIView):
"""
Retrieve, update or delete a snippet instance.
"""
def get_object(self, pk):
try:
return Snippet.objects.get(pk=pk)
except Snippet.DoesNotExist:
raise Http404
def get(self, request, pk, format=None):
snippet = self.get_object(pk)
serializer = SnippetSerializer(snippet)
return Response(serializer.data)
def put(self, request, pk, format=None):
snippet = self.get_object(pk)
serializer = SnippetSerializer(snippet, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, pk, format=None):
snippet = self.get_object(pk)
snippet.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
- class based views
- using mixins
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from rest_framework import mixins
from rest_framework import generics
class SnippetList(mixins.ListModelMixin,
mixins.CreateModelMixin,
generics.GenericAPIView):
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
class SnippetDetail(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
generics.GenericAPIView):
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
- generic class based views
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from rest_framework import generics
class SnippetList(generics.ListCreateAPIView):
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
- Authentication & Permissions
from rest_framework import permissions
class ...
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
- permissions.py (custom)
from rest_framework import permissions
class IsOwnerOrReadOnly(permissions.BasePermission):
"""
Custom permission to only allow owners of an object to edit it.
"""
def has_object_permission(self, request, view, obj):
# Read permissions are allowed to any request,
# so we'll always allow GET, HEAD or OPTIONS requests.
if request.method in permissions.SAFE_METHODS:
return True
# Write permissions are only allowed to the owner of the snippet.
return obj.owner == request.user
- insert at class
class ...
permission_classes = [permissions.IsAuthenticatedOrReadOnly,
IsOwnerOrReadOnly]
- Relationships & Hyperlinked APIs
class SnippetSerializer(serializers.HyperlinkedModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
highlight = serializers.HyperlinkedIdentityField(view_name='snippet-highlight', format='html')
class Meta:
model = Snippet
fields = ['url', 'id', 'highlight', 'owner',
'title', 'code', 'linenos', 'language', 'style']
class UserSerializer(serializers.HyperlinkedModelSerializer):
snippets = serializers.HyperlinkedRelatedField(many=True, view_name='snippet-detail', read_only=True)
class Meta:
model = User
fields = ['url', 'id', 'username', 'snippets']
from django.urls import path
from rest_framework.urlpatterns import format_suffix_patterns
from snippets import views
# API endpoints
urlpatterns = format_suffix_patterns([
path('', views.api_root),
path('snippets/',
views.SnippetList.as_view(),
name='snippet-list'),
path('snippets/<int:pk>/',
views.SnippetDetail.as_view(),
name='snippet-detail'),
path('snippets/<int:pk>/highlight/',
views.SnippetHighlight.as_view(),
name='snippet-highlight'),
path('users/',
views.UserList.as_view(),
name='user-list'),
path('users/<int:pk>/',
views.UserDetail.as_view(),
name='user-detail')
])
- adding pagination
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 10
}
- ViewSets & Routers
from rest_framework import viewsets
class UserViewSet(viewsets.ReadOnlyModelViewSet):
"""
This viewset automatically provides `list` and `retrieve` actions.
"""
queryset = User.objects.all()
serializer_class = UserSerializer
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework import permissions
class SnippetViewSet(viewsets.ModelViewSet):
"""
This viewset automatically provides `list`, `create`, `retrieve`,
`update` and `destroy` actions.
Additionally we also provide an extra `highlight` action.
"""
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
permission_classes = [permissions.IsAuthenticatedOrReadOnly,
IsOwnerOrReadOnly]
@action(detail=True, renderer_classes=[renderers.StaticHTMLRenderer])
def highlight(self, request, *args, **kwargs):
snippet = self.get_object()
return Response(snippet.highlighted)
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
from snippets.views import SnippetViewSet, UserViewSet, api_root
from rest_framework import renderers
snippet_list = SnippetViewSet.as_view({
'get': 'list',
'post': 'create'
})
snippet_detail = SnippetViewSet.as_view({
'get': 'retrieve',
'put': 'update',
'patch': 'partial_update',
'delete': 'destroy'
})
snippet_highlight = SnippetViewSet.as_view({
'get': 'highlight'
}, renderer_classes=[renderers.StaticHTMLRenderer])
user_list = UserViewSet.as_view({
'get': 'list'
})
user_detail = UserViewSet.as_view({
'get': 'retrieve'
})
urlpatterns = format_suffix_patterns([
path('', api_root),
path('snippets/', snippet_list, name='snippet-list'),
path('snippets/<int:pk>/', snippet_detail, name='snippet-detail'),
path('snippets/<int:pk>/highlight/', snippet_highlight, name='snippet-highlight'),
path('users/', user_list, name='user-list'),
path('users/<int:pk>/', user_detail, name='user-detail')
])
- 리팩터링 된 urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from snippets import views
# Create a router and register our viewsets with it.
router = DefaultRouter()
router.register(r'snippets', views.SnippetViewSet,basename="snippet")
router.register(r'users', views.UserViewSet,basename="user")
# The API URLs are now determined automatically by the router.
urlpatterns = [
path('', include(router.urls)),
]
'Study > django' 카테고리의 다른 글
mysql에서 postgresql로 마이그레이션 시도를 하다 #1 (0) | 2023.10.11 |
---|---|
[udemy] Build REST APIs with Django REST Framework and Python 학습 (0) | 2023.10.03 |
[인프런] Django REST Framework 핵심사항 학습 정리 (0) | 2023.08.29 |
DRF, 기본 django 공식 사이트 (0) | 2023.08.25 |
List of Useful URL Patterns, 정규 표현에 대해 정리 (0) | 2023.08.23 |
댓글