Как создать значение по умолчанию для атрибутов в модели 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?

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

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

 .... 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 

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

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

 class MyModel < ActiveRecord::Base after_initialize :default_values private def default_values self.name ||= "default value" end end 

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

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

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

Когда мне нужно значение по умолчанию, оно обычно используется для новых записей перед визуализацией представления нового действия. Следующий метод будет устанавливать значения по умолчанию только для новых записей, чтобы они были доступны при рендеринге форм. 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 

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

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

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

Если вы ответили на второй вопрос «да», тогда вы хотите использовать решение Даниэля

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

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

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

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

Я бы рассмотрел использование найденных здесь attr_default. Твои самые смелые мечты сбываются.

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

Использование присутствия можно сделать

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

Для типов столбцов 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 

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

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

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

 # 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 

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

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

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

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

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

Давайте будем гением компьютера.