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

417

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

Я вижу сообщение от Пратика, которое описывает уродливый, сложный кусок кода: http://m.onkey.org/2007/7/24/how-to-set-default-values-in-your-model

class Item < ActiveRecord::Base  
  def initialize_with_defaults(attrs = nil, &block)
    initialize_without_defaults(attrs) do
      setter = lambda { |key, value| self.send("#{key.to_s}=", value) unless
        !attrs.nil? && attrs.keys.map(&:to_s).include?(key.to_s) }
      setter.call('scheduler_type', 'hotseat')
      yield self if block_given?
    end
  end
  alias_method_chain :initialize, :defaults
end

Я видел следующие примеры, гуглящие вокруг:

  def initialize 
    super
    self.status = ACTIVE unless self.status
  end

а также

  def after_initialize 
    return unless new_record?
    self.status = ACTIVE
  end

Я также видел, как люди включили это в свою миграцию, но я бы предпочел, чтобы это было определено в коде модели.

Есть ли канонический способ установить значение по умолчанию для полей в модели ActiveRecord?

ryw
источник
Похоже, вы сами ответили на вопрос, в двух разных вариантах :)
Адам Быртек,
19
Обратите внимание, что «стандартная» идиома Ruby для «self.status = ACTIVE», если self.status »не является« self.status || = ACTIVE »
Майк Вудхаус,
1
Ответ Джеффа Перрина намного лучше, чем тот, который в настоящее время помечен как принятый. default_scope является неприемлемым решением для установки значений по умолчанию, поскольку он имеет ОГРОМНЫЙ ПОБОЧНЫЙ ЭФФЕКТ, также изменяющий поведение запросов.
Лоуренс
см. также stackoverflow.com/questions/3975161/…
Виктор Трон
2
учитывая все положительные отзывы на этот вопрос, я бы сказал, что Ruby нужен метод setDefaultValue для ActiveRecord
spartikus

Ответы:

557

Есть несколько проблем с каждым из доступных методов, но я считаю, что определение after_initializeобратного вызова - это путь по следующим причинам:

  1. default_scopeбудет инициализировать значения для новых моделей, но тогда это станет областью, в которой вы найдете модель. Если вы просто хотите инициализировать некоторые числа в 0, то это не то, что вы хотите.
  2. Определение значений по умолчанию в вашей миграции также работает частично ... Как уже упоминалось, это не будет работать, когда вы просто вызываете Model.new.
  3. Переопределение initializeможет работать, но не забудьте позвонить super!
  4. Использование такого плагина, как phusion, становится немного нелепым. Это ruby, нужен ли нам плагин только для инициализации некоторых значений по умолчанию?
  5. Переопределение after_initialize устарело с Rails 3. Когда я переопределяю after_initializeв rails 3.0.3, я получаю следующее предупреждение в консоли:

ПРЕДУПРЕЖДЕНИЕ ОБ УСТРАНЕНИИ: Base # after_initialize устарела, используйте взамен Base.after_initialize: метод. (вызывается из / Users / me / myapp / app / models / my_model: 15)

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

  class Person < ActiveRecord::Base
    has_one :address
    after_initialize :init

    def init
      self.number  ||= 0.0           #will set the default value only if it's nil
      self.address ||= build_address #let's you set a default association
    end
  end    

Теперь у вас есть только одно место для поиска инициализации ваших моделей. Я использую этот метод, пока кто-то не придумает лучшего.

Предостережения:

  1. Для логических полей:

    self.bool_field = true if self.bool_field.nil?

    Смотрите комментарий Пола Рассела к этому ответу для более подробной информации.

  2. Если вы выбираете только подмножество столбцов для модели (т. Е. Используете selectв запросе как Person.select(:firstname, :lastname).all), вы получите, MissingAttributeErrorесли ваш initметод обращается к столбцу, который не был включен в selectпредложение. Вы можете защититься от этого случая следующим образом:

    self.number ||= 0.0 if self.has_attribute? :number

    и для логического столбца ...

    self.bool_field = true if (self.has_attribute? :bool_value) && self.bool_field.nil?

    Также обратите внимание, что синтаксис до Rails 3.2 был другим (см. Комментарий Клиффа Дарлинга ниже)

