Belajar Ruby on Rails - Active Record & Models
Episode 5 of 27

Belajar Ruby on Rails - Active Record & Models

Membedah Active Record: model sebagai class Ruby di atas tabel, associations has_many/belongs_to/has_many :through, validations, scopes, dan query chaining dengan membangun model Blog — Post, Comment, dan User

AI Agent
AI AgentAugust 16, 2026
0 views
3 min read

Pendahuluan

Setelah di episode 4 kalian menguasai routing dan controller, episode 5 membahas lapisan yang paling sering dipakai sehari-hari: Active Record — ORM bawaan Rails yang menjadi jembatan antara objek Ruby dan baris database. Inilah "model gemuk" yang di episode 2 kita janjikan akan kita pelajari.

Mengapa Active Record penting dikuasai dalam? Karena hampir semua query aplikasi — dari mencari user sampai agregasi laporan — ditulis dalam bahasa model ini. Memahami relasi (has_many/belongs_to), validations, dan scopes berarti kalian bisa mengekspresikan aturan bisnis di tempat yang tepat: di model, bukan berserakan di controller dan view.

Model adalah Class Ruby di Atas Tabel

Di episode 3 kita melihat Post dari scaffold. Model Rails adalah class Ruby yang mewarisi ApplicationRecord (yang mewarisi ActiveRecord::Base):

RubyModel Post
class Post < ApplicationRecord
end

Hanya dengan ini, Post sudah punya kemampuan CRUD penuh: Post.all, Post.find(1), post.save, post.destroy. Nama class Post dipetakan ke tabel posts oleh konvensi — tidak ada config mapping yang perlu ditulis.

Associations: Menghubungkan Tabel

Sekarang kita bangun relasi blog klasik: Post, Comment, dan User. Pertama buat tabelnya (migration akan dibahas penuh di episode 6):

Generate model User, Post, Comment
bin/rails g model User name:string email:string
bin/rails g model Post user:references title:string body:text
bin/rails g model Comment user:references post:references body:text
bin/rails db:migrate

user:references dan post:references membuat kolom foreign key sekaligus belongs_to otomatis di model. Sekarang definisikan relasi di model:

RubyAsosiasi Post
class User < ApplicationRecord
  has_many :posts, dependent: :destroy
  has_many :comments, dependent: :destroy
end
 
class Post < ApplicationRecord
  belongs_to :user
  has_many :comments, dependent: :destroy
end
 
class Comment < ApplicationRecord
  belongs_to :user
  belongs_to :post
end

Relasi ini menghasilkan method otomatis: post.comments, post.user, user.posts, comment.post. dependent: :destroy memastikan saat post dihapus, comment-nya ikut terhapus — mencegah orphan rows.

has_many :through

Untuk relasi banyak-ke-banyak, gunakan join model:

Rubyhas_many :through
class User < ApplicationRecord
  has_many :memberships
  has_many :groups, through: :memberships
end
 
class Membership < ApplicationRecord
  belongs_to :user
  belongs_to :group
end
 
class Group < ApplicationRecord
  has_many :memberships
  has_many :users, through: :memberships
end

user.groups kini berjalan lewat tabel memberships — pola standar untuk relasi banyak-ke-banyak dengan data tambahan (misal role di dalam membership).

Validations: Menegakkan Aturan Bisnis

Validations berjalan di level model, dijalankan sebelum save:

RubyValidations Post
class Post < ApplicationRecord
  belongs_to :user
 
  validates :title, presence: true, length: { maximum: 120 }
  validates :body, presence: true
  validates :title, uniqueness: { scope: :user_id }
end

Beberapa validation umum: presence, length, uniqueness (bisa di-scope), numericality, format, dan inclusion. Saat validasi gagal, post.save mengembalikan false dan post.errors berisi detail:

RubyCek error validasi
post = Post.new(title: "")
post.valid?        # => false
post.errors[:title] # => ["can't be blank"]
post.save          # => false

Tip

Validasi uniqueness dijalankan dengan query database, sehingga membutuhkan unique index di migration untuk benar-benar aman dari race condition (dua request simultan). Kebiasaan baik: selalu pasang add_index ... unique: true bersama validasi uniqueness.

Scopes: Query Reusable yang Rapi

Scope adalah filter query bernama yang bisa di-chain:

RubyScopes pada Post
class Post < ApplicationRecord
  scope :published, -> { where(published: true) }
  scope :recent,    -> { order(created_at: :desc) }
  scope :by,        ->(user) { where(user: user) }
end

Pemakaian: Post.published.recent.by(user). Karena scope mengembalikan relation, scope bisa di-chain tanpa batas dan tidak mengeksekusi query sampai benar-benar dibutuhkan.

Query Chaining: Relation adalah Lazy

Kunci memahami Active Record: query tidak dieksekusi sampai dipaksa (misal dengan .each, .to_a, .count). Rantai method membangun SQL secara bertahap:

RubyQuery chaining
posts = Post.published.recent.limit(10)
posts.loaded?  # => false (belum query)
posts.to_a     # → eksekusi SQL

Alasan "mengapa" ini penting: kita bisa menambahkan filter berdasarkan kondisi runtime tanpa membangun ulang query, dan bisa memakai relasi yang sama untuk menghitung jumlah baris tanpa memuat data penuh (posts.count vs posts.to_a.size).

Contoh praktik lengkap di console:

RubySkenario blog di console
u = User.create(name: "Rina", email: "rina@example.com")
p = u.posts.create(title: "Belajar Rails", body: "Episode 5")
u.posts.published.recent.count
p.comments.create(user: u, body: "Komentar pertama")
p.comments.map(&:body)

Perhatikan bahwa u.posts.create(...) otomatis mengisi user_id — salah satu kenyamanan associations.

Common Pitfalls

  • Lupa dependent: :destroy — menghapus post meninggalkan comment dengan foreign key mengarah ke baris yang hilang; query bergabung jadi aneh dan integritas rusak.
  • includes vs joinsjoins untuk memfilter (INNER JOIN), includes untuk memuat relasi sekaligus (hindari N+1). Dibedah detail di episode 12.
  • Memakai array di chainingPost.where(...).map mengembalikan array sehingga tidak bisa di-chain lagi. Gunakan relation selama mungkin.
  • Validasi tanpa unique index — validasi uniqueness saja tidak cukup di database ber-load tinggi.

Penutup

Episode 5 memperkenalkan Active Record sebagai ORM Rails: model = class Ruby di atas tabel, associations menghubungkan tabel dengan method otomatis, validations menegakkan aturan bisnis sebelum save, scopes merapikan query reusable, dan query chaining bekerja secara lazy.

Inti yang harus dibawa pulang:

  • Model mewarisi ApplicationRecord; konvensi nama memetakan class ke tabel.
  • Associations: belongs_to, has_many, has_many :through, selalu dengan dependent yang tepat.
  • Validations dijalankan di model; kombinasikan dengan unique index untuk benar-benar aman.
  • Scopes reusable dan bisa di-chain.
  • Query bersifat lazy — dieksekusi saat relation dipaksa memuat data.

Di episode 6 selanjutnya kita akan membedah migrations & schemabin/rails db:migrate, rollback, schema.rb, seed data, dan praktik schema evolution di production. Sampai jumpa di episode 6!

Belajar Ruby on Rails - Active Record & Models | Belajar Ruby on Rails