Django REST framework (1)

Serialization, Requests & Responses, Class-based views

RESTDjango

2017-09-04


Django REST framework๋Š” ๋‹ค์–‘ํ•œ ์•ฑ์—์„œ DB์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” API endpoint๋ฅผ ๋งŒ๋“ค์–ด์ฃผ๋Š” ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค. Django REST framework Tutorial์„ ๋”ฐ๋ผํ•˜๋ฉฐ ๊ณต๋ถ€ํ•œ ๋‚ด์šฉ์„ ์ •๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ‘‰ Django REST framework (2) - Authentication & Permissions, Relationships & Hyperlinked API


1. Serializer.py

Serializing ์€ ๋ชจ๋ธ ๊ฐ์ฒด๋ฅผ Python ๋„ค์ดํ‹ฐ๋ธŒ ๋ฐ์ดํ„ฐ ํƒ€์ž…์œผ๋กœ, ๊ทธ๊ฒƒ์„ ๋‹ค์‹œ JSON์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ฒƒ์„ ๋งํ•œ๋‹ค.

ํŒŒ์ด์ฌ ์ฝ”๋“œ๋กœ ์ ‘๊ทผํ•˜๋˜ ๋ฐ์ดํ„ฐ๋ฅผ JSON์œผ๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ๋Š” ๊ฒƒ์ด ๋ฐ”๋กœ ์ด Serializer.py ์˜ ์—ญํ• ์ด๋‹ค. (์ ‘๊ทผ ๊ฐ€๋Šฅํ•œ API endpoint ๋ฅผ ๋งŒ๋“ ๋‹ค๋Š” ๊ฒƒ์€ ๋ฐ์ดํ„ฐ ํƒ€์ž…์„ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•˜๋„๋ก ๋งŒ๋“ ๋‹ค๋Š” ์˜๋ฏธ๋„ ํฌํ•จํ•˜๋Š” ๊ฒƒ ์•„๋‹๊นŒ...)

Serializer ํด๋ž˜์Šค๋Š” ์žฅ๊ณ ์˜ ํผ ํด๋ž˜์Šค์™€ ๋งค์šฐ ๋น„์Šทํ•˜๋‹ค. ํ•„๋“œ์— required, max_length, default ๊ฐ™์€ ํ”Œ๋ž˜๊ทธ๋„ ๋˜‘๊ฐ™์ด ์‚ฌ์šฉํ•œ๋‹ค.

from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser

snippet = Snippet(code='foo = "bar"\n')
snippet.save()

snippet = Snippet(code='print "hello, world"\n')
snippet.save()
serializer = SnippetSerializer(snippet)
serializer.data
# {'id': 2, 'title': u'', 'code': u'print "hello, world"\n', 'linenos': False, 'language': u'python', 'style': u'friendly'}
content = JSONRenderer().render(serializer.data)
content
# '{"id": 2, "title": "", "code": "print \\"hello, world\\"\\n", "linenos": false, "language": "python", "style": "friendly"}'

Deserializing ์€ ๊ทธ ๋ฐ˜๋Œ€์˜ ํ”„๋กœ์„ธ์Šค(JSON์—์„œ Python native data๋กœ)๋ฅผ ์˜๋ฏธํ•œ๋‹ค.

from django.utils.six import BytesIO

stream = BytesIO(content)
data = JSONParser().parse(stream)
serializer = SnippetSerializer(data=data)
serializer.is_valid()
# True
serializer.validated_data
# OrderedDict([('title', ''), ('code', 'print "hello, world"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])
serializer.save()
# <Snippet: Snippet object>
  • ** Github์—์„œ ์†Œ์Šค์ฝ”๋“œ ๋ณด๊ธฐ - serializers.py

2. Requests and Responses

  1. Request objects
  2. DRF ์˜ request.data ๋Š” ์ผ๋ฐ˜์ ์ธ HttpRequest ๋ณด๋‹ค ๋” ์œ ์—ฐ(flexible request parsing)ํ•˜๋‹ค.