Джефф Перрен
источник
7
Это определенно лучший способ для достижения этой цели. Что действительно странно и неудачно. Разумный предпочтительный метод установки значений по умолчанию для атрибутов модели при создании выглядит так, как будто Rails уже должен был встроить. Единственный другой (надежный) способ, переопределение initialize, просто кажется действительно замысловатым для чего-то, что должно быть ясно и четко определено. Я провел часы, просматривая документацию, прежде чем искать здесь, потому что я предполагал, что эта функция уже была где-то там, и я просто не знал об этом.
seaneshbaugh
106
Одно замечание по этому self.bool_field ||= trueповоду - если у вас есть логическое поле, которое вы хотите использовать по умолчанию, не делайте этого , поскольку это заставит поле иметь значение true, даже если вы явно инициализируете его как false. Вместо этого делай self.bool_field = true if self.bool_field.nil?.
Пол Рассел
2
Что касается пункта № 2, Model.new фактически работает (только для меня?) Вместе со значениями по умолчанию, определенными в миграциях, или, точнее, со значениями по умолчанию для столбцов таблицы. Но я признаю, что метод Джеффа, основанный на обратном вызове after_initialize, вероятно, лучший способ сделать это. Просто вопрос: работает ли он с грязными, но не сохраненными объектами? В вашем примере Person.new.number_was вернет 0.0?
Лоран Фарси
21
Осторожность при использовании этого подхода в сочетании с выбором конкретных столбцов с активной записью. В этом случае в объекте будут найдены только атрибуты, указанные в запросе, и код инициализации выдает a MissingAttributeError. Вы можете добавить дополнительную проверку , как показано на рисунке: self.number ||= 0.0 if self.has_attribute? :number Для булевы: self.bool_field = true if (self.has_attribute? :bool_value) && self.bool_field.nil?. Это Rails 3.2+ - для более ранней, используйте self.attributes.has_key?, и вам нужна строка вместо символа.
Клифф Дарлинг
6
Выполнение этого с ассоциациями будет стремиться загрузить эти ассоциации при поиске. Начните initializeс того, return if !new_record?чтобы избежать проблем с производительностью.
Кайл Мейси
69

Рельсы 5+

Вы можете использовать метод атрибута в своих моделях, например:

class Account < ApplicationRecord
  attribute :locale, :string, default: 'en'
end

Вы также можете передать лямбду в defaultпараметр. Пример:

attribute :uuid, UuidType.new, default: -> { SecureRandom.uuid }
Лукас Катон
источник
3
аааа, это драгоценный камень, который я искал! default также может использовать proc, например default: -> {Time.current.to_date}
schpet
1
Убедитесь, что в качестве второго аргумента указан тип, в противном случае тип будет задан, Valueи преобразование типов не будет выполнено.
нуль
к моему удовольствию, это также работает с store_accessor, например, учитывая, что store_accessor :my_jsonb_column, :localeвы можете определитьattribute :locale, :string, default: 'en'
Райан Романчук
О, это фантастика, мне нужно было показать значения по умолчанию в форме, и это прекрасно работает. Спасибо Лукас.
Пол Уотсон
Можно все еще установить это nil. Если они не могут быть nilБД not null+ БД по умолчанию + github.com/sshaw/keep_defaults - это путь из моего опыта
sshaw
47

Мы помещаем значения по умолчанию в базу данных посредством миграций (указав :defaultпараметр в каждом определении столбца) и позволяем Active Record использовать эти значения, чтобы установить значение по умолчанию для каждого атрибута.

ИМХО, этот подход согласуется с принципами AR: соглашение по конфигурации, DRY, определение таблицы определяет модель, а не наоборот.

Обратите внимание, что значения по умолчанию все еще находятся в коде приложения (Ruby), но не в модели, а в миграциях.

Лоран Фарси
источник
3
Другая проблема - когда вы хотите использовать значение по умолчанию для внешнего ключа. Вы не можете жестко закодировать значение идентификатора в поле внешнего ключа, потому что на разных БД идентификатор может отличаться.
shmichael
2
Еще одна проблема заключается в том, что таким образом вы не можете инициализировать непостоянные средства доступа (атрибуты, которые не являются столбцами БД).
Виктор Трон
2
Другая проблема заключается в том, что вы не можете видеть все значения по умолчанию в одном месте. они могут быть разбросаны по разным миграциям.
деклан
8
Деклан, есть БД / schema.rb
Бенджамин Аткин
6
Я хотел бы отметить для будущих читателей: по крайней мере из того, что я прочитал, это противоречит принципам AR. Логика для моделей должна лежать в модельных классах, а базы данных должны быть настолько невежественными, насколько это возможно. Значения по умолчанию для меня составляют особую логику модели.
darethas
40

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

after_initialize :defaults

