요모조모 ʚɞ

[D.R.F] APIView로 CRUD 구현하기 본문

개발/Django RESTful API

[D.R.F] APIView로 CRUD 구현하기

Angela_OH 2021. 1. 13. 00:32

 

안녕하세요! (。・∀・)ノ゙

오늘은 Django RESTful API를 통해 CRUD 기능을 구현해보도록 하겠습니다.

view는 Class Based View를 통해 구현할 계획이며,

APIView > Mixins > Generic CBV > ViewSet 순으로 익혀보도록 하겠습니다.

위의 순서는 모두 동일한 기능(CRUD)을 하는 view의 구현 방식입니다.

차이점이라고 하면 오른쪽으로 갈수록 상속(CBV의 특성)을 활용하여,

view를 더욱 간단하게 구현할 수 있다는 특징이 있습니다.

 

우선 본격적인 view 구현에 들어가기에 앞서

Django REST framework에서 사용되는 중요 개념 중 하나인 Serializer에 대해 알아보도록 하겠습니다.

Serializer는 기존 Django에서 흔하게 활용되는 Form과 매우 유사한 개념이라고 생각하면 됩니다.

두 개념 모두 유효성 검사를 거쳐 model로부터 field를 읽어옵니다.

하지만 Form이 HTML form을 생성하는 것에 반해, Serializer는 JSON 문자열을 생성하게 됩니다.

Django Rest Framework에서는 이러한 serializer를 통해 client의 입력 데이터를 직렬화하여 전달할 수 있습니다.

 

그럼 이제 APIView를 사용하여 CRUD 기능을 구현해보도록 하겠습니다.

APIView는 원하는 HTTP method를 커스터마이징 할 수 있다는 장점이 있습니다.

하지만 그만큼 코드가 더 길어지고, 번거로워질 수 있습니다.

이 외에 자세한 내용은 아래의 공식 문서를 참고하세요!

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

 

APIView는 기본적으로는 아래와 같이 구현할 수 있습니다.

class Class_name(APIView):
	def method_name(self, request, format=None):
    	# 해당 HTTP method를 어떻게 동작시키고 처리할지 개발자가 정의!

method_name으로는 HTTP method인 get, post, delete 등이 존재합니다!

개발자는 APIView를 상속받아, 자신을 원하는 기능을 구현하기 위해 필요한 HTTP method를 구현하면 됩니다.

 

models.py

Blog라는 모델은 게시판 기능을 위해 title과 body라는 field를 주었습니다.

from django.db import models
from django.conf import settings

class Blog(models.Model):
    title = models.CharField(max_length=100)
    body = models.TextField()

 

serializer.py

serializer.py는 project라는 앱 폴더 내부에 별도로 만들어주었습니다.

해당 파일에서는 주어진 데이터를 직렬화하는 역할을 한다고 생각하면 됩니다.

저는 fields를 '__all__'로 설정하였기 때문에 model의 title과 body를 모두 직렬화할 수 있었습니다.

필요에 따라서는 fields = ['title'] 등과 같이 model의 일부만 설정할 수도 있습니다.

from .models import Blog
from rest_framework import serializers

class BlogSerializer(serializers.ModelSerializer):
    class Meta:
        model = Blog
        fields = '__all__'

 

views.py

views.py는 Blog 전체 목록을 보여주는 역할을 하는 BlogList 클래스와,

Blog 객체의 detail을 보여주는 BlogDetail 클래스 두 개를 선언하였습니다.

# 데이터 처리
from .models import Blog
from .serializers import BlogSerializer

# APIView를 사용하기 위해 import
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from django.http import Http404

# Blog의 목록을 보여주는 역할
class BlogList(APIView):
    # Blog list를 보여줄 때
    def get(self, request):
        blogs = Blog.objects.all()
        # 여러 개의 객체를 serialization하기 위해 many=True로 설정
        serializer = BlogSerializer(blogs, many=True)
        return Response(serializer.data)

    # 새로운 Blog 글을 작성할 때
    def post(self, request):
        # request.data는 사용자의 입력 데이터
        serializer = BlogSerializer(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)

