Tutorial REST API Python Django Part 2 — Koneksi DB, Migrations, & Read

Mengatur koneksi ke MySQL, membuat migrations dan menampilkan data dengan format JSON.

Hudya
9 min readApr 19, 2020

Halo semua, kembali lagi bersama Kiddy untuk belajar REST API Python Django, pada part ini kita akan membahas mengenai koneksi ke DB, migrations, dan menampilkan data dengan REST API.

Kembali menggunakan project sebelumnya kita akan mengatur koneksi ke database, di tutorial saya menggunakan MySQL, jadi yang pake NoSQL mohon minggir dulu ya alias cari sendiri gimana caranya hahaha.

Oke sekarang kita masuk ke settings.py yang ada didalam folder project kita yaitu todoproject, dan cari bagian database.

Tapi jangan diganti dulu, kita akan menggunakan .env seperti biasa, biar kita bisa gampang ganti valuenya. Kita install dulu library django-environ dengan keadaan virtual environment sudah diaktifkan seperti biasa.

pip install django-environ

Kita buat dulu file .env sejajar dengan settings.py

Kalo udah kita tambahin dulu ini didalam file .env kita, sesuaikan dengan kredensial kalian dan jangan lupa dibuat databasenya ya ^^.

DEBUG=on
SECRET_KEY=your-secret-key

DB_HOST=127.0.0.1
DB_DATABASE=todolist
DB_PORT=3306
DB_USERNAME=homestead
DB_PASSWORD=secret

Udah? Oke sekarang balik lagi ke settings.py, paling atas kita import dulu.

import os
import environ

env = environ.Env(
# set casting, default value
DEBUG=(bool, False)
)
# Membaca file .env
environ.Env.read_env()

Setelahnya kita ganti bagian SECRET_KEY dan DEBUG mode-nya.

SECRET_KEY = env('SECRET_KEY')
DEBUG = env('DEBUG')

Sehingga secret keynya akan mengikuti yang kita butuhkan, sekarang ganti bagian databasenya.

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': env('DB_DATABASE'),
'USER': env('DB_USERNAME'),
'PASSWORD': env('DB_PASSWORD'),
'HOST': env('DB_HOST'),
'PORT': env('DB_PORT'),
'OPTIONS': {
# Tell MySQLdb to connect with 'utf8mb4' character set
'charset': 'utf8mb4',
},
}
}

Databasenya akan menyesuaikan yang kita setting pada .env, dan kita akan menggunakan charset utf8mb4 ketimbang utf8, tapi terserah kalian juga sih, kalau mau pake utf8 juga ngga apa.

Jalankan kembali apakah sudah bisa konek?

python manage.py runserver

Error ya? Ups, tentu saja akan error, karena kita belum menginstall library konektor ke Database.

django.core.exceptions.ImproperlyConfigured: Error loading MySQLdb module.
Did you install mysqlclient?

Nah kita ditanya, udah install library python mysqlclient belom? Kalo belom kita wajib install. Librarynya akan menggunakan mysqlclient, dan sebelum melakukan instalasi library, ada yang perlu kamu perhatikan.

Untuk kawan-kawan Linux seperti saya Ubuntu, kalian wajib install dulu kebutuhannya. Cek sendiri ya.

Untuk kawan-kawan Windows user, kalau terjadi error kalian tidak bisa menjalankan commandnya kalian perlu melakukan compile Wheel dari library itu sendiri. Wheel adalah file library seperti JAR-nya java. Jadi installnya secara offline.

Pertama download dulu disini. Sesuaikan dengan Python yang kalian gunakan, dan Bit komputer kalian, cp36 menandakan Compiler Python 3.6, cp37 menandakan Compiler Python 3.7. Jadi sesuaikan versi python kalian ya.

Ngga tau caranya gimana cek versi? Harusnya sih tau karena ini kan tutorial tingkat advanced, tidak disarankan bagi kamu yang baru pertama kali menyentuh codingan~

Cek versi Python

Silahkan copy filenya ke dalam direktori todoproject kalian ntar tinggal install deh.

pip install namafile

Untuk temen-temen Linux apabila sudah menambahkan yang dibutuhkan tinggal install aja.

pip install mysqlclient

Okeh apabila selesai kita akan lanjut, cek lagi udah berjalan dengan baik belum.

python manage.py runserver

Kalo sudah begini dan kembali bisa diakses halamannya, maka aman koneksi databasenya selesai.

Kita lanjut ke migrations, kita ubah file models.py didalam app users kita.

from datetime import datetime
from django.db import models


# Create your models here.
class Users(models.Model):
id = models.BigAutoField(primary_key=True)
name = models.CharField(max_length=200, null=True)
email = models.CharField(max_length=200, null=True, unique=True)
password = models.CharField(max_length=230, null=False)
created_at = models.DateTimeField(default=datetime.utcnow)
updated_at = models.DateTimeField(default=datetime.utcnow)

