📌 이 포스팅에서는 DRF의 Function Based View와 Class Based View를 활용하여, 같은 기능의 CRUD를 구현해보았습니다.
🔥 Simple Settings
🔥 Serializer 작성
🔥 FBV로 CRUD 구현
🔥 CBV로 CRUD 구현
✔️ 가상환경 내에 아래와 같이 'djangorestframework'를 설치하고, requirements.py 등록한다.
$ > pip install djangorestframework
✔️ settings.py에서는 'rest_framework'와 App(fbvApp, cbvAp)을 등록해준다.
✔️ fbvAPP에서의 Student 모델과, StudentSerializer를 활용하여 FBV로 CRUD를 구현하고, cbvAPP에서는 이를 활용하여 CBV로 CRUD를 구현해보기 위해 App을 분리했다.
INSTALLED_APPS = [
......
......
'rest_framework', # 👈 설치한 djangorestframework 등록
'fbvApp',
'cbvApp',
]
✔️ 학생의 이름과 점수를 등록하는 Model을 간단히 작성한다. 앞으로 CBV에서도 이 Model을 재활용 하겠다.
✔️ id를 직접 명시하려면, primary_key를 True값로 주어야 한다.
✔️ Model 작성 후, migration은 잊지 말자.
from django.db import models # Student Model class Student(models.Model): id = models.IntegerField(primary_key=True) name = models.CharField(max_length=20) score = models.DecimalField(max_digits=10, decimal_places=3) def __str__(self): return f"{self.id} : {self.name}"
✔️ root url은 inlcude를 추가하여, 해당 앱에서 url을 관리할 수 있도록 설정한다.
from django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('fbvApp/', include('fbvApp.urls')), # 👈 include로 fbvApp.urls를 참조 path('cbvApp/', include('cbvApp.urls')), # 👈 include로 cbvApp.urls를 참조 ]
✔️ serializers는 Django에서 제공하는 form과 유사하다. 유효성 검사를 진행하기 때문이다.
✔️ Form에서 Form과 ModelForm을 상속받는 것처럼 serializers에서는 serializer 또는 ModelSerializer를 상속받는다.
✔️ Serializer는 데이터를 직렬화해주는 기능도 포함하기 때문에 편리하게 API 서버 구축할 수 있다.
from rest_framework import serializers # 👈 serializers를 가져온다. from fbvApp.models import Student # StudentSerializer class StudentSerializer(serializers.ModelSerializer): class Meta: model = Student # 👈 Model 지정 fields = ['id', 'name', 'score'] # 👈 모델의 field 지정
✔️ list제공하는 View에서는 GET요청으로 학생에 대한 목록을 제공하거나, POST요청일 경우 학생 객체를 생성한다.
✔️ FBV로 API요청을 받기 위해서는 api_view 데코레이션이 필요하다. 요청으로 받을 HTTP method를 list안에 작성한다.
✔️ 또한 Response를 이용해서 직렬화한 데이터를 손쉽게 클라이언트로 제공할 수 있다. 이 때, 응답코드로 status를 활용한다.
✔️ serializer의 errors를 사용하면, 유효성 검사 중 발생한 error를 응답코드와 전달할 수 있다.
from rest_framework.response import Response # 👈 Response import from rest_framework import status # 👈 응답코드를 위해 status import from rest_framework.decorators import api_view # 👈 api_view import from fbvApp.models import Student from fbvApp.serializers import StudentSerializer # student_list @api_view(['GET', 'POST']) # 👈 api_view 데코레이션 사용 def student_list(request): if request.method == 'GET': student = Student.objects.all() serializer = StudentSerializer(student, many=True) # 👈 객체가 다수일 경우, Many=True 옵션을 활성화한다. return Response(serializer.data) elif request.method == 'POST': serializer = StudentSerializer(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)
✔️ GET요청으로 상세정보를 응답하고, PUT으로 수정, DELETE요청으로 삭제하기 위해 student_detail은 해당 객체를 지정해야하기 때문에 pk값이 필요하다.
✔️ 우선 try..except 구문으로 해당 객체를 가져오고, 없다면 DoesNotExists 에러를 반환시킨다.
✔️ 해당 객체를 가져오는데 문제가 없다면, method에 따라 로직을 처리한다.
✔️ 수정 요청을 처리할 때는 StudentSerializer에 해당 object와 수정할 데이터인 request.date를 인자로 전달한다.
# student_detail @api_view(['GET', 'PUT', 'DELETE']) def student_detail(request, pk): try: student = Student.objects.get(pk=pk) except Student.DoesNotExist: # 👈 해당 객체가 없으면 Error 발생 return Response(status=status.HTTP_404_NOT_FOUND) if request.method == 'GET': # 👈 상세 보기 serializer = StudentSerializer(student) return Response(serializer.data) elif request.method == 'PUT': # 👈 수정 serializer = StudentSerializer(student, request.data) # 👈 instance와 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': student.delete() return Response(status=status.HTTP_204_NO_CONTENT)
✔️ views.py에서 작성한 2개의 api를 url와 연결시킨다.
from django.urls import path from . import views urlpatterns = [ path('students/', views.student_list), path('students/<int:pk>/', views.student_detail), ]
✔️ rest_framework에서 제공하는 가장 기본 View는 APIView다.
✔️ CBV로 구현하면 api_view 데코레이션이 필요하지 않다. 상속받은 APIView Class 내부에 HTTP 매서드를 이미 갖고 있다.
from django.http import Http404 from rest_framework.response import Response from rest_framework import status from rest_framework.views import APIView # 👈 APIView import from fbvApp.models import Student # 👈 이미 만든 Student 모델 재사용 from fbvApp.serializers import StudentSerializer # 👈 이미 만든 StudentSerializer 재사용 # StudentList class StudentList(APIView): def get(self, request): students = Student.objects.all() serializer = StudentSerializer(students, many=True) return Response(serializer.data) def post(self, request): serializer = StudentSerializer(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)
✔️ pk값을 이용하여 해당 객체를 가져올 때는 get_object 함수를 사용해서 같은 코드를 반복하지 않을 수 있다.
✔️ get_object는 해당 pk값의 객체가 모델 내에 있을 경우, 해당 객체를 반환하지만 없다면 DoesNotExist 에러를 발생시킨다.
✔️ 따라서 get, put, delete 매서드에서는 get_object 함수를 호출하여 객체를 반환받아 처리한다.
✔️ CBV로 구현해도 로직에는 큰 차이가 없지만, HTTP 매서드가 함수와 매핑되기 때문에 보다 간결한 느낌을 준다.
class StudentDetail(APIView): def get_object(self, pk): try: return Student.objects.get(pk=pk) except Student.DoesNotExist: raise Http404 def get(self, request, pk): student = self.get_object(pk=pk) serializer = StudentSerializer(student) return Response(serializer.data) def put(self, request, pk): student = self.get_object(pk=pk) serializer = StudentSerializer(student, request.data) if serializer.is_valid(): serializer.save() return Response(serializer.data) return Response(serializer.erros, status=status.HTTP_400_BAD_REQUEST) def delete(self, request, pk): student = self.get_object(pk=pk) student.delete() return Response(status=status.HTTP_204_NO_CONTENT)
✔️ views.py에서 작성한 2개의 api를 url와 연결시킨다.
from django.urls import path from . import views urlpatterns = [ path('students/', views.StudentList.as_view()), path('students/<int:pk>/', views.StudentDetail.as_view()), ]