Featured image of post Serializer Dasar pada Django REST Framework

Serializer Dasar pada Django REST Framework

Memahami apa itu serializer, kenapa serializer penting, perbedaan Serializer dan ModelSerializer, serta alur serialization/deserialization di DRF.

Serializer Dasar pada Django REST Framework (Apa, Kenapa, Serializer vs ModelSerializer, Pipeline Serialization/Deserialization)

Di materi sebelumnya, kita sudah memahami, membuat model dan migrations. Sekarang kita masuk ke layer yang membuat model-model tersebut benar-benar bisa dipakai di API.

Serializer adalah kontrak antara backend kita dan konsumen dari API:

  • Arah keluar (outgoing): object Python diubah menjadi data yang JSON-ready.
  • Arah masuk (incoming): input JSON diubah menjadi data Python yang tervalidasi (dan sering kali menjadi instance model).

Tanpa serializer, API kita tidak punya batas validasi yang handal dan tidak punya bentuk data yang jelas.


Apa Itu Serializer

Di Django REST Framework, serializer menangani dua pekerjaan inti:

  1. Serialization Mengubah model instance atau queryset menjadi tipe data Python primitif (dict, list, string, number), yang kemudian bisa di-render DRF sebagai JSON.
  2. Deserialization Melakukan parse dan memvalidasi data yang masuk, lalu mengekspose data yang bersih melalui validated_data. Secara opsional, serializer juga bisa membuat atau mengupdate model instance lewat save().

Bayangkan serializer sebagai Django Forms di sisi API, yang didesain untuk JSON API.


Kenapa Serializer Penting dalam API

Serializer bukan sekadar formalitas yang bersifat opsional. Serializer melindungi API kita.

Serializer memberi kita:

  • Validasi input yang dekat dengan batasan sisi dari API
  • Bentuk response yang konsisten
  • Kontrol jelas untuk field read-only dan write-only
  • Keamanan yang lebih baik karena hanya field yang memang diizinkan yang diekspos
  • Error message yang bisa dipakai client untuk memperbaiki request

Kalau kita tidak melalui serializer dan parse request.data secara manual di views, codebase kita akan cepat jadi rapuh dan repetitif.


Serializer vs ModelSerializer

Keduanya berguna. Pilih sesuai konteks.

TipeCocok UntukTradeoff
SerializerPayload custom, data non-model, aturan validasi yang tidak biasaDefinisi field lebih manual
ModelSerializerAPI CRUD umum pada model Djangomodel-driven

Plain Serializer (field manual)

Kita deklarasikan semua field sendiri.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
from rest_framework import serializers


class TaskPreviewSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    title = serializers.CharField(max_length=120)
    status = serializers.CharField(max_length=20)
    created_at = serializers.DateTimeField(read_only=True)

    def validate_title(self, value):
        if len(value.strip()) < 3:
            raise serializers.ValidationError("Title must be at least 3 characters.")
        return value

ModelSerializer (field berdasarkan model)

DRF akan memetakan field model secara otomatis.

1
2
3
4
5
6
7
8
9
from rest_framework import serializers
from migration_lab.models import LabTask


class LabTaskSerializer(serializers.ModelSerializer):
    class Meta:
        model = LabTask
        fields = ["id", "title", "status", "created_at"]
        read_only_fields = ["id", "created_at"]

Aturan Umumnya:

  • Mulai dengan ModelSerializer untuk CRUD model normal.
  • Gunakan Serializer saat field request/response tidak memetakan langsung ke satu model.

Pipeline Serialization dan Deserialization

Outgoing pipeline (alur response)

Ini adalah alur outgoing pipeline. Artinya DRF menyajikan data response ke client.

1
2
3
4
5
6
Model instance/queryset
-> Serializer(instance=...)
-> serializer.data (data Python primitif)
-> Response(serializer.data)
-> JSONRenderer
-> HTTP JSON response

Incoming pipeline (alur request)

Ini adalah alur incoming pipeline. Artinya DRF menerima request data dari client, memvalidasi data tersebut, lalu mengubahnya menjadi data Python yang bersih. Jika validasi lolos, DRF bisa membuat atau mengupdate model instance; jika gagal, DRF mengembalikan validation errors.

1
2
3
4
5
6
7
Incoming JSON
-> request.data
-> Serializer(data=request.data)
-> serializer.is_valid()
-> serializer.validated_data
-> serializer.save()   (opsional create/update)
-> model instance

Detail penting:

  • serializer.data digunakan untuk representasi output.
  • serializer.validated_data digunakan untuk input yang sudah bersih setelah validasi.

Jangan gunakan serializer.data seakan-akan itu input yang sudah divalidasi.


Lab dengan App Baru (serializer_lab)

Di bagian ini, kita akan menggunakan app baru agar praktik serializer terisolasi dari lab sebelumnya. Semua hal dari Bagian 5 dan seterusnya akan memakai app yang sama ini.

Buat app dan daftarkan

1
2
3
cd my-drf-project
source .venv/bin/activate
python manage.py startapp serializer_lab

Tambahkan 'serializer_lab' ke INSTALLED_APPS di config/settings.py.

Buat model di serializer_lab/models.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
from django.db import models


