📑
Django REST Framework (DRF)
DRF 설치 및 설정
pip install djangorestframework
pip freeze > requirements.txt
그리고 settings.py의 INSTALLED_APPS에 추가해 주고, DRF를 사용하기 위해 우선 각각의 앱마다 serializers.py 파일을 생성한다.
serializers를 내가 원하는 형태로 여러 가지 만들어 놓고 원할 때 바꿔 끼워 쓸 수 있도록 함.
from rest_framework import serializers
from .models import Article
class ArticleSerializer(serializers.ModelSerializer):
class Meta:
model = Article
fields = "__all__"
from django.shortcuts import render
from django.http import JsonResponse, HttpResponse
from django.core import serializers
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .models import Article
from .serializers import ArticleSerializer
...
@api_view(["GET"])
def json_drf(request):
articles = Article.objects.all()
# articles가 단일객체가 아니기 때문에 many=True를 넣어줘야 함.
serializer = ArticleSerializer(articles, many=True)
return Response(serializer.data)
이렇게 하면 전에 django serializers를 사용하거나 JsonResponse로 직렬화했을 때와는 또 다르게 보여진다. ↴
Postman
: API를 호출하고 결과도 볼 수 있게 해주는 프로그램. Postman 가입 및 설치🔗
API 테스트, 환경관리, 협업 등을 위한 강력한 기능을 제공한다. (API 디자인, 테스트, 문서화, 공유 등)
DRF Single Model
단일모델에서 데이터를 조회해서 직렬화(serialization)하여 JSON으로 응답하기.
ModelSerializer - Model의 여러 가지 필드들을 어떻게 직렬화해서 (어떤 데이터 포맷으로) response를 줄지가 핵심. ModelForm의 사용과 굉장히 유사하다 !
API 설계
ex.
Method | Endpoint | |
Article 목록조회 | GET | /articles/ |
Article 상세조회 | GET | /articles/<int:article_id>/ |
Article 생성 | POST | /articles/ |
Article 수정 | PUT | /articles/<int:article_id>/ |
Article 삭제 | DELETE | /articles/<int:article_id>/ |
@api_view()
# article 목록 조회
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .models import Article
from .serializers import ArticleSerializer
@api_view(["GET", "POST"])
def article_list(request):
pass
이렇게 method를 지정해 놓으면 다른 method가 요청되면 405(Method Not Allowed)가 return 됨.
() 안에 아무것도 적지 않으면 기본적으로 GET만 허용.
DFR의 함수형 뷰의 경우 필수적으로 작성 필요. rest_framework 패키지의 decorators.py 내에서 확인할 수 있다.
urls.py
# article 상세 조회
from django.urls import path
from . import views
app_name = "articles"
urlpatterns = [
path("", views.article_list, name="article_list"),
path("<int:pk>/", views.article_detail, name="article_list"),
]
API는 redirect를 하는 일이 많이 없어서 name으로 참조할 일이 거의 없기 때문에 대체로 name을 써주지 않아도 된다. app들도 겹치거나 하는 일이 거의 없으므로 app_name을 써주지 않아도 된다. ↴
urlpatterns = [
path("", views.article_list),
path("<int:pk>/", views.article_detail),
]
views.py
# article 상세 조회
from rest_framework.decorators import api_view
from rest_framework.response import Response
from django.shortcuts import get_object_or_404
from .models import Article
from .serializers import ArticleSerializer
...
@api_view(["GET"])
def article_detail(request, pk):
article = get_object_or_404(Article, pk=pk)
serializer = ArticleSerializer(article)
return Response(serializer.data)
get_object_or_404를 써줘야 유효하지 않은 pk값이 들어오는 경우에 500번대 에러(서버문제)로 뜨지 않고 404(client의 잘못된 요청) 에러가 뜬다.
Create - JSON으로 주고받는다(Form도 가능함)
article_list에 GET을 요청하면 목록 조회가 되고 POST를 하면 생성이 된다.
from rest_framework.decorators import api_view
from rest_framework.response import Response
from django.shortcuts import get_object_or_404
from .models import Article
from .serializers import ArticleSerializer
@api_view(["GET", "POST"])
def article_list(request):
if request.method == "GET":
articles = Article.objects.all()
serializer = ArticleSerializer(articles, many=True)
return Response(serializer.data)
elif request.method == "POST": # article 생성
data = request.data
title = data.get("title")
content = data.get("content")
article = Article.objects.create(title=title, content=content)
return Response()
위의 elif문처럼 model의 field명을 key값으로 사용할 수도 있지만 serializer를 이용하면 로직을 간편하게 처리할 수 있다. ↴
from rest_framework.decorators import api_view
from rest_framework.response import Response
from django.shortcuts import get_object_or_404
from .models import Article
from .serializers import ArticleSerializer
@api_view(["GET", "POST"])
def article_list(request):
if request.method == "GET":
articles = Article.objects.all()
serializer = ArticleSerializer(articles, many=True)
return Response(serializer.data)
elif request.method == "POST":
serializer = ArticleSerializer(data=request.data)
if serializer.is_valid():
serializer.save() #생성
return Response(serializer.data, status=201) #생략하면 status 200(ok). 써주면 201(created)
return Response(serializer.errors, status=400) #valid하지 않은 경우, 400(Bad request)
*serializer.errors를 써주면 오류메시지를 자동으로 보여줌. 원하는 오류메시지가 따로 있다면 serializer.errors 대신 {"message": "커스텀 에러메시지"}를 써주면 됨.
is_valid()에 raise_exception=True를 넣어주면 맨 아랫줄 return Response(serializer.errors, status=400)을 안 써도 됨.
elif request.method == "POST":
serializer = ArticleSerializer(data=request.data)
if serializer.is_valid(raise_exception=True):
serializer.save() #생성
return Response(serializer.data, status=201)
# return Response(serializer.errors, status=400)
rest_framework 내에 status가 들어가 있어서 이걸 import 하면 정의되어 있는 상태코드를 갖다 쓸 수 있음. 직접 status code를 쓰는 것보다 정의되어 있는 것을 가져다 쓰는 것을 권장.(실수 방지)
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status
from django.shortcuts import get_object_or_404
from .models import Article
from .serializers import ArticleSerializer
elif request.method == "POST":
serializer = ArticleSerializer(data=request.data)
if serializer.is_valid(raise_exception=True):
serializer.save() #생성
return Response(serializer.data, status=status.HTTP_201_CREATED)
삭제는 article_detail에 DELETE 요청을 하면 삭제가 되도록 만들면 된다.
GET요청만 받던 article_detail 코드를 고쳐서 DELETE요청을 받는 경우를 처리해 준다.
elif request.method == "DELETE":
article = get_object_or_404(Article, pk=pk)
article.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
수정은 article_detail에 PUT 요청을 하면 수정되도록 만들면 된다.
elif request.method == "PUT":
article = get_object_or_404(Article, pk=pk)
serializer = ArticleSerializer(article, data=request.data, partial=True)
# partial=True를 넣어주면 부분 수정이 가능해짐
if serializer.is_valid(raise_exception=True):
serializer.save()
return Response(serializer.data)
CBV
Django는 두 가지 방식으로 view를 작성할 수 있고 마찬가지고 DRF도 이 두가지 방식의 뷰 형태로 작성이 가능하다. 지금까지는 DRF에서 함수형(Function Based) View만 작성해 봤다. 이번에는 클래스형(Class Based) View를 써보자.
Class Based View의 특징
- 특정 Http Method의 처리를 함수로 분리할 수 있다.
- 클래스를 사용하기 때문에 코드의 재사용성과 유지보수성이 향상된다.
- 기본 APIView 외에도 여러 편의를 제공하는 다양한 내장 CBV가 존재한다.
Class Based View의 종류
- APIView: DRF CBV의 베이스 클래스
- Generic APIView: 일반적인 API작성을 위한 기능이 포함된 클래스. 보통 CRUD 기능이 대부분인 상황을 위해 여러 기능이 미리 내장되어 있다. APIView보다 코드를 더 적게 적어도 자동으로 만들어진다.
- Mixin: 재사용 가능한 여러 가지 기능을 담고 있는 클래스. 여러 클래스를 섞어서 사용할 수 있도록 해준다.
ListModelMixin: 리스트 반환 API를 만들기 위해 상속받는 클래스
CreateModelMisxin: 새로운 객체를 생성하는 API를 만들기 위해 상속 받는 클래스
- ViewSets: 여러 endpoint를 한 번에 관리할 수 있는 클래스. RESTful API에서 반복되는 구조를 더 편리하게 작성할 수 있는 방법을 제공.
views.py
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status
from rest_framework.views import APIView
from django.shortcuts import get_object_or_404
from .models import Article
from .serializers import ArticleSerializer
@api_view(["GET", "PUT", "DELETE"])
def article_detail(request, pk): # 상속
pass
class ArticleListAPIView(APIView):
def get(self, request):
articles = Article.objects.all()
serializer = ArticleSerializer(articles, many=True)
return Response(serializer.data)
def post(self, request):
serializer = ArticleSerializer(data=request.data)
if serializer.is_valid(raise_exception=True):
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
class ArticleDetailAPIView(APIView):
def get(self, request, pk):
article = get_object_or_404(Article, pk=pk)
serializer = ArticleSerializer(article)
return Response(serializer.data)
def put(self, request, pk):
article = get_object_or_404(Article, pk=pk)
serializer = ArticleSerializer(article, data=request.data, partial=True)
if serializer.is_valid(raise_exception=True):
serializer.save()
return Response(serializer.data)
def delete(self, request, pk):
article = get_object_or_404(Article, pk=pk)
article.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
urls.py
from django.urls import path
from . import views
urlpatterns = [
path("", views.ArticleListAPIView.as_view(), name="article_list"),
path("<int:pk>/", views.ArticleDetailAPIView.as_view(), name="article_detail"),
]
*** as_view() 메서드를 사용해서 url패턴에 연결해야 함.