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

Я пытаюсь найти лучший способ установить значения по умолчанию для объектов в Rails.

Самое лучшее, о чем я могу думать, - установить значение по умолчанию в методе new в контроллере.

Есть ли у кого-нибудь какие-либо данные, если это приемлемо или если есть лучший способ сделать это?

Ответ 1

"Правильно" - опасное слово в Ruby. Обычно существует более чем один способ сделать что-либо. Если вы знаете, что всегда будете хотеть, чтобы это значение по умолчанию для этого столбца в этой таблице, установка их в файле миграции DB является самым простым способом:

class SetDefault < ActiveRecord::Migration
  def self.up
    change_column :people, :last_name, :type, :default => "Doe"
  end

  def self.down
    # You can't currently remove default values in Rails
    raise ActiveRecord::IrreversibleMigration, "Can't remove the default"
  end
end

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

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

class GenericPerson < Person
  def initialize(attributes=nil)
    attr_with_defaults = {:last_name => "Doe"}.merge(attributes)
    super(attr_with_defaults)
  end
end

Затем, когда вы выполняете GenericPerson.new(), он всегда будет стекать с атрибутом Doe до Person.new(), если вы не переопределите его чем-то другим.

Ответ 2

В соответствии с ответом SFEley, это обновленный/исправленный для новых версий Rails:

class SetDefault < ActiveRecord::Migration
  def change
    change_column :table_name, :column_name, :type, default: "Your value"
  end
end

Ответ 3

Прежде всего вы не можете перегружать initialize(*args), поскольку он не вызывается во всех случаях.

Ваш лучший вариант - поместить ваши значения по умолчанию в вашу миграцию:

add_column :accounts, :max_users, :integer, :default => 10

Вторым преимуществом является размещение настроек по умолчанию в вашей модели, но это будет работать только с атрибутами, которые изначально ноль. У вас могут быть проблемы со столбцами boolean:

def after_initialize
  if new_record?
    max_users ||= 10
  end
end

Вам нужен new_record?, поэтому значения по умолчанию не переопределяют значения, загруженные из базы данных.

Вам нужно ||= остановить Rails от переопределения параметров, переданных в метод initialize.

Ответ 4

Вы также можете попробовать change_column_default в своих миграциях (протестировано в Rails 3.2.8):

class SetDefault < ActiveRecord::Migration
  def up
    # Set default value
    change_column_default :people, :last_name, "Smith"
  end

  def down
    # Remove default
    change_column_default :people, :last_name, nil
  end
end

change_column_default Rails API docs

Ответ 5

Если вы имеете в виду объекты ActiveRecord, у вас есть (более) два способа сделать это:

1. Используйте параметр: default в DB

например.

class AddSsl < ActiveRecord::Migration
  def self.up
    add_column :accounts, :ssl_enabled, :boolean, :default => true
  end

  def self.down
    remove_column :accounts, :ssl_enabled
  end
end

Дополнительная информация здесь: http://api.rubyonrails.org/classes/ActiveRecord/Migration.html

2. Использовать обратный вызов

например. before_validation_on_create

Дополнительная информация здесь: http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html#M002147

Ответ 6

В Ruby on Rails v3.2.8, используя обратный вызов after_initialize ActiveRecord, вы можете вызвать метод в своей модели, который будет присваивать значения по умолчанию для нового объекта.

after_initialize обратный вызов запускается для каждого объекта, который найден и инстанцируется с помощью поискового устройства, причем after_initialize запускается после создания новых объектов (см. обратные вызовы ActiveRecord).

Итак, IMO он должен выглядеть примерно так:

class Foo < ActiveRecord::Base
  after_initialize :assign_defaults_on_new_Foo
  ...
  attr_accessible :bar
  ...
  private
  def assign_defaults_on_new_Foo
    # required to check an attribute for existence to weed out existing records
    self.bar = default_value unless self.attribute_whose_presence_has_been_validated
  end
end

Foo.bar = default_value для этого экземпляра, если только экземпляр не содержит attribute_whose_presence_has_been_validated ранее при сохранении/обновлении. Затем default_value будет использоваться вместе с вашим представлением для визуализации формы с помощью default_value для атрибута bar.

В лучшем случае это хаки...

EDIT - используйте 'new_record?' чтобы проверить, не создает ли экземпляр нового вызова

Вместо проверки значения атрибута используйте встроенный метод new_record? с рельсами. Итак, приведенный выше пример должен выглядеть так:

