Prefetch pada Django adalah fungsi query milik Django untuk optimasi querying data yang berkaitan dengan relasi antar tabel, fitur ini digunakan bersama dalam metode prefetch_related, agar lebih eager loading yang bersifat reverse ataupun many to many.
Keuntungan menggunakan Prefetch adalah
1. Menentukan query yang lebih spesifik untuk data yang akan di-prefetch.
2. Mengurangi jumlah query database dengan menggabungkan data yang saling berkaitan dalam sekali perintah.
Kenapa harus Prefetch ?
Jika tanpa Prefetch, prefetch_related mengambil semua data terkait tanpa filter ataupun cusotmize, jika kita mau filter data terkait atau memberi nama atribut berbeda untuk hasil prefetch, fungsi Prefetch akan melakukannya dengan efisien.
Kapan Prefetch tepatnya dipakai ?
a. Kalau mau limit dan filter data yang diambil dari relasimya.
b. JIka perlu data yang ber relasi dengan nama atribut yang berbeda supaya tidak conflict.
c. Ketika Querying data lebih kompleks atau relasi yang spesifik.
Contoh tanpa penggunaan Prefetch, contoh databasenya adalah
class Nasabah(models.Model):
nama = models.CharField(max_length=255)
class SurveyNasabah(models.Model):
nasabah = models.OneToOneField(DataNasabah, on_delete=models.CASCADE, related_name='survey')
class DpdanTermin(models.Model):
nasabah = models.ForeignKey(Nasabah, on_delete=models.CASCADE, related_name='dpdan_termin')
jangka_waktu = models.IntegerField()
SurveyNasabah.objects.prefetch_related('nasabah__dpdan_termin')
perintah di atas, akan mengambil semua data dari dpdan_termin tanpa filter ataupun customizenya.
(nasabah__ di sini adalah nasabah nama kolom dalam model SurveyNasabah)
Sedangkan jika menggunakan Prefetch, kita bisa melakukan hal spesifik saja, misal ambil dpdan_termin dengan jangka_waktu lebih dari ('>') 12.
contoh :
from django.db.models import Prefetch
SurveyNasabah.objects.prefetch_related(Prefetch('nasabah__dpdan_termin', queryset=DpdanTermin.objects.filter(jangka_waktu__gt=12), to_attr='filtered_dpdan_termin'))
dimana queryset menyimpan nilai Query khusus yang hanya mengambil data jangka_waktu > 12 (gt singkatan dari greater than)
to_attr digunakan untuk menyimpan hasil prefetch, ke atribut, filtered_dpdan_termin di setiap instance atau nama kolom nasabah.
Keuntungan Prefetch
1. efisiensi query, jumlah query bisa lebih spesifik, ketika menggabungkan relasi yang kompleks hanya dalam 1 perintah.
2. lebih fleksibel, karena membuat querying relasi lebih spesifik dengan menyimpan hasil prefetch dengan nama yang memang lebih spesifik.
3. lebih memudahkan pengolahan data di views ataupun di template, karena data yang diambil telah difilter lebih dulu.
Apa itu Reverse relasi tabel di Django ?
Adalah ketika mengakses data dari models yang tidak secara eksplisit memiliki ForeignKey atau relasi terkait, tetapi dihubungkan melalui models lain yang memiliki relasi tersebut,
Contoh, jika models A memiliki relasi ke models B, maka models B memiliki relasi Reverse ke models A. Di sini django secara otomatis menyediakan akses ini melalui default name atau related_name yang ditentukan.
contoh code :
class DataPelanggan(models.Model):
nama = models.CharField(max_length=255)
class Produk(models.Model):
nama_produk = models.CharField(max_length=255)
dibeli_oleh = models.ForeignKey(DataPelanggan, on_delete=models.CASCADE, related_name='produks')
Arah "Forward" atau Maju
Dari models Produk, kita bisa langsung akses DataPelanggan karena ada ForeignKey di Produk.
Arah "Reverse" atau Dibalik
Dari models DataPelanggan, kita bisa akses semua Produk yang terhubung melalui related_name='produks'
contoh akses biasa, atau forward
produk = Produk.objects.all()
print(produk.dibeli_oleh.nama)
contoh mengakses secara reverse
data = DataPelanggan.objects.get(id=1)
barang = data.produks.all() #ini yang disebut dengan relasi Reverse
for item in barang:
print(item.nama_produk)
Relasi Reverse dalam QuerySet
Masalah tanpa prefetch
Ketika kita memerlukan data dari relasi reverse, setiap akses akan men-trigger tambahan ke database(N+1 query problem), contoh :
cust = DataPelanggan.objects.all()
for tamu in cust:
barang = dibeli_oleh.produks.all()
print(barang)
Solusi dengan Prefetch
gunakan select_related untuk mengambil data reverse secara efisien dalam sekali querying.
Sederhananya, Prefetch ini memang powerful untuk filtering dan manage relasi tabel apakah itu reverse ataupun ManyToMany, dan jadinya bisa lebih spesifik penggunaannya.
from django.db.models import Prefetch
customer = DataPelanggan.objects.prefetch_related(Prefetch('produks', queryset=Produk.objects.filter(nama_produk__icontains='Outdoor')))
for pelanggan in customer:
print(dibeli_oleh.produks.all()) #ini yang tidak akan memicu query tambahan
Perbandingan Reverse dan ManyToMany
1. Relasi reverse biasanya terjadi karena ForeignKey, jika dilihat contoh di atas, models DataPelanggan memiliki hubungan reverse ke semua models Produk,
melalui related_name='produks'
2. Relasi dua arah yang eksplisit, contoh
class Siswa(models.Model):
nama = models.CharField(max_length=255)
class MataPelajaran(models.Model):
nama = models.CharField(max_length=255)
siswa = models.ManyToManyField(Student, related_name='pelajaran')
Cara mengakses :
murid = Siswa.objects.get(id=1)
print(murid.pelajaran.all()) # ini adalah ManyToMany
materi = MataPelajaran.objects.get(id=1)
print(materi.siswa.all())
Sederhananya adalah relasi reverse adalah cara untuk mengakses data terkait dari arah sebaliknya (dari models target ke models asal)
yang secara otomatis dikelola oleh Django, untuk meningkatkan efisiensi bekerja saat menggunakan relasi reverse, gunakan Prefetch bersama prefetch_related.