Belajar Django: DRF (List dan Create)

Untuk membuat REST di Django terdapat paket yang bisa membantu kita dalam membangun aplikasi REST API dengan cepat namanya djangorestframework (selanjutnya DRF), oleh karena itu mari kita pasang terlebih dahulu.

pip install djangorestframework

Lalu Pasang “rest_framework” di bagian installed app

INSTALLED_APPS = [
    'blog.apps.BlogConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
]

 

Ada dua hal yang bisa diperhatikan, pertama serializers dan kedua views.

Serializers

Di situsnya serializers dijelaskan seperti ini

Serializers allow complex data such as querysets and model instances to be converted to native Python datatypes that can then be easily rendered into JSON, XML or other content types. Serializers also provide deserialization, allowing parsed data to be converted back into complex types, after first validating the incoming data.

Oke, sedangkan berdasarkan pengalaman pribadi serializers itu bagi saya membantu menjembatani komunikasi dari views ke model termasuk validasi saat proses CRUD.

View

Di DRF terdapat beberapa class based views seperti APIView da genericviews, di situsnya dijelakan keuntungan menggunakan class based views seperti berikut

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.

Di tulisan saya ini saya akan menggunakan genericviews, alasannya nanti disebutkan di akhir tulisan. Oke langsung saja.

Pertama kita buat serializer terlebih dahulu, di dalam app blog buat berkas serializers.py lalu isikan sebagai berikut.

class PostSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        fields = '__all__'

Di sana saya membuat 1 buah serializer yaitu PostSerializer menggunakan serializers.Modelserializer, di sana saya “meyambungkan” serializer tersebut ke model Post.

Selanjutnya beralih ke view, di sini kita akan langsung membuat list daftar post yang telah dibuat

from rest_framework.generics import ListCreateAPIView
from .models import Post, Category
from .serializers import PostSerializer

class PostListCreate(ListCreateAPIView):
        serializer_class = PostSerializer
        queryset = Post.objects.all()

Saya menggunakan ListCreateAPIView, sesaui namanya view ini menghandle List (method GET)  dan Create (method POST).

View yang sudah dibuat kemarin kita ubah jadi seperti berikut

from rest_framework.generics import ListCreateAPIView
from .models import Post, Category
from .serializers import PostSerializer

class PostListCreate(ListCreateAPIView):
        serializer_class = PostSerializer
        queryset = Post.objects.all()

di urls.py di app blog juga diubah jadi

from django.urls import path
from .views import PostListCreate

app_name = 'blog'
urlpatterns = [
    path('', PostListCreate.as_view(), name='index'),
]

Saat kita menuju endpoint blog, dengan kode di atas akan menghasilkan respon seperti berikut

[
  {
    "id": 1,
    "title": "something",
    "content": "something",
    "publish": false,
    "created_at": "2019-05-25T20:52:21Z",
    "updated_at": "2019-05-25T20:52:21Z",
    "category_id": 1
  },
  {
    "id": 2,
    "title": "some",
    "content": "a",
    "publish": false,
    "created_at": "2019-05-25T20:52:21Z",
    "updated_at": "2019-05-25T20:52:21Z",
    "category_id": 1
  },
  {
    "id": 3,
    "title": "example",
    "content": "example",
    "publish": false,
    "created_at": "2019-05-25T20:52:21Z",
    "updated_at": "2019-05-25T20:52:21Z",
    "category_id": 1
  }
]

Ya hanya dengan 7 baris saja, butuh pencarian? Mudah, ubah kode view menjadi

from rest_framework.generics import ListCreateAPIView
from .models import Post, Category
from .serializers import PostSerializer
from rest_framework import filters

class PostListCreate(ListCreateAPIView):
        serializer_class = PostSerializer
        queryset = Post.objects.all()
        filter_backends = (filters.SearchFilter,)
        search_fields = ('title', 'content')

untuk mencoba pencarian bisa dengan url seperti berikut http://localhost:8000/?search=exam, hasilnya

[
  {
    "id": 3,
    "title": "example",
    "content": "example",
    "publish": false,
    "created_at": "2019-05-25T20:52:21Z",
    "updated_at": "2019-05-25T20:52:21Z",
    "category_id": 1
  }
]

Simple kan?
Satu lagi yaitu pagination, kita bisa implementasi pagination dengan mudah, pertama mari buat dulu satu buah file pagination.py yang nantinya akan digunakan sebagai struktur utama respon.