class Foo < ActiveRecord::Base
  after_initialize :assign_defaults_on_new_Foo, if: 'new_record?'
  ...
  attr_accessible :bar
  ...
  private
  def assign_defaults_on_new_Foo
    self.bar = default_value
  end
end

Это намного чище. Ах, магия Rails - это умнее меня.

Ответ 7

Для логических полей в Rails 3.2.6 по крайней мере это будет работать в вашей миграции.

def change
  add_column :users, :eula_accepted, :boolean, default: false
end

Помещение 1 или 0 по умолчанию не будет работать здесь, так как это логическое поле. Это должно быть значение true или false.

Ответ 8

Если вы имеете дело с моделью, вы можете использовать API Attriutes в Rails 5+ http://api.rubyonrails.org/classes/ActiveRecord/Attributes/ClassMethods.html#method-i-attribute

просто добавьте миграцию с правильным именем столбца, а затем в модели установите его:

class StoreListing < ActiveRecord::Base
  attribute :country, :string, default: 'PT'
end

Ответ 9

Если вы просто устанавливаете значения по умолчанию для определенных атрибутов модели, поддерживаемой базой данных, я бы рассмотрел использование значений столбца по умолчанию для sql - можете ли вы уточнить, какие типы значений по умолчанию вы используете?

Существует несколько подходов к его обработке, этот плагин выглядит как интересный вариант.

Ответ 10

Предложение о переопределении new/initialize, вероятно, является неполным. Rails будет (часто) вызывать выделение для объектов ActiveRecord, а вызовы для выделения не приведут к вызовам для инициализации.

Если вы говорите об объектах ActiveRecord, посмотрите на переопределение after_initialize.

Эти сообщения в блоге (не мои) полезны:

Значения по умолчанию Конструкторы по умолчанию не вызываются

[Edit: SFEley указывает, что Rails на самом деле смотрит на значение по умолчанию в базе данных при создании экземпляра нового объекта в памяти - я этого не осознавал.]

Ответ 11

Мне нужно было установить значение по умолчанию так же, как если бы он был указан как значение столбца по умолчанию в БД. Таким образом, он ведет себя так:

a = Item.new
a.published_at # => my default value

a = Item.new(:published_at => nil)
a.published_at # => nil

Поскольку after_initialize callback вызывается после установки атрибутов из аргументов, не было никакого способа узнать, является ли атрибут нулевым, потому что он никогда не был установлен или потому, что он был намеренно установлен как nil. Поэтому мне пришлось немного засунуть внутрь и пришли с этим простым решением.

class Item < ActiveRecord::Base
  def self.column_defaults
    super.merge('published_at' => Time.now)
  end
end

Отлично работает для меня. (Rails 3.2.x)

Ответ 12

Потенциально даже лучший/более чистый потенциальный путь, чем предлагаемые ответы, заключается в том, чтобы перезаписать аксессор, например:

def status
  self['name_of_var'] || 'desired_default_value'
end

См. "Перезаписывание аксессуаров по умолчанию" в документация ActiveRecord:: Base и fooobar.com/questions/18100/....

Ответ 13

Я ответил на аналогичный вопрос здесь. Чистый способ сделать это - использовать Rails attr_accessor_with_default

class SOF
  attr_accessor_with_default :is_awesome,true
end

sof = SOF.new
sof.is_awesome

=> true

UPDATE

attr_accessor_with_default устарел в Rails 3.2.. вы могли бы сделать это вместо чистого Ruby

class SOF
  attr_writer :is_awesome

  def is_awesome
    @is_awesome ||= true
  end
end

sof = SOF.new
sof.is_awesome

#=> true

Ответ 14

Если вы говорите об объектах ActiveRecord, я использую "атрибут-по умолчанию".

Документация и загрузка: https://github.com/bsm/attribute-defaults

Ответ 15

Вы можете использовать драгоценный камень rails_default_value. например:

class Foo < ActiveRecord::Base
  # ...
  default :bar => 'some default value'
  # ...
end

https://github.com/keithrowell/rails_default_value

Ответ 16

Создайте миграцию и используйте change_column_default, она краткая и обратимая:

class SetDefaultAgeInPeople < ActiveRecord::Migration[5.2]
  def change
    change_column_default :people, :age, { from: nil, to: 0 }
  end
end

Ответ 17

Вы можете переопределить конструктор для модели ActiveRecord.

Вот так:

def initialize(*args)
  super(*args)
  self.attribute_that_needs_default_value ||= default_value
  self.attribute_that_needs_another_default_value ||= another_default_value
  #ad nauseum
end