request.POST  # ํผ ๋ฐ์ดํ„ฐ๋งŒ ๋‹ค๋ฃธ. POST ๋ฉ”์†Œ๋“œ๋งŒ ๊ฐ€๋Šฅ.
request.data  # ์ž„์˜ ๋ฐ์ดํ„ฐ ๋‹ค๋ฃธ. POST, PUT, PATCH ๊ฐ€๋Šฅ.
  1. Resposne objects
  2. Response๋„ JSONResponse์ธ์ง€, HttpResponse ์ธ์ง€ ๋ช…์‹œํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค. ํด๋ผ์ด์–ธํŠธ request ์—์„œ ๋“ค์–ด์˜จ content type์— ๋”ฐ๋ผ ์ž๋™์œผ๋กœ ์•Œ๋งž์€ content type์˜ response๋ฅผ ๋ฆฌํ„ดํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. (์ฐธ๊ณ  - Content negotiation) ์ด๊ฒƒ์˜ ์žฅ์ ์€ ์›น ๋ธŒ๋ผ์šฐ์ €์—์„œ ์š”์ฒญ์ด ๋“ค์–ด์™”์„ ๊ฒฝ์šฐ, response๋„ html ํ˜•์‹์œผ๋กœ ๋ธŒ๋ผ์šฐ์ €์—์„œ ๋ณผ ์ˆ˜ ์žˆ๋‹ค(browsable-api)๋Š” ์ ์ด๋‹ค.
return Response(data) # ํด๋ผ์ด์–ธํŠธ์—์„œ ์š”์ฒญ๋œ ํƒ€์ž…์œผ๋กœ ๋ Œ๋”๋ง ํ•œ๋‹ค.
  1. Status codes
  2. status ๋ชจ๋“ˆ์—์„œ๋Š” HTTP_400_BAD_REQUEST์™€ ๊ฐ™์ด ๋ช…์‹œ์ ์ธ status code๋ฅผ ์ œ๊ณตํ•œ๋‹ค. ์„ธ์ž๋ฆฌ ์ˆซ์ž๋งŒ ์‚ฌ์šฉํ•˜๋Š” status code ๋ณด๋‹ค ๋” ์ดํ•ดํ•˜๊ธฐ ์‰ฝ๋‹ค.
  3. Wrapping API Views
  4. FBV์—์„œ๋Š” @api_view ์žฅ์‹์ž๋ฅผ, CBV์—์„œ๋Š” APIView ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•ด์„œ API view๋ฅผ ๋ž˜ํ•‘ํ•˜์—ฌ ์‚ฌ์šฉํ•œ๋‹ค. Request, response, status, error ์™€ ๊ด€๋ จ๋œ ๊ธฐ๋Šฅ์ด ์ž‘๋™ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ค€๋‹ค.

3. Class Based View

  • Mixin ์‚ฌ์šฉํ•˜๊ธฐ : CRUD ๋Š” ๋ฐ˜๋ณต๋˜๋Š” ํŒจํ„ด์ด๋‹ค. DRF์—์„œ๋Š” Mixin์— ์ด๋ฅผ ๋ฏธ๋ฆฌ ๊ตฌํ˜„ํ•ด ๋†“์•˜๋‹ค. ์ด๋ฅผ CBV ์—์„œ ์ ์ ˆํžˆ ๋ถˆ๋Ÿฌ์™€ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from rest_framework import mixins
from rest_framework import generics

class SnippetList(mixins.ListModelMixin,
                  mixins.CreateModelMixin,
                  generics.GenericAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer

    # The base class provides the core functionality, and the mixin classes provide the .list() and .create() actions. We're then explicitly binding the get and post methods to the appropriate actions.

    def get(self, request, *args, **kwargs):    
        # `*args`, `**kwargs` ์—๋Š” snippet์˜ id๋‚˜ title๊ฐ™์€ ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ๋“ค์–ด๊ฐ„๋‹ค.
        return self.list(request, *args, **kwargs)

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

์—ฌ๊ธฐ์„œ ListModelMixin, CreateModelMixin ๋“ฑ์„ ํ•œ๋ฒˆ ๋” ๋ž˜ํ•‘ํ•œ generics class๋ฅผ ์“ฐ๋ฉด ์•„๋ž˜์ฒ˜๋Ÿผ ๋” ์งง์•„์ง„๋‹ค.

** Github์—์„œ ์†Œ์Šค์ฝ”๋“œ ๋ณด๊ธฐ - mixins.py, generics.py

from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from rest_framework import generics

class SnippetList(generics.ListCreateAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer

class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer

To be continued!