[Django/DB] Django REST framework - Single Model

문지은·2023년 5월 14일
0

Django + Database

목록 보기
10/12
post-thumbnail

단일 모델의 data를 Serialization 하여 JSON으로 변환하는 방법에 대해 알아보자.

사전 준비

Postman 설치

app 등록, urls 작성

# articles/urls.py

urlpatterns = [
    path('articles/', views.article_list),
]
# drf/urls.py

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/v1/', include('articles.urls')),
]

view 임시 작성

# articles/views.py

def article_list(request):
    pass

모델 작성 및 migration 진행

class Article(models.Model):
    title = models.CharField(max_length=10)
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
$ python manage.py migrate

fixture 데이터 load

$ python manage.py loaddata articles.json

DRF 설치, 등록 및 패키지 목록 업데이트

$ pip install djangorestframework
# settings.py

INSTALLED_APPS = [
    'articles',
    'rest_framework',  # DRF 등록
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

ModelSerializer

  • articles/serializers.py 생성
    • 위치나 파일명은 자유롭게 작성 가능
  • ModelSerializer 작성
# articles/serializers.py

from rest_framework import serializers
from .models import Article

class ArticleListSerializer(serializers.ModelSerializer):
    class Meta:
        model = Article
        fields = ('id', 'title', 'content')
  • ModelSerializer 클래스는 모델 필드에 해당하는 필드가 있는 Serializer 클래스를 자동으로 만들 수 있는 shortcut 제공
    1. Model 정보에 맞춰 자동으로 필드를 생성
    2. serializer에 대한 유효성 검사기를 자동으로 생성
    3. .create().update()의 간단한 기본 구현 포함됨

Serializer 연습

  • shell_plus 실행 및 ArticleListSerializer import
$ python manage.py shell_plus

from articles.serializers import ArticleListSerializer
  • 인스턴스 구조 확인
>>> serializer 
ArticleListSerializer():
    id = IntegerField(label='ID', read_only=True)
    title = CharField(max_length=10)
    content = CharField(style={'base_template': 'textarea.html'})
  • Model instance 객체 serialize
>>> article = Article.objects.get(pk=1)

>>> serializer = ArticleListSerializer()

>>> serializer
ArticleListSerializer():
    id = IntegerField(label='ID', read_only=True)
    title = CharField(max_length=10)
    content = CharField(style={'base_template': 'textarea.html'})

# serialized data 조회
>>> serializer.data
{'id': 1, 'title': 'Summer region chance benefit avoid alone.', 'content': 'Try country perform increase hotel. Why shake grow poor for moment hit.\nHouse town week gun window but benefit.'}y country perform increase hotel. Why shake grow poor for moment hit.\nHouse town week gun window but benefit.'}
  • QuerySet 객체 serialize
    • 단일 객체 인스턴스 대신 QuerySet 또는 객체 목록을 serialize 하려면 many=True를 작성해야 함
articles = Article.objects.all()

# many = True 옵션 X
>>> serializer = ArticleListSerializer(articles)
>>> serializer.data
AttributeError: Got AttributeError when attempting to get a value for field `title` on serializer `ArticleListSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on 
the `QuerySet` instance.
Original exception text was: 'QuerySet' object has no attribute 'title'.

# many=True 옵션 O
>>> serializer = ArticleListSerializer(articles, many=True)
>>> serializer.data
[OrderedDict([('id', 1), ('title', 'Summer region chance benefit avoid alone.'), ('content', 'Try country perform increase hotel. Why shake grow poor for moment hit.\nHouse town week gun window but benefit.')]), ...]

Build RESTful API - Article

URL과 HTTP requests method 설계

GET - List

  • 게시글 데이터 목록 조회하기
  • DRF에서 api_view 데코레이터 작성 필수
# articles/urls.py

from django.urls import path
from articles import views

urlpatterns = [
    path('articles/', views.article_list),
]
# articles/views.py

from django.shortcuts import render
from rest_framework.decorators import api_view
from rest_framework.response import Response

from .models import Article
from .serializers import ArticleListSerializer

# Create your views here.
@api_view(['GET'])
def article_list(request):
    articles = Article.objects.all()
    serializer = ArticleListSerializer(articles, many=True)
    return Response(serializer.data)

api_view decorator

  • DRF view 함수가 응답해야 하는 HTTP 메서드 목록을 받음
  • 기본적으로 GET 메서드만 허용되며 다른 메서드 요청에 대해서는 405 Method Not Allowed로 응답

GET - Detail

  • 단일 게시글 데이터 조회
  • 각 데이터의 상세 정보를 제공하는 ArticleSerializer 정의
# articles/serializer.py

from rest_framework import serializers
from .models import Article

class ArticleSerializer(serializers.ModelSerializer):
    class Meta:
        model = Article
        fields = '__all__'
  • url 및 view 함수 작성
# articles/urls.py

urlpatterns = [
    path('articles/', views.article_list),
    path('articles/<int:article_pk>/', views.article_detail),
]
# articles/views.py

from .serializers import ArticleListSerializer, ArticleSerializer

@api_view(['GET'])
def article_detail(request, article_pk):
    article = Article.objects.get(pk=article_pk)
    serializer = ArticleSerializer(article)
    return Response(serializer.data)

POST

  • 게시글 데이터 생성하기
  • 요청에 대한 데이터 생성이 성공했을 경우는 201 Created 상태 코드를 응답하고 실패했을 경우는 400 Bad request를 응답
# articles/views.py

from rest_framework import status

# Create your views here.
@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=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
  • POST 응답 확인

  • 새로 생성된 데이터 확인

Raising an exception on invalid data

  • 유효하지 않은 데이터에 대해 예외 발생시키기
  • is_valid()는 유효성 검사 오류가 있는 경우 ValidationError 예외를 발생시키는 선택적 raise_exception 인자를 사용할 수 있음
  • DRF에서 제공하는 기본 예외 처리기에 의해 자동으로 처리되며 기본적으로 HTTP 400 응답을 반환
  • view 함수 수정
# articles/views.py

@api_view(['GET', 'POST'])
def article_list(request):
    if request.method == 'GET':
        articles = Article.objects.all()
        serializer = ArticleListSerializer(articles, many=True)
        return Response(serializer.data)
    
    elif request.method == 'POST':
        serializer = ArticleListSerializer(data=request.data)
        if serializer.is_valid(raise_exception=True):
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)

DELETE

  • 게시글 데이터 삭제하기
  • 요청에 대한 데이터 삭제가 성공했을 경우는 204 No Content 상태 코드 응답
    • 명령을 수행했고 더 이상 제공할 정보가 없는 경우
# articles/views.py

@api_view(['GET', 'DELETE'])
def article_detail(request, article_pk):
	  article = Article.objects.get(pk=article_pk)

    if request.method == 'GET':
        serializer = ArticleSerializer(article)
        return Response(serializer.data)
    
    elif request.method == 'DELETE':
        article.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)
  • DELETE 응답 확인

  • 삭제한 뒤 메세지 출력하기
@api_view(['GET', 'DELETE', 'PUT'])
def article_detail(request, article_pk):
    article = get_object_or_404(Article, pk=article_pk)
    if request.method == 'GET':
        serializer = ArticleSerializer(article)
        return Response(serializer.data)
    
    elif request.method == 'DELETE':
        article.delete()
        data = {
            'delete': f'데이터 {article_pk}번 글이 삭제되었습니다.',
        }
        return Response(data, status=status.HTTP_204_NO_CONTENT)

PUT

  • 게시글 데이터 수정하기
  • 요청에 대한 데이터 수정이 성공했을 경우는 200 OK 상태 코드 응답
@api_view(['GET', 'DELETE', 'PUT'])
def article_detail(request, article_pk):
    article = Article.objects.get(pk=article_pk)

    if request.method == 'GET':
        serializer = ArticleSerializer(article)
        return Response(serializer.data)
    
    elif request.method == 'DELETE':
        article.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)
    
    elif request.method == 'PUT':
        serializer = ArticleSerializer(article, data=request.data)
        if serializer.is_valid(raise_exception=True):
            serializer.save()
            return Response(serializer.data)
  • PUT 응답 확인

📍 프로젝트 전체 코드 확인하기

profile
코드로 꿈을 펼치는 개발자의 이야기, 노력과 열정이 가득한 곳 🌈

0개의 댓글