Pada dasarnya migration di sudah pasti membuatkan field id, jadi sebenernya tidak perlu dibuat, namun bedanya field id akan membuatkan integer biasa, sedangkan saya maunya membuat biginteger, jadi perlu di define ulang.

Sekarang kita perlu mengaktifkan konfiguriasi model kita, kembali ke settings.py yang ada di project todoproject dan edit bagian INSTALLED_APPS.

INSTALLED_APPS = [
'user.apps.UserConfig',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]

Disitu kita akan menambahkan class UserConfig yang ada di apps.py didalam app user.

Sekarang jalankan command berikut untuk membuat migration untuk user

python manage.py makemigrations user

Nah kalau sudah terbuat, kita bisa melihatnya di user/migrations.

Kerennya di Django adalah, kita bisa ngintip dulu migration yang kita buat udah sesuai harapan atau belum berbeda sama Flask yang agak kaku.

python manage.py sqlmigrate user 0001

Nah hasilnya kira-kira akan seperti ini gaes.

--
-- Create model Users
--
CREATE TABLE `user_users` (`id` bigint AUTO_INCREMENT NOT NULL PRIMARY KEY, `name` varchar(200) NULL, `email` varchar(200) NULL UNIQUE, `password` varchar(230) NOT NULL, `created_at` datetime(6) NOT NULL, `updated_at` datetime(6) NOT NULL);

Keren banget ya? Sekarang kita jalankan aja yuk migrasinya.

python manage.py migrate

Tiba-tiba kamu bingung, loh kok ada migrasi yang saya ngga buat tapi malah jalan sih, contohnya:

Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions, user
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying admin.0003_logentry_add_action_flag_choices... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying auth.0009_alter_user_last_name_max_length... OK
Applying auth.0010_alter_group_name_max_length... OK
Applying auth.0011_update_proxy_permissions... OK
Applying sessions.0001_initial... OK
Applying user.0001_initial... OK

Migration yang kita buat hanya yang user, tapi kok yang lain ngikut ya? Nah tenang aja ngga usah bingung, ini emang merupakan default packagenya si Django, seperti yang pernah saya bilang, Django udah nyisipin paket Admin untuk ngebuat sebuah projek.

Waktu kamu pertama kali jalanin projek juga harusnya kamu engeh ada tulisan kaya gini:

You have 17 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run ‘python manage.py migrate’ to apply them.

Ngga tau sih ya engeh apa ngga, kalo kalian cuek kaya doi, pasti sih ngga engeh (Kok curhat?).

Nah ini dia yang spesial dari django, kalo di Laravel kita hanya membuat table dengan langsung nama objek atau table yang kita mau, di Django dia akan menambahkan nama si app terlebih dahulu sebelum ke objek/tablenya. Sebenernya agak risih buat yang menggunakan Laravel terus mau migrasi ke Django pasti sebel ngeliat struktur gini, ngaku, kalian risih, iya kan? Hahaha

Bisa ngga kalo dimatiin? Tentu saja, coba cek INSTALLED_APPS di setting.

INSTALLED_APPS = [
'user.apps.UserConfig',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]

Kalian bisa menghapus yang admin kalo ngga dipake, tapi sepengetahuan saya selain admin itu kepake, jadi mending jangan dihapus soalnya nanti ngga bisa jalan projeknya. Coba aja kalo ngga percaya (Kalo ada mastah Django mohon koreksi diri hamba yang masih oon ini).

Sebenernya struktur seperti ini cukup baik untuk projek yang besar, karena kita jadi mendefinisikan sebuah table berdasarkan si kata depan, contoh kalo misalnya kamu memiliki tiga turunan table user anggaplah user itu sendiri, email mereka, dan nomor telfon mereka yang terpisah menjadi dua table baru, tentunya kita akan membuat strukur seperti ini kan:

  • users
  • user_phones
  • user_emails

Nah si django untuk membuat itu menjadi cantik sudah mendefine nama app didepan, jadi yang hubungannya sama user ya disitu semua tablenya, ngga misah, dan ini baik untuk tetep konsisten dalam penamaan table ketika projek yang kalian buat emang akan direncanakan untuk menjadi lebih besar dan kompleks.

Tapi tenang aja, kalo kalian ngga suka ya kalian bisa define sendiri kok tanpa pake embel-embel user. Menurut jawaban disini, kita tinggal menambahkan sedikit Meta Class.

LEWATI INI KALO KALIAN MERASA NGGA APA DENGAN STRUKTUR DATABASE SEKARANG INI

Pertama kita rollback dulu migrationnya dengan cara:

python manage.py migrate user zero

Delete file 0001_initial.py didalam migrations. Setelah itu ubah si models.py:

from datetime import datetime

from django.db import models


# Create your models here.
class Users(models.Model):
id = models.BigAutoField(primary_key=True)
name = models.CharField(max_length=200, null=True)
email = models.CharField(max_length=200, null=True, unique=True)
password = models.CharField(max_length=230, null=False)
created_at = models.DateTimeField(default=datetime.utcnow)
updated_at = models.DateTimeField(default=datetime.utcnow)

