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:
- Serialization
Mengubah model instance atau queryset menjadi tipe data Python primitif (dict, list, string, number), yang kemudian bisa di-render DRF sebagai JSON.
- 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.
| Tipe | Cocok Untuk | Tradeoff |
|---|
Serializer | Payload custom, data non-model, aturan validasi yang tidak biasa | Definisi field lebih manual |
ModelSerializer | API CRUD umum pada model Django | model-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:
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:
- Kenapa serializer ada di DRF
- Perbedaan antara
Serializer dan ModelSerializer - Pipeline request dan response secara utuh
- Perbedaan antara
data, validated_data, dan errors - Bagaimana
is_valid() dan save() bekerja bersama
Foto cover oleh purzlbaum dari Unsplash