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