4 분 소요

GitHub

RESTful API

  • REST(Representational State Transfer)는 월드 와이드 웹과 같은 분산 하이퍼미디어 시스템을 위한 소프트웨어 아키텍처의 한 형식이다. 이 용어는 로이 필딩(Roy Fielding)의 2000년 박사학위 논문에서 소개되었다. (위키백과)
  • 자세한 내용은 위키백과, TOAST Meetup - REST API 제대로 알고 사용하기를 참고해주세요.

Django Rest Framework

djangorestframework를 이용한 RESTful API 서버를 만들어봅니다.
Django REST framework의 Tutorial을 참고해 만들어보는 메모입니다.

Environments

  • macOS 10.13.3
  • Python 3.6.5

Install python

Python3을 사용하겠습니다.

$ brew install python3

Virtual Environments

프로젝트별 독립적인 황경을 위해 Python에서는 virtualenv를 이용해 가상 환경을 만들어 줍니다.

$ pip3 install virtualenv
$ virtualenv env # 가상 환경 생성
$ source env/bin/activate # 가상 환경 활성화

Install frameworks

$ pip3 install django
$ pip3 install djangorestframework

Create project

가상 환경의 django-admin.py를 이용해 “djangotutorial”이라는 프로젝트를 생성합니다.

$ django-admin.py startproject project

Create app

$ cd project
$ python3 manage.py startapp app

어플리케이션이 생성되었고 프로젝트의 설정을 변경해보겠습니다.
project/project/settings.pyINSTALLED_APPS 에 아래처럼 추가해줍니다.

1
2
3
4
5
INSTALLED_APPS = [
    ...
    'rest_framework',
    'app.apps.AppConfig'
]

Models

어플리케이션에서 사용할 모델을 만들어야 합니다.
장고 모델에 대한 설명은 djangogirls의 장고 모델을 참고해주세요.
project/app/models.py 를 아래와 같이 수정합니다.

1
2
3
4
5
6
7
8
from django.db import models

class Memo(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()
    created = models.DateTimeField(auto_now_add=True)
    class Meta:
        ordering = ('created',)

Migrate

$ python3 manage.py makemigrations app
$ python3 manage.py migrate

project/db.sqlite3 이 만들어졌습니다.

Serializers

project/app/serializers.py 를 추가하고 아래의 내용을 입력합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from app.models import Memo
from rest_framework import serializers

class MemoSerializer(serializers.HyperlinkedModelSerializer):
    id = serializers.IntegerField(read_only=True)
    title = serializers.CharField(max_length=100)
    content = serializers.CharField()
    created = serializers.ReadOnlyField()

    def create(self, validated_data):
        return Memo.objects.create(**validated_data)
    def update(self, instance, validated_data):
        instance.title = validated_data.get('title', instance.title)
        instance.content = validated_data.get('content', instance.content)
        instance.save()
        return instance

    class Meta:
        model = Memo
        fields = ('id', 'title', 'content', 'created')

Test

$ python3 manage.py shell
>>> from app.models import Memo
>>> from app.serializers import MemoSerializer
>>> memo = Memo(title='TITLE', content='CONTENT')
>>> memo.save()
>>> serializer = MemoSerializer(memo)
>>> serializer.data
{'id': 1, 'title': 'TITLE', 'content': 'CONTENT'}

Views

project/app/views.py 가 HTTP Method를 이용해 메모의 조회, 추가, 수정, 삭제를 할 수 있도록 아래의 내용으로 수정합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
from app.models import Memo
from app.serializers import MemoSerializer
from django.http import Http404
from rest_framework import status
from rest_framework.views import APIView
from rest_framework.response import Response

class MemoList(APIView):
    def get(self, request, format=None):
        memo = Memo.objects.all()
        serializer = MemoSerializer(memo, many=True)
        return Response(serializer.data)

    def post(self, request, format=None):
        serializer = MemoSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.error, status=status.HTTP_400_BAD_REQUEST)

class MemoDetail(APIView):
    def get_object(self, pk):
        try:
            return Memo.objects.get(pk=pk)
        except Memo.DoesNotExist:
            return Http404

    def get(self, request, pk, format=None):
        memo = self.get_object(pk)
        serializer = MemoSerializer(memo)
        return Response(serializer)

    def put(self, request, pk, format=None):
        memo = self.get_object(pk)
        serializer = MemoSerializer(memo, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.error, status=status.HTTP_400_BAD_REQUEST)

    def delete(self, request, pk, format=None):
        memo = self.get_object(pk)
        memo.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

format=None 은 URL의 접미사가 없다는 것을 의미합니다.

restframework의 mixins를 이용해 더 쉽게 구현해낼 수 있습니다.
아래는 mixins를 이용한 views.py입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from app.models import Memo
from app.serializers import MemoSerializer
from rest_framework import mixins, generics

class MemoList(mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView):
    queryset = Memo.objects.all()
    serializer_class = MemoSerializer

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)

class MemoDetail(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, generics.GenericAPIView):
    queryset = Memo.objects.all()
    serializer_class = MemoSerializer

    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)

generics를 이용해 더 간결화할 수 있습니다.

1
2
3
4
5
6
7
class MemoList(generics.ListCreateAPIView):
    queryset = Memo.objects.all()
    serializer_class = MemoSerializer

class MemoDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Memo.objects.all()
    serializer_class = MemoSerializer

URLs

Django의 URL은 정규표현식(regular expressions)를 사용합니다.
추가적인 내용은 djangogirls의 장고 urls를 참고해주세요.
Django REST framework의 Tutorial에는 URL의 마지막에 ‘/’를 사용하지만 URI의 마지막 문자로 ‘/’를 포함하지 말라는 RESTful API의 URI 설계시 주의점을 지키기 위해 수정했습니다.

  • project/app/urls.py
1
2
3
4
5
6
7
8
9
10
from django.conf.urls import url
from rest_framework.urlpatterns import format_suffix_patterns
from app import views

urlpatterns = [
    url(r'^memo$', views.MemoList.as_view()),
    url(r'^memo/(?P<pk>[0-9]+)$', views.MemoDetail.as_view()),
]

urlpatterns = format_suffix_patterns(urlpatterns)

format_suffix_patterns

  • project/urls.py
1
2
3
4
5
6
7
8
9
from django.contrib import admin
from django.urls import path

from django.conf.urls import url, include

urlpatterns = [
    path('admin/', admin.site.urls),
    url(r'^', include('app.urls'))
]

Execution

$ python3 manage.py runserver

  • GET
    curl -XGET http://127.0.0.1:8000/memo
    image
    curl -XGET http://127.0.0.1:8000/memo/1
    image

  • POST
    curl -XPOST http://127.0.0.1:8000/memo -H 'content-type: application/json' -d '{"title": "SECOND TITLE", "content": "SECOND CONTENT"}'
    image

  • PUT
    curl -XPUT http://127.0.0.1:8000/memo/1 -H 'content-type: application/json' -d '{"title": "FIRST TITLE", "content": "FIRST CONTENT"}'
    image

  • DELETE
    curl -XDELETE http://127.0.0.1:8000/memo/2 image

카테고리:

업데이트:

댓글남기기