Как создать значение по умолчанию для атрибутов в модели 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?
большое спасибо.
11 ответов
вы можете установить значение по умолчанию для столбца в миграции
....
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
когда мне нужны значения по умолчанию, его обычно для новых записей перед отображением представления нового действия. Следующий метод установит значения по умолчанию только для новых записей, чтобы они были доступны при отрисовке форм. 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
вы можете сделать это без написания какого-либо кода :) вам просто нужно установить значение по умолчанию для столбца в базе данных. Вы можете сделать это в своих миграциях. Например:
create_table :projects do |t|
t.string :status, :null => false, :default => 'P'
...
t.timestamps
end
надеюсь, что это поможет.
решение зависит от нескольких вещей.
зависит ли значение по умолчанию от другой информации, доступной во время создания? Вы можете стереть базу данных с минимальными последствиями?
Если вы ответили на первый вопрос да, то вы хотите использовать Джима решение
Если вы ответили на второй вопрос да, то вы хотите использовать Дэниела решением
Если вы ответили нет на оба вопроса, Вы, вероятно, лучше добавить и новую миграция.
class AddDefaultMigration < ActiveRecord::Migration
def self.up
change_column :tasks, :status, :string, :default => default_value, :null => false
end
end
: string можно заменить любым типом, который распознает ActiveRecord::Migration.
CPU дешев, поэтому переопределение задачи в решении Джима не вызовет много проблем. Особенно в производственной среде. Эта миграция является правильным способом сделать это, поскольку она загружается и вызывается гораздо реже.
Я бы рассмотрел использование найденных attr_defaults здесь. Ваши самые смелые мечты сбудутся.
просто укрепление Джим
С помощью присутствие можно сделать
class Task < ActiveRecord::Base
before_save :default_values
def default_values
self.status = status.presence || 'P'
end
end
для типов столбцов Rails поддерживает из коробки-как строка в этом вопросе-лучший подход-установить столбец по умолчанию в самой базе данных, как указывает Даниэль Кристенсен. Rails будет интроспектировать БД и инициализировать объект соответствующим образом. Кроме того, это делает вашу БД безопасной от кого-то, добавляющего строку вне вашего приложения Rails и забывающего инициализировать этот столбец.
для типов столбцов рельсы не поддерживают из коробки-например, столбцы перечисления-рельсы не будут возможность интроспекции столбца по умолчанию. Для этих случаев вы делаете не хотите использовать after_initialize (он вызывается каждый раз, когда объект загружается из базы данных, а также каждый раз, когда объект создается с помощью .new), before_create (потому что это происходит после проверки) или before_save (потому что это происходит и при обновлении, что обычно не то, что вы хотите).
скорее, вы хотите установить атрибут в before_validation on: create, например:
before_validation :set_status_because_rails_cannot, on: :create
def set_status_because_rails_cannot
self.status ||= 'P'
end
Как я вижу, есть две проблемы, которые требуют решения при необходимости значения по умолчанию.
- вам нужно значение, присутствующее при инициализации нового объекта. Использование 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
Я хотел бы знать, почему люди думают этот подход.
Я нашел лучший способ сделать это сейчас:
def status=(value)
self[:status] = 'P'
end
в Ruby вызову метода разрешено не иметь круглых скобок, поэтому я должен назвать локальную переменную во что-то еще, иначе Ruby распознает ее как вызов метода.
в вашем примере здесь вы назначаете " P "в локальную переменную "status". Попробуй!--0--> для этого короля вещей. Хотя было бы лучше использовать after_initialize
Как уже упоминалось в других ответов.