Как создать значение по умолчанию для атрибутов в модели Rails activerecord?

Я хочу создать значение по умолчанию для атрибута, определяя его в ActiveRecord. По умолчанию каждый раз, когда создается запись, я хочу иметь значение по умолчанию для атрибута :status. Я попытался сделать это:

class Task < ActiveRecord::Base
  def status=(status)
    status = 'P'
    write_attribute(:status, status)
  end
end

Но после создания я все еще извлекаю эту ошибку из базы данных:

ActiveRecord::StatementInvalid: Mysql::Error: Column 'status' cannot be null

Поэтому я предполагаю, что значение не было применено к атрибуту.

Каким будет элегантный способ сделать это в Rails?

Большое спасибо.

Ответ 1

Вы можете установить параметр по умолчанию для столбца в разделе миграции

....
add_column :status, :string, :default => "P"
....

ИЛИ

Вы можете использовать обратный вызов, before_save

class Task < ActiveRecord::Base
  before_save :default_values
  def default_values
    self.status ||= 'P' # note self.status = 'P' if self.status.nil? might be safer (per @frontendbeauty)
  end
end

Ответ 2

Поскольку я столкнулся с этой проблемой совсем недавно, а опции для Rails 3.0 немного разные, я дам еще один ответ на этот вопрос.

В Rails 3.0 вы хотите сделать что-то вроде этого:

class MyModel < ActiveRecord::Base
  after_initialize :default_values

  private
    def default_values
      self.name ||= "default value"
    end
end

Ответ 3

Вы можете сделать это без написания кода вообще:) Вам просто нужно установить значение по умолчанию для столбца в базе данных. Вы можете сделать это в своих миграциях. Например:

create_table :projects do |t|
  t.string :status, :null => false, :default => 'P'
  ...
  t.timestamps
end

Надеюсь, что это поможет.

Ответ 4

Когда мне нужно значение по умолчанию, оно обычно используется для новых записей перед визуализацией нового действия. Следующий метод будет устанавливать значения по умолчанию только для новых записей, чтобы они были доступны при рендеринге форм. before_save и before_create слишком поздно и не будут работать , если вы хотите, чтобы значения по умолчанию отображались в полях ввода.

after_initialize do
  if self.new_record?
    # values will be available for new record forms.
    self.status = 'P'
    self.featured = true
  end
end

Ответ 5

Решение зависит от нескольких вещей.

Значение по умолчанию зависит от другой информации, доступной во время создания? Можете ли вы стереть базу данных с минимальными последствиями?

Если вы ответили на первый вопрос "да", вы хотите использовать решение "Джим"

Если вы ответили на второй вопрос "да", вы хотите использовать решение Daniel

Если вы ответили "нет" на оба вопроса, вам, вероятно, лучше добавить и запустить новую миграцию.

class AddDefaultMigration < ActiveRecord::Migration
  def self.up
     change_column :tasks, :status, :string, :default => default_value, :null => false
  end
end

: строка может быть заменена любым типом, который распознает ActiveRecord:: Migration.

CPU дешев, поэтому переопределение задачи в решении Jim не вызовет много проблем. Особенно в производственной среде. Эта миграция - это правильный способ сделать это, поскольку она загружается и называется гораздо реже.

Ответ 6

Я бы рассмотрел использование найденных здесь attr_defaults . Ваши самые смелые мечты сбудутся.

Ответ 7

Просто усиление Джим отвечает

Используя presence, вы можете сделать

class Task < ActiveRecord::Base
  before_save :default_values
  def default_values
    self.status = status.presence || 'P'
  end
end

Ответ 8

Как я вижу, есть две проблемы, требующие адресации при необходимости значения по умолчанию.

  • Вам нужно значение, которое будет присутствовать при инициализации нового объекта. Использование after_initialize не подходит, поскольку, как указано, он будет вызываться во время вызовов #find, что приведет к поражению производительности.
  • Вам нужно сохранить значение по умолчанию при сохранении

Вот мое решение:

# the reader providers a default if nil
# but this wont work when saved
def status
  read_attribute(:status) || "P"
end

# so, define a before_validation callback
before_validation :set_defaults
protected
def set_defaults
  # if a non-default status has been assigned, it will remain
  # if no value has been assigned, the reader will return the default and assign it
  # this keeps the default logic DRY
  status = status
end

Мне бы хотелось узнать, почему люди думают об этом.

Ответ 9

Для типов столбцов Rails поддерживает из коробки - как строка в этом вопросе - лучший подход заключается в том, чтобы установить столбец по умолчанию в самой базе данных, как указывает Даниэль Кристенсен. Rails будет интроспективно рассматривать БД и соответственно инициализировать объект. Кроме того, это делает вашу БД безопасной от кого-то, добавляющего строку за пределами вашего приложения Rails, и забывает инициализировать этот столбец.

Для типов столбцов Rails не поддерживается из коробки. Столбцы ENUM - Rails не смогут инспектировать столбец по умолчанию. Для этих случаев вы не хотите использовать after_initialize (он вызывается каждый раз, когда объект загружается из БД, а также каждый раз, когда объект создается с использованием .new), before_create (потому что он возникает после проверки) или before_save (потому что это происходит и при обновлении, что обычно не является тем, что вы хотите).

Скорее, вы хотите установить атрибут в before_validation на: create, например:

before_validation :set_status_because_rails_cannot, on: :create

def set_status_because_rails_cannot
  self.status ||= 'P'
end

Ответ 10

Я нашел лучший способ сделать это сейчас:

def status=(value) 
  self[:status] = 'P' 
end 

В Ruby вызов метода не может иметь круглых скобок, поэтому я должен указать локальную переменную в нечто другое, иначе Ruby распознает ее как вызов метода.

Ответ 11

В вашем примере здесь вы назначаете 'P' в локальную переменную 'status'. Попробуйте self.status = 'P' для этого короля вещей. Хотя было бы лучше использовать after_initialize, как упоминалось в других ответах.