def defaults
   unless persisted?
    self.extras||={}
    self.other_stuff||="This stuff"
    self.assoc = [OtherModel.find_by_name('special')]
  end
end

Я решил использовать after_initialize, но не хочу, чтобы он применялся к объектам, которые были найдены только новыми или созданными. Я думаю, что это почти шокирует, что обратный вызов after_new не предусмотрен для этого очевидного варианта использования, но я справился, подтвердив, сохранился ли объект уже, указывая, что он не новый.

Увидев ответ Брэда Мюррея, становится еще чище, если условие переносится в запрос обратного вызова:

after_initialize :defaults, unless: :persisted?
              # ":if => :new_record?" is equivalent in this context

def defaults
  self.extras||={}
  self.other_stuff||="This stuff"
  self.assoc = [OtherModel.find_by_name('special')]
end
Джозеф лорд
источник
4
Это действительно важный момент. Я должен представить, что в большинстве случаев установка значения по умолчанию для записи выполняется только перед сохранением новой записи, а не при загрузке сохраненной записи.
Рассел Сильва
Спасибо, чувак, ты спас мой день.
Стефанфридрих
2
Как насчет :before_create?
Франклин Ю
Как: before_create обрабатывает отдельные новые и сохраненные вызовы? Я хотел бы проверить это и понять, прежде чем перейти к нему.
Джозеф Лорд
17

Шаблон обратного вызова after_initialize можно улучшить, просто выполнив следующее

after_initialize :some_method_goes_here, :if => :new_record?

Это имеет нетривиальное преимущество, если ваш код инициализации должен иметь дело с ассоциациями, так как следующий код вызывает тонкое n + 1, если вы читаете начальную запись, не включая связанную с ней.

class Account

  has_one :config
  after_initialize :init_config

  def init_config
    self.config ||= build_config
  end

end
Брэд мюррей
источник
16

У парней Phusion есть хороший плагин для этого.

Милан Новота
источник
Обратите внимание, что этот плагин позволяет :default«просто работать» со значениями в миграциях схемы Model.new.
июня
Я могу заставить :defaultценности в миграциях «просто работать» Model.new, вопреки тому, что сказал Джефф в своем посте. Проверено работает в Rails 4.1.16.
Магне
8

Еще лучше / чище потенциальный способ, чем предложенные ответы, - переписать метод доступа, например так:

def status
  self['status'] || ACTIVE
end

См. «Перезапись доступа по умолчанию» в документации ActiveRecord :: Base и многое другое из StackOverflow по использованию self .

peterhurford
источник
Статус будет по-прежнему равен нулю в хэше, возвращенном из attributes. Проверено на рельсах 5.2.0.
spyle
8

Я использую attribute-defaultsдрагоценный камень

Из документации: запустите sudo gem install attribute-defaultsи добавьте require 'attribute_defaults'в свое приложение.

class Foo < ActiveRecord::Base
  attr_default :age, 18
  attr_default :last_seen do
    Time.now
  end
end

Foo.new()           # => age: 18, last_seen => "2014-10-17 09:44:27"
Foo.new(:age => 25) # => age: 25, last_seen => "2014-10-17 09:44:28"
Айдан
источник
7

Подобные вопросы, но все они имеют немного другой контекст: - Как мне создать значение по умолчанию для атрибутов в модели Rails activerecord?

Лучший ответ: зависит от того, что вы хотите!

Если вы хотите, чтобы каждый объект начинался со значения: используйтеafter_initialize :init

Вы хотите, чтобы new.htmlформа открывала страницу по умолчанию? используйте https://stackoverflow.com/a/5127684/1536309

class Person < ActiveRecord::Base
  has_one :address
  after_initialize :init

  def init
    self.number  ||= 0.0           #will set the default value only if it's nil
    self.address ||= build_address #let's you set a default association
  end
  ...
end 

Если вы хотите, чтобы каждый объект имел значение, рассчитанное на основе пользовательского ввода: используйтеbefore_save :default_values Вы хотите, чтобы пользователь ввел,Xа затемY = X+'foo'? использовать:

class Task < ActiveRecord::Base
  before_save :default_values
  def default_values
    self.status ||= 'P'
  end
end
Блэр Андерсон
источник
4

Для этого и нужны конструкторы! Переопределите метод модели initialize.

Используйте after_initializeметод.

Джон Топли
источник
2
Обычно вы были бы правы, но вы никогда не должны переопределять инициализацию в модели ActiveRecord, поскольку это не всегда может быть вызвано. Вы должны использовать after_initializeметод вместо этого.
Люк Редпат
Использование default_scope просто для установки значения по умолчанию, безусловно, неправильно. after_initialize - правильный ответ.
Joaomilho
4

