Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

rails - apply default value for enum field

I want to have a default value for my enum field to prevent it from being nil. I did the following:

# db/schema.rb
create_table "templates", force: :cascade do |t|
  t.integer  "status"
end

# app/models/template.rb
class Template < ActiveRecord::Base
  STATUSES = [:draft, :published]
  enum status: STATUSES
  after_initialize :init

  def init
    self.status ||= STATUSES.index(:draft)
  end
end

I get the expected results in my local environment. But not quite in heroku. I need it to be the default value draft after updating status to nil, but in this context it becomes nil and yet the published scope still includes the updated row.

$ heroku run rails console

> Template.published.pluck :id
=> [1, 2]

> Template.find(1).update(status:nil)
Template Load (4.4ms)  SELECT  "templates".* FROM "templates" WHERE "templates"."id" = $1 LIMIT 1  [["id", 1]]
Template Load (4.4ms)  SELECT  "templates".* FROM "templates" WHERE "templates"."id" = $1 LIMIT 1  [["id", 1]]
(1.7ms)  BEGIN
(1.7ms)  BEGIN
(1.1ms)  COMMIT
(1.1ms)  COMMIT
=> true

> Template.find(1).status
=> nil

> Template.published.pluck :id
=> [1, 2]

Is this the correct use case to use an enum? Is there a peculiarity with my heroku environment that I'm missing out?

like image 498
Martin Verdejo Avatar asked Jan 31 '16 09:01

Martin Verdejo


4 Answers

Rails 6.1+

Starting from Rails 6.1, it is possible to set a default enum value right in the model. For example:

class Template < ActiveRecord::Base
  enum status: [:draft, :published], _default: :draft
end

Here is a link to relative PR and a link to the docs.

Rails 7+

Starting from Rails 7, there is no more need to use leading underscore. For example:

class Template < ActiveRecord::Base
  enum :status, [:draft, :published], default: :draft
end

Here is a link to relative PR and a link to the docs.

like image 57
Marian13 Avatar answered Oct 13 '22 10:10

Marian13


You can set the default value from the database declaration.

create_table :templates do |t|
  t.column :status, :integer, default: 0
end

And then, map the relationship as following

class Template < ActiveRecord::Base
  enum status: { draft: 0, published: 1 }
end
like image 23
Jefferson Avatar answered Oct 13 '22 08:10

Jefferson


Rails 7

Rails 6.1 allowed the setting of a default enum value right in the model.

Rails 7 introduced new syntax for defining an enum. E.g.

class Template < ActiveRecord::Base
  enum :status, [:draft, :published], default: :draft
end

Here is a link to the relevant PR and a link to the edge docs.

like image 1
Tom Luce Avatar answered Oct 13 '22 08:10

Tom Luce


Another way of doing it is:

class Template < ActiveRecord::Base
  before_create :set_default_status

  enum :status: { draft: 0, published: 1 }

  def set_default_status
    self.status ||= :draft
  end
end

In this case, you can keep your migration file simple:

...
t.integer :status
...
like image 1
Anton S. Avatar answered Oct 13 '22 10:10

Anton S.