class TodoItem(models.Model):
    class Priority(models.TextChoices):
        LOW = "low", "Low"
        MEDIUM = "medium", "Medium"
        HIGH = "high", "High"

    title = models.CharField(max_length=120)
    description = models.TextField(blank=True)
    priority = models.CharField(
        max_length=10,
        choices=Priority.choices,
        default=Priority.MEDIUM,
    )
    is_done = models.BooleanField(default=False)
    due_date = models.DateField(null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ["-created_at"]

    def __str__(self):
        return self.title

Buat dan apply migrations

1
2
python manage.py makemigrations serializer_lab
python manage.py migrate

Tambahkan sample data yang cukup ke database

Buka shell:

1
python manage.py shell

Jalankan script persis seperti ini:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
from datetime import date, timedelta
from serializer_lab.models import TodoItem

TodoItem.objects.all().delete()

today = date.today()

TodoItem.objects.bulk_create([
    TodoItem(title="Set up serializer_lab app", priority="high", due_date=today),
    TodoItem(title="Write serializer basics notes", priority="medium", due_date=today + timedelta(days=1)),
    TodoItem(title="Create API payload examples", priority="high", due_date=today + timedelta(days=2)),
    TodoItem(title="Review validation errors", priority="medium", due_date=today + timedelta(days=3)),
    TodoItem(title="Practice queryset serialization", priority="low", due_date=today + timedelta(days=4)),
    TodoItem(title="Refactor lesson wording", priority="low", due_date=today + timedelta(days=5)),
])

print(TodoItem.objects.count())
print(list(TodoItem.objects.values_list("title", "priority", "is_done")))

Hasil yang diharapkan:

  • Count harus 6.
  • Kita harus melihat enam baris data dengan prioritas yang beragam.

Buat serializer_lab/serializers.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
from rest_framework import serializers
from .models import TodoItem


class TodoItemSerializer(serializers.ModelSerializer):
    class Meta:
        model = TodoItem
        fields = ["id", "title", "description", "priority", "is_done", "due_date", "created_at"]
        read_only_fields = ["id", "created_at"]

    def validate_title(self, value):
        value = value.strip()
        if len(value) < 3:
            raise serializers.ValidationError("Title must be at least 3 characters.")
        return value

Uji outgoing pipeline (serialization)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from serializer_lab.models import TodoItem
from serializer_lab.serializers import TodoItemSerializer

one_item = TodoItem.objects.first()
single = TodoItemSerializer(one_item)
print(single.data)

all_items = TodoItem.objects.all()
many = TodoItemSerializer(all_items, many=True)
print(len(many.data))
print(many.data[0])

Hasil yang diharapkan:

  • len(many.data) harus 6.
  • Setiap object hasil serialization harus berisi: id, title, description, priority, is_done, due_date, created_at.

Uji incoming pipeline (deserialization + validation)

Payload valid:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
from datetime import date, timedelta

today = date.today()

valid_payload = {
    "title": "Send weekly progress update",
    "description": "Share serializer lab progress",
    "priority": "medium",
    "is_done": False,
    "due_date": str(today + timedelta(days=7)),
}

ser = TodoItemSerializer(data=valid_payload)
print(ser.is_valid()) #True
print(ser.errors) #{}

new_item = ser.save()
print(new_item.id, new_item.title, new_item.priority) #7 Send weekly progress update medium

Contoh payload tidak valid:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
bad_title_payload = {
    "title": "a",
    "description": "Too short title",
    "priority": "medium",
}

bad_priority_payload = {
    "title": "Valid title",
    "description": "Invalid priority value",
    "priority": "urgent",
}

bad_title = TodoItemSerializer(data=bad_title_payload)
print(bad_title.is_valid()) #False
print(bad_title.errors) #Title must be at least 3 characters

bad_priority = TodoItemSerializer(data=bad_priority_payload)
print(bad_priority.is_valid()) #False
print(bad_priority.errors) #"urgent" is not a valid choice

Atribut dan Method Serializer yang Penting (Menggunakan serializer_lab)

  • is_valid() Menjalankan validasi dan mengisi errors atau validated_data.
  • validated_data Input bersih setelah validasi berhasil.
  • errors Dictionary error ketika validasi gagal.
  • data Representasi output untuk API response.
  • save() Memanggil create() atau update() tergantung penggunaan serializer.
  • many=True Wajib saat melakukan serialization queryset/list.

Contoh yang konkret:

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

# 1) Queryset serialization wajib many=True
items = TodoItem.objects.all()
ser_many = TodoItemSerializer(items, many=True)
print(len(ser_many.data))

# 2) Alur input tervalidasi
payload = {"title": "Practice partial updates", "priority": "low"}
ser_in = TodoItemSerializer(data=payload)
ser_in.is_valid(raise_exception=True)
print(ser_in.validated_data)
created = ser_in.save()

# 3) Alur partial update
update_ser = TodoItemSerializer(created, data={"is_done": True}, partial=True)
update_ser.is_valid(raise_exception=True)
updated = update_ser.save()
print(updated.is_done)

Kesalahan Yang Sering Terjadi

  • Menaruh kode serializer di models.py, bukan di serializers.py.
  • Lupa many=True saat melakukan serialization TodoItem.objects.all().
  • Memanggil save() sebelum is_valid().
  • Menganggap validasi frontend sudah cukup lalu melewati validasi serializer di backend.
  • Lupa read_only_fields sehingga client tidak sengaja diizinkan mengirim field yang seharusnya dikelola server.
  • Tertukar antara serializer.data (output) dan validated_data (input bersih).

Penutup

Kita bisa menandai “Serializer Basics” sebagai selesai jika kita bisa menjelaskan:

  1. Kenapa serializer ada di DRF
  2. Perbedaan antara Serializer dan ModelSerializer
  3. Pipeline request dan response secara utuh
  4. Perbedaan antara data, validated_data, dan errors
  5. Bagaimana is_valid() dan save() bekerja bersama

Foto cover oleh purzlbaum dari Unsplash

Dibawah Lisensi CC BY-NC-SA 4.0
Dibangun dengan Hugo
Tema Stack dirancang oleh Jimmy