class Meta:
db_table = 'users'

Jalanin ulang

python manage.py makemigrations user
python manage.py migrate user

Nanti hasilnya akan menjadi seperti ini.

Kalo saya sih jujur bodoamat ngga harus batu dengan struktur diatas untuk projek baru, tapi kalo projek lama yang migrasi teknologi misalnya dari Laravel ke Django, cara diatas tokcer deh.

Oke kita lanjut ya, udah separuh jalan nih kita hahaha~

Sekarang kita akan make pattern dasar dari yang semua developer umum udah tau, yaitu MVC.

Sebenernya kalo di Django kurang tepat, karena kalo di Django adalah MTV, bukan yang musik-musik loh, tapi Models Templates Views, dimana Models ya tetep selaku Models, templates ini ibarat view, dan Views ibarat Controller kalo di pattern MVC.

Nah Django juga memakai filosofi “Fat Models, Skinny Views”, yang artinya kebanyakan business logic kamu akan dipasang di Models, dan bukan di Views alias Controller kita.

Oke sekarang kita buat dulu file response.py didalam project todoproject kita.

Kodenya ambil dibawah.

from django.http import JsonResponse


class Response:

def base(self, values=None, message="", status=200):
if values is None:
values = []

return JsonResponse({
'values': values,
'message': message
}, status=status)

@staticmethod
def ok(values=None, message=""):
return Response().base(values=values, message=message, status=200)

@staticmethod
def badRequest(values=None, message=""):
return Response().base(values=values, message=message, status=400)

Seperti biasa kita akan membungkus sebuah method Response API agar kita bisa pake berulang-ulang alias filosofi dari DRY (Don’t Repeat Yourself). Sekali ngoding, pake fungsi yang sama lagi dan lagi di berbagai fungsi.

Setelahnya kita buat transformer kita, konsepnya hampir mirip Serializer, atau yang intinya akan nerjemahin semua yang kita tangkap dari DB supaya kita bisa olah dulu ditengah jalan.

buat file transformer.py didalam app user dan copas code dibawah ini.

def transform(values):
arr = []

for item in values:
arr.append(singleTransform(item))

return arr


def singleTransform(values):
return {
"id": values.id,
"name": values.name,
"email": values.email,
"created_at": str(values.created_at),
"updated_at": str(values.updated_at)
}

Saatnya mengolah views alias controller kita! Sekarang ubah views.py yang didalam app user menjadi seperti ini:

from todoproject.response import Response
from . import transformer
from .models import Users


def index(request):
user = Users.objects.all()
user = transformer.transform(user)
return Response.ok(values=user)

Untuk pengguna PyCharm, biasanya kamu akan mendapatkan error di bagian import todoproject.response, hal ini dikarenakan Pycharm mengidentifikasi folder django-todo sebagai folder utama kita. Pengguna VSCode woles aja, kan Python udah tau projectnya dimana, keistimewaan VSCode yang ngga ribet-ribet disini haha.

Tapi tenang aja, caranya cukup klik kanan di todoproject yang sudah saya blok menjadi warna biru, klik kanan -> Mark Directory as -> Sources Root

Nah sekarang kita gaskeun dengan cara jalanin projek seperti biasa dan execute ke localhost:8000/user.

Jangan lupa isi data dummy dulu di DB kalian, biar bisa muncul.

Nah sekarang response API kita sudah terbungkus kedalam object values dan message, hal ini akan memudahkan Frontend atau Mobile Developer membuat Base Response sehingga mereka hanya fokus kedalam response didalam values untuk mengolah data, dan message untuk menampilkan pesan.

Sekarang kita coba get by ID yuk, tambahkan fungsi berikut di views.py

def show(request, id):
user = Users.objects.filter(id=id).first()

if not user:
return Response.badRequest(message='Pengguna tidak ditemukan!')

user = transformer.singleTransform(user)
return Response.badRequest(values=user)

Lalu kita ubah urls.py di app user.

urlpatterns = [
path('', views.index, name='index'),
path('<int:id>', views.show, name='show'),
]

Kalo udah kita coba akses lagi.

Oke kan? Khusus untuk single return, dia tidak akan menggunakan array, melainkan object, hal ini juga akan memudahkan mobile dan frontend developer gan. Kita sebagai backend wajib memberikan kenyamanan juga untuk frontend/mobile devs.

Nah sekarang kita coba kalo ID-nya tidak ditemukan.

Mantep, return values berbentuk array ini merupakan default value kalau kita ngga isi saat melempar response. Jadi si frontend/mobile dev tinggal mengecek saja apakah ini kembalian yang hanya berisi pesan atau juga ada objek.

Oke hyung, sekian dulu ya dari saya, nanti dilanjut lagi lain waktu, adios dan happy coding ^^

--

--

Hudya
Hudya

Written by Hudya

Which is more difficult, coding or counting? Not both of them, the difficult one is sharing your knowledge to people without asking the payment.

Responses (1)