# Blog의 detail을 보여주는 역할
class BlogDetail(APIView):
    # Blog 객체 가져오기
    def get_object(self, pk):
        try:
            return Blog.objects.get(pk=pk)
        except Blog.DoesNotExist:
            raise Http404
    
    # Blog의 detail 보기
    def get(self, request, pk, format=None):
        blog = self.get_object(pk)
        serializer = BlogSerializer(blog)
        return Response(serializer.data)

    # Blog 수정하기
    def put(self, request, pk, format=None):
        blog = self.get_object(pk)
        serializer = BlogSerializer(blog, data=request.data) 
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data) 
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    # Blog 삭제하기
    def delete(self, request, pk, format=None):
        blog = self.get_object(pk)
        blog.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)      
    

 

BlogList의 경우에는

1. 전체 목록 보여주기(GET) 2. 새로운 Blog 객체 등록하기(POST)

라는 2가지 HTTP method가 필요했습니다.

 

BlogDetail의 경우에는

1. Blog 객체의 detail 보여주기(GET) 2. Blog 객체 수정하기(PUT) 3. Blog 객체 삭제하기(DELETE)

라는 3가지 HTTP method가 필요합니다.

 

urls.py

마지막으로 생성한 view를 바탕으로 url을 작성해주었습니다.

해당 urls.py는 project app 폴더 내부에 파일을 따로 생성해주었습니다.

from django.urls import path
from rest_framework.urlpatterns import format_suffix_patterns
from . import views

urlpatterns =[
    path('blog/', views.BlogList.as_view()),
    path('blog/<int:pk>/', views.BlogDetail.as_view()),
]

urlpatterns = format_suffix_patterns(urlpatterns)

이후 app 폴더 내부의 url을 프로젝트 내부의 urls.py에 등록해주었습니다.

from django.contrib import admin
from django.urls import path, include
from project import urls

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('project.urls')),
]

 

이렇게 코드 작성을 마쳤다면

python manage.py makemigrations
python manage.py migrate
python manage.py runserver

새로 작성한 model을 마이그레이션 시켜주고,

웹 서버를 동작시킵니다!

 

127.0.0.1:8000

 

'127.0.0.1:8000'에 들어가 보면 위의 사진과 같이 Page not found가 뜨는데,

이것은 오류가 아니라 urls.py에서 메인 페이지에 대한 view를 별도로 등록해주지 않아서 생긴 것입니다!

'127.0.0.1:8000/blog'라는 url로 접속하면 됩니다!

 

127.0.0.1:8000/blog

 

blog 페이지에 접속하면 위에는 Blog 객체를 보여주는 GET 방식이,

아래에는 새로운 Blog 객체를 등록할 수 있는 POST 방식이 구현되어 있다는 것을 확인할 수 있습니다.

Content 란에 위의 사진과 같이 JSON 문자열을 전달하면 새로운 Blog 객체를 생성할 수 있습니다.

 

127.0.0.1:8000/blog/<int:pk>

 

생성된 Blog 객체의 pk 값을 세부 url로 전달하게 되면 위의 사진과 같이 detail 정보를 확인(GET)할 수 있습니다.

아래의 PUT 기능을 통해 내용을 수정하고, DELETE 버튼을 통해 해당 객체를 삭제할 수도 있습니다!

 

이처럼 우리는 APIView를 통해 Blog 객체를 Create, Read, Update, Delete 할 수 있었습니다.

하지만 APIView를 통해 views.py를 작성하다 보면, 이런 생각이 들 수 있습니다.

각 클래스마다 필요한 method 함수를 계속 작성해주려고 보니 특정 함수를 계속 반복해서 쓰고 있고,

코드가 길어진 것 같다는 것입니다.

따라서 다음 시간에는 이러한 코드의 반복을 줄일 수 있는 mixins 방식의 view에 대해 좀 더 알아보도록 하겠습니다!

 

Comments