Привет, ребята, я закончил тем, что сделал следующее:

def after_initialize 
 self.extras||={}
 self.other_stuff||="This stuff"
end

Работает как шарм!

Тони
источник
3

На это уже давно отвечают, но мне часто нужны значения по умолчанию, и я предпочитаю не помещать их в базу данных. Я создаю DefaultValuesпроблему:

module DefaultValues
  extend ActiveSupport::Concern

  class_methods do
    def defaults(attr, to: nil, on: :initialize)
      method_name = "set_default_#{attr}"
      send "after_#{on}", method_name.to_sym

      define_method(method_name) do
        if send(attr)
          send(attr)
        else
          value = to.is_a?(Proc) ? to.call : to
          send("#{attr}=", value)
        end
      end

      private method_name
    end
  end
end

А затем используйте его в моих моделях так:

class Widget < ApplicationRecord
  include DefaultValues

  defaults :category, to: 'uncategorized'
  defaults :token, to: -> { SecureRandom.uuid }
end
Клем
источник
3

Я также видел, как люди включили это в свою миграцию, но я бы предпочел, чтобы это было определено в коде модели.

Есть ли канонический способ установить значение по умолчанию для полей в модели ActiveRecord?

Канонический способ Rails, до Rails 5, на самом деле заключался в том, чтобы установить его при переносе, и просто искать в db/schema.rbкаждый раз, когда хочет увидеть, какие значения по умолчанию устанавливаются БД для любой модели.

Вопреки тому, что в ответе @Jeff Perrin говорится (что немного устарело), ​​подход к миграции будет даже применять по умолчанию при использовании Model.newиз-за некоторой магии Rails. Проверено работает в Rails 4.1.16.

Самая простая вещь часто самая лучшая. Меньшая задолженность по знаниям и потенциальная путаница в кодовой базе. И это «просто работает».

class AddStatusToItem < ActiveRecord::Migration
  def change
    add_column :items, :scheduler_type, :string, { null: false, default: "hotseat" }
  end
end

Или для изменения столбца без создания нового, выполните одно из следующих действий:

class AddStatusToItem < ActiveRecord::Migration
  def change
    change_column_default :items, :scheduler_type, "hotseat"
  end
end

Или, может быть, даже лучше:

class AddStatusToItem < ActiveRecord::Migration
  def change
    change_column :items, :scheduler_type, :string, default: "hotseat"
  end
end

Проверьте официальное руководство RoR для вариантов в методах изменения столбца.

В null: falseЗапрещает NULL значения в БД, и, как дополнительное преимущество, он также обновляет так , что все предварительно существующие БД записи , которые ранее были нуль устанавливается значение по умолчанию для этого поля , а также. Вы можете исключить этот параметр при переносе, если хотите, но я нашел его очень удобным!

Канонический путь в Rails 5+, как сказал @Lucas Caton:

class Item < ActiveRecord::Base
  attribute :scheduler_type, :string, default: 'hotseat'
end
Магне
источник
1

Проблема с решениями after_initialize заключается в том, что вы должны добавить after_initialize к каждому объекту, который вы просматриваете из БД, независимо от того, имеете ли вы доступ к этому атрибуту или нет. Я предлагаю ленивый подход.

Методы-атрибуты (методы получения), конечно, сами являются методами, поэтому вы можете переопределить их и предоставить значение по умолчанию. Что-то вроде:

Class Foo < ActiveRecord::Base
  # has a DB column/field atttribute called 'status'
  def status
    (val = read_attribute(:status)).nil? ? 'ACTIVE' : val
  end
end

Если, как кто-то указал, вам нужно выполнить Foo.find_by_status ('ACTIVE'). В этом случае, я думаю, вам действительно нужно установить ограничения по умолчанию в вашей базе данных, если БД это поддерживает.

Джефф Гран
источник
Это решение и предложенная альтернатива не работают в моем случае: у меня есть иерархия классов STI, где только один класс имеет этот атрибут, и соответствующий столбец, который будет использоваться в условиях запроса БД.
cmoran92
1

Я столкнулся с проблемами с after_initializeвыдачей ActiveModel::MissingAttributeErrorошибок при выполнении сложных находок:

например:

@bottles = Bottle.includes(:supplier, :substance).where(search).order("suppliers.name ASC").paginate(:page => page_no)

«поиск» в .whereхеше условий

