TIL122. DRF : FBV와 CBV로 CRUD 구현하기

ID짱재·2022년 2월 15일
0

Django REST Framework

목록 보기
6/10
post-thumbnail

📌 이 포스팅에서는 DRF의 Function Based View와 Class Based View를 활용하여, 같은 기능의 CRUD를 구현해보았습니다.



🌈 FBV와 CBV로 CRUD 구현하기

🔥 Simple Settings

🔥 Serializer 작성

🔥 FBV로 CRUD 구현

🔥 CBV로 CRUD 구현



1. Simple Settings

🤔 settings.py

✔️ 가상환경 내에 아래와 같이 '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', 
]

🤔 Models.py

✔️ 학생의 이름과 점수를 등록하는 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}" 

🤔 URL 경로 세팅

✔️ 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를 참조    
]


2. Serializer 작성

🤔 serializers.py

✔️ 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 지정


3. FBV로 CRUD 구현

🤔 list 제공 및 객체 생성을 위한 student_list 구현

✔️ 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)

🤔 상세 정보, 수정, 삭제를 위한 student_detail 구현

✔️ 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)

🤔 App urls.py 작성

✔️ 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),
]


4. CBV로 CRUD 구현

🤔 list 제공 및 객체 생성을 위한 StudentList 구현

✔️ 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)

🤔 상세 정보, 수정, 삭제를 위한 StudentDetail 구현

✔️ 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) 

🤔 App urls.py 작성

✔️ 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()),
]
profile
Keep Going, Keep Coding!

0개의 댓글