from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response
from rest_framework import status
class MyPagination(PageNumberPagination):
    page_size=50
    page_size_query_param='limit'
    def get_paginated_response(self, data):
        count = self.page.paginator.count
        code = status.HTTP_200_OK
        message = 'Empty'
        if count > 0:
            message = 'Success'
        return Response({
            'status': code,
            'message': message,
            'links': {
                'next': self.get_next_link(),
                'previous': self.get_previous_link(),
                'total_page': self.page.paginator.num_pages
            },
            'total': count,
            'total_on_page': '{} from {}'.format(len(data), count),
            'data': data
        },status=code)

lalu Views kita ubah jadi seperti berikut

from rest_framework.generics import ListCreateAPIView
from .models import Post, Category
from .serializers import PostSerializer
from rest_framework import filters
from .pagination import MyPagination

class PostListCreate(ListCreateAPIView):
        serializer_class = PostSerializer
        queryset = Post.objects.all()
        filter_backends = (filters.SearchFilter,)
        search_fields = ('title', 'content')
        pagination_class = MyPagination

lalu kita kunjungi lagi endpoint nya

{
  "status": 200,
  "message": "Success",
  "links": {
    "next": null,
    "previous": null,
    "total_page": 1
  },
  "total": 3,
  "total_on_page": "3 from 3",
  "data": [
    {
      "id": 1,
      "title": "something",
      "content": "something",
      "publish": false,
      "created_at": "2019-05-25T20:52:21Z",
      "updated_at": "2019-05-25T20:52:21Z",
      "category_id": 1
    },
    {
      "id": 2,
      "title": "some",
      "content": "a",
      "publish": false,
      "created_at": "2019-05-25T20:52:21Z",
      "updated_at": "2019-05-25T20:52:21Z",
      "category_id": 1
    },
    {
      "id": 3,
      "title": "example",
      "content": "example",
      "publish": false,
      "created_at": "2019-05-25T20:52:21Z",
      "updated_at": "2019-05-25T20:52:21Z",
      "category_id": 1
    }
  ]
}

Kita juga bisa mengubah limit secara dinamis dengan bantuan query_param http://localhost:8000/?limit=2
hasilnya

{
  "status": 200,
  "message": "Success",
  "links": {
    "next": "http://localhost:8000/?limit=2&page=2",
    "previous": null,
    "total_page": 2
  },
  "total": 3,
  "total_on_page": "2 from 3",
  "data": [
    {
      "id": 1,
      "title": "something",
      "content": "something",
      "publish": false,
      "created_at": "2019-05-25T20:52:21Z",
      "updated_at": "2019-05-25T20:52:21Z",
      "category_id": 1
    },
    {
      "id": 2,
      "title": "some",
      "content": "a",
      "publish": false,
      "created_at": "2019-05-25T20:52:21Z",
      "updated_at": "2019-05-25T20:52:21Z",
      "category_id": 1
    }
  ]
}

ombinasikan dengan pencarian? Tambahkan saja `query param` search http://localhost:8000/?search=exam&limit=2

{
  "status": 200,
  "message": "Success",
  "links": {
    "next": null,
    "previous": null,
    "total_page": 1
  },
  "total": 1,
  "total_on_page": "1 from 1",
  "data": [
    {
      "id": 3,
      "title": "example",
      "content": "example",
      "publish": false,
      "created_at": "2019-05-25T20:52:21Z",
      "updated_at": "2019-05-25T20:52:21Z",
      "category_id": 1
    }
  ]
}

Mengubah response
Di respon di atas terdapat key category_id berupa integer, sungguh tidak manusiawi, agar lebih manusiawi kita bisa ubah responnya agar respon post menampilkan category yang bisa dibaca manusiawi

Buka serializer dan ubah menjadis eperti berikut

class PostSerializer(serializers.ModelSerializer):
    def to_representation(self, instance):
        data = super().to_representation(instance)
        if data['category_id']:
            objects = Category.objects.get(pk=data['category_id'])
            data['category_id'] = {
                "id":objects.id,
                "title":objects.title
            }
        return data

    class Meta:
        model = Post
        fields = '__all__'

Respon berubah menjadi

{
  "status": 200,
  "message": "Success",
  "links": {
    "next": null,
    "previous": null,
    "total_page": 1
  },
  "total": 1,
  "total_on_page": "1 from 1",
  "data": [
    {
      "id": 3,
      "title": "Example",
      "content": "example",
      "publish": false,
      "created_at": "2019-05-25T20:00:00Z",
      "updated_at": "2019-05-25T20:00:00Z",
      "category_id": {
        "id": 1,
        "title": "Cat 1"
      }
    }
  ]
}

Untuk create? di sini magic paling ajaib, tak perlu ngoding kita tainggal buat method post aja seperti berikut
hasilnya

Pun dengan validasi

 

Untuk membuat list, pencarian, pagination dan create dengan generics view cukup mudah kan?

 

Referensi: https://www.django-rest-framework.org/