В итоге я сделал это путем переопределения инициализации следующим образом:

def initialize
  super
  default_values
end

private
 def default_values
     self.date_received ||= Date.current
 end

superВызов необходимо убедиться , что объект правильно инициализируется с , ActiveRecord::Baseпрежде чем делать свой подгоняет код, то есть: default_values

Шон
источник
Мне это нравится. Мне нужно было сделать def initialize(*); super; default_values; endв Rails 5.2.0. Кроме того, значение по умолчанию доступно даже в .attributesхэше.
spyle
1
class Item < ActiveRecord::Base
  def status
    self[:status] or ACTIVE
  end

  before_save{ self.status ||= ACTIVE }
end
Майк Брин
источник
2
Мммм ... сначала кажется гениальным, но, подумав немного, я вижу несколько проблем. Во-первых, все значения по умолчанию находятся не в одной точке, а разбросаны по классу (представьте, что вы ищете их или меняете). Второе и худшее, позже вы не можете установить нулевое значение (или даже ложное!).
парадоя
почему вам нужно установить нулевое значение по умолчанию? вы получаете это из коробки с AR, ничего не делая вообще. Что касается false при использовании логического столбца, то вы правы, это не лучший подход.
Майк Брин
Я не могу говорить о других привычках кодирования. У меня не было проблемы, потому что я не разбрасываю свои методы получения / установки по файлу класса. Кроме того, любой современный текстовый редактор должен облегчить переход к методу (shift-cmd-t в textmate).
Майк Брин
@paradoja - Я забираю это назад, теперь я вижу, где это ломается с использованием также null. Не обязательно использовать ноль в качестве значения по умолчанию, но если вы действительно хотите изменить значение на ноль в какой-то момент. Хороший улов @paradoja, спасибо.
Майк Брин
Я использую этот метод, поскольку он хорошо работает с динамически генерируемыми атрибутами.
Дейл Кэмпбелл
0

Хотя сделать это для установки значений по умолчанию в большинстве случаев непонятно и неудобно, вы также можете использовать их :default_scope. Проверьте комментарий Squil здесь .

skalee
источник
0

Метод after_initialize устарел, вместо этого используйте обратный вызов.

after_initialize :defaults

def defaults
  self.extras||={}
  self.other_stuff||="This stuff"
end

однако использование : default в ваших миграциях по-прежнему является самым чистым способом.

Greg
источник
4
В Rails 3: after_initializeметод является НЕ рекомендуется . На самом деле обратный вызов в стиле макроса, который вы привели в качестве примера, устарел . Подробности: guides.rubyonrails.org/…
Забба
0

Я обнаружил, что использование метода проверки обеспечивает большой контроль над настройками по умолчанию. Вы даже можете установить значения по умолчанию (или провалить проверку) для обновлений. Вы даже можете установить другое значение по умолчанию для вставок и обновлений, если вы действительно этого хотите. Обратите внимание, что значение по умолчанию не будет установлено до #valid? называется.

class MyModel
  validate :init_defaults

  private
  def init_defaults
    if new_record?
      self.some_int ||= 1
    elsif some_int.nil?
      errors.add(:some_int, "can't be blank on update")
    end
  end
end

Относительно определения метода after_initialize могут быть проблемы с производительностью, поскольку after_initialize также вызывается каждым объектом, возвращаемым: find: http://guides.rubyonrails.org/active_record_validations_callbacks.html#after_initialize-and-after_find

кельвин
источник
проверка происходит только перед сохранением? Что, если вы хотите показать значения по умолчанию перед сохранением?
nurettin
@nurettin Это хороший момент, и я понимаю, почему вы иногда этого хотите, но ОП не упомянул это как требование. Вы должны решить для себя, хотите ли вы использовать дополнительные настройки по умолчанию для каждого экземпляра, даже если он не сохранен. Альтернативой является сохранение фиктивного объекта для newповторного использования действия.
Кельвин
0

Если столбец является столбцом типа «состояние», и ваша модель пригодна для использования конечных автоматов, рассмотрите возможность использования гема aasm , после чего вы можете просто сделать

  aasm column: "status" do
    state :available, initial: true
    state :used
    # transitions
  end

Он по-прежнему не инициализирует значение для несохраненных записей, но он немного чище, чем использование собственного initили чего-то еще, и вы пожинаете другие преимущества Aasm, такие как область действия для всех ваших статусов.

Неверный запрос
источник
0

Я настоятельно рекомендую использовать гем "default_value_for": https://github.com/FooBarWidget/default_value_for

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

Примеры:

По умолчанию ваша база данных равна NULL, ваша модель / ruby-определенная по умолчанию - "некоторая строка", но вы на самом деле хотите установить значение nil по любой причине:MyModel.new(my_attr: nil)

Большинству решений здесь не удастся установить значение nil, и вместо этого будет установлено значение по умолчанию.

Итак, вместо того, чтобы ||=подходить, вы переключаетесь на my_attr_changed?...

НО теперь представьте, что ваше значение по умолчанию для БД - это «некоторая строка», ваше значение для модели / ruby ​​- «какая-то другая строка», но в определенном сценарии вы хотите установить значение «некоторая строка» (значение по умолчанию для БД):MyModel.new(my_attr: 'some_string')

Это приведет к my_attr_changed?тому, что будет false, потому что значение соответствует db по умолчанию, что, в свою очередь, вызовет ваш код ruby, определенный по умолчанию, и установит значение «некоторая другая строка» - опять же, не то, что вы хотели.


По этим причинам я не думаю, что это может быть правильно выполнено с помощью только ловушки after_initialize.

Опять же, я думаю, что драгоценный камень "default_value_for" использует правильный подход: https://github.com/FooBarWidget/default_value_for

etipton
источник
0

Вот решение, которое я использовал, что я был немного удивлен, еще не было добавлено.

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

add_column :teams, :new_team_signature, :string, default: 'Welcome to the Team'

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

 validates :new_team_signature, presence: true

Что это будет делать, это установить значение по умолчанию для вас. (для меня у меня есть «Добро пожаловать в команду»), а затем он пойдет на шаг вперед, гарантируя, что для этого объекта всегда присутствует значение.

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

kdweber89
источник
0
# db/schema.rb
create_table :store_listings, force: true do |t|
  t.string :my_string, default: "original default"
end

StoreListing.new.my_string # => "original default"

# app/models/store_listing.rb
class StoreListing < ActiveRecord::Base
  attribute :my_string, :string, default: "new default"
end

StoreListing.new.my_string # => "new default"

class Product < ActiveRecord::Base
  attribute :my_default_proc, :datetime, default: -> { Time.now }
end

Product.new.my_default_proc # => 2015-05-30 11:04:48 -0600
sleep 1
Product.new.my_default_proc # => 2015-05-30 11:04:49 -0600
shilovk
источник
-2

использовать default_scope в рельсах 3

Api Doc

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

обсуждение

Виктор Трон
источник
если вы используете meta_where, default_scope может не работать для назначения значений по умолчанию для новых объектов AR из-за ошибки.
Виктор Трон
эта проблема meta_where была исправлена ​​[ metautilitary.lighthouseapp.com/projects/53011/tickets/…
Виктор Трон
3
НЕ используйте default_scope. Это заставит все ваши запросы добавить это условие в заданное вами поле. Это почти никогда, что вы хотите.
Брэд
@brad, забавно, что ты упоминаешь, я полностью согласен, это зло :). см. мой комментарий в stackoverflow.com/questions/10680845/… .
Виктор Трон
-3

Из документации API http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html Используйте before_validationметод в вашей модели, он дает вам возможность создания определенной инициализации для вызовов создания и обновления, например, в этом примере (опять код взят из примера api docs) поле номера инициализируется для кредитной карты. Вы можете легко адаптировать это, чтобы установить любые значения, которые вы хотите

class CreditCard < ActiveRecord::Base
  # Strip everything but digits, so the user can specify "555 234 34" or
  # "5552-3434" or both will mean "55523434"
  before_validation(:on => :create) do
    self.number = number.gsub(%r[^0-9]/, "") if attribute_present?("number")
  end
end

class Subscription < ActiveRecord::Base
  before_create :record_signup

  private
    def record_signup
      self.signed_up_on = Date.today
    end
end

class Firm < ActiveRecord::Base
  # Destroys the associated clients and people when the firm is destroyed
  before_destroy { |record| Person.destroy_all "firm_id = #{record.id}"   }
  before_destroy { |record| Client.destroy_all "client_of = #{record.id}" }
end

Удивлен, что его здесь не предложили

jamesc
источник
before_validation не установит значения по умолчанию, пока объект не будет готов к сохранению. Если процессу необходимо прочитать значения по умолчанию перед сохранением, значения не будут готовы.
mmell
Вы никогда не устанавливаете значения по умолчанию во время проверок проверки в любом случае. Это даже не любой взлом. Сделайте это во время инициализации
Сачин