Using Rails ActiveRecord Enum to Model Post Status Safely
Learn how to replace magic‑number status columns with Rails ActiveRecord enum to get predicate methods, scopes, and safe database storage.
15 Oct 2025, 03:35 UTC

The problem with raw status values
When a Post model stores its lifecycle as a plain integer or string column, the code often contains "magic numbers" like if post.status == 1. This makes intent unclear, creates coupling between the database and the application, and introduces bugs when a value is changed in one place but forgotten elsewhere.
Thesis: let ActiveRecord handle the mapping
Defining an enum on the model gives you predicate methods, scopes, and automatic conversion to integers while keeping the database column simple. The approach is straightforward, works with existing migrations, and can be verified with a few console commands.
Implementation steps
Generate a Post model with an integer status column. Run this in your project’s root directory (no special permissions needed):
rails generate model Post title:string body:text status:integerOpen the generated migration (
db/migrate/[timestamp]_create_posts.rb) and confirm the column type isinteger. No changes are required.Apply the migration:
rails db:migrateAdd the enum definition to the model (
app/models/post.rb):class Post < ApplicationRecord enum status: { draft: 0, published: 1, archived: 2 } end
Worked example: using the enum in practice
After the migration and model change, you can interact with the status through convenient methods. Open a Rails console (rails console) and try the following:
Create a draft post:
Post.create!(title: 'First draft', body: '…', status: :draft)Check the predicate:
post = Post.last post.draft? # => true post.published? # => falseTransition to another state using the bang methods:
post.published! # updates status to 1 and saves post.published? # => trueQuery with scopes:
Post.published.to_a # returns all posts with status = 1 Post.where(status: :archived).count
These commands run in the console; no additional setup is required. Verify that the underlying database still stores only integers by checking schema.rb:
t.integer "status", default: 0, null: false
Trade‑off and limitation
The enum ties the application to specific integer values. If you need to insert a new state (e.g., scheduled) you must add a migration that updates the enum definition and, if the column already contains data, ensure the new value does not clash with existing ones. Changing the integer value of an existing entry (e.g., moving published from 1 to 3) will silently misinterpret all rows that currently have that value, leading to data corruption.
For workflows that require transitions guards, callbacks, or rich metadata per state, consider a dedicated state‑machine gem (aasm or state_machines-activerecord) instead of a plain enum.
Actionable closing
Add the enum to any model where a status field is used primarily for querying and display.
Run
rails db:migrateto ensure the column exists.Verify locally with
rails consolethat predicate methods, bang methods, and scopes return expected results.Document the integer mapping in a comment or a model annotation to protect future developers from inadvertently re‑ordering the enum.
If you anticipate complex state logic, prototype with a state‑machine library early to avoid costly refactors later.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.