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

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

  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". Попробуй!--0--> для этого короля вещей. Хотя было бы лучше использовать after_initialize Как уже упоминалось в других ответов.