Як я можу встановити значення за замовчуванням у ActiveRecord?


417

Як я можу встановити значення за замовчуванням у ActiveRecord?

Я бачу допис від Pratik, який описує потворний, складний фрагмент коду: 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?


Схоже, ви самі відповіли на це питання у двох різних варіантах :)
Адам Біртек

19
Зауважте, що "стандартна" ідентифікація "Рубі" для "self.status = АКТИВНА", якщо self.status "не є" self.status || = АКТИВНА "
Майк Вудхаус

1
Відповідь Джеффа Перріна набагато краща за відповідь, яка наразі позначена як прийнята. default_scope є неприйнятним рішенням для встановлення значень за замовчуванням, оскільки він має ВЕЛИЧЕЗНИЙ ЕФЕКТ також зміни поведінки запитів.
Лоуренс


2
з огляду на всі підсумки цього питання, я б сказав, що Рубі потрібен метод
setDefaultValue

Відповіді:


557

З кожним із доступних методів існує кілька проблем, але я вважаю, що визначення after_initializeзворотного дзвінка - це спосіб пройти з наступних причин:

  1. default_scopeбуде ініціалізувати значення для нових моделей, але тоді це стане сферою, в якій ви знайдете модель. Якщо ви просто хочете ініціалізувати деякі числа до 0, це не те, що ви хочете.
  2. Визначення за замовчуванням у вашій міграції також працює частину часу ... Як уже згадувалося, це не працюватиме, коли ви просто зателефонуєте на Model.new.
  3. Переосмислення initializeможе спрацювати, але не забудьте зателефонувати super!
  4. Використовувати плагін типу phusion стає трохи смішним. Це рубін, чи нам справді потрібен плагін просто для ініціалізації деяких значень за замовчуванням?
  5. Переосмислення after_initialize застаріло, як у Rails 3. Коли я перекриваю after_initializeрейки 3.0.3, на консолі я отримую таке попередження:

ПОПЕРЕДЖЕННЯ ЗАВАНТАЖЕННЯ: База № after_initialize застаріла, замість цього скористайтеся методом Base.after_initialize:. (зателефоновано від / Користувачі / мене / 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 (див. Коментар Cliff Darling нижче)


7
Це, безумовно, є найкращим способом досягти цього. Що насправді дивно і прикро. Розумний кращий метод встановлення типових стандартних атрибутів при створенні здається чимось вбудованим у Rails. Єдиний інший (надійний) спосіб, що переосмислює initialize, просто здається справді викривленим для чогось, що має бути чітким і чітко визначеним. Я проводив години, переглядаючи документацію, перш ніж шукати тут, тому що я припустив, що ця функція вже є десь, і я просто не знав про неї.
seaneshbaugh

106
Одне зауваження щодо цього - якщо у вас є булеве поле, яке ви хочете за замовчуванням, не робіть цього self.bool_field ||= true, оскільки це змусить поле істинно, навіть якщо ви явно ініціалізуєте його на false. Натомість робити self.bool_field = true if self.bool_field.nil?.
Пол Рассел

2
Щодо пункту №2, Model.new насправді працює (лише для мене?) Разом із типовими значеннями, визначеними під час міграцій, а точніше із значеннями за замовчуванням для стовпців таблиці. Але я усвідомлюю, що метод Джеффа, заснований на зворотному виклику after_initialize, мабуть, найкращий спосіб зробити. Лише питання: чи це працює з брудними, але ще не збереженими предметами? У вашому прикладі, Person.new.number_was поверне 0,0?
Лоран Фарсі

21
Обережність при використанні цього підходу у поєднанні з вибором конкретних стовпців із активним записом. У цьому випадку в об'єкті будуть знайдені лише атрибути, вказані в запиті, а код init буде висувати а MissingAttributeError. Ви можете додати додатковий чек, як показано: self.number ||= 0.0 if self.has_attribute? :number Для booleans : 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?щоб уникнути проблем з продуктивністю.
Кайл Мейсі

68

Рейки 5+

Ви можете використовувати метод атрибутів у своїх моделях, наприклад:

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

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

attribute :uuid, UuidType.new, default: -> { SecureRandom.uuid }

3
ааааа, це саме дорогоцінний камінь, який я шукав! за замовчуванням може також братись за програму, наприклад, за замовчуванням: -> {Time.current.to_date}
schpet

1
Не забудьте вказати тип як другий аргумент, інакше тип буде, Valueі не буде здійснено жодного набору даних.
null

на моє захоплення, це також працює з store_accessor, наприклад, з огляду на те, що store_accessor :my_jsonb_column, :localeви можете потім визначитиattribute :locale, :string, default: 'en'
Раян Романчук

О, це фантастично, мені потрібні параметри за замовчуванням, щоб показати у формі, і це чудово працює. Спасибі Лукас.
Пол Уотсон

Ще можна встановити це nil. Якщо вони не можуть бути nilDB not null+ DB за замовчуванням + github.com/sshaw/keep_defaults - це спосіб перейти від мого досвіду
sshaw

47

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

IMHO, цей підхід узгоджується з принципами AR: конвенція щодо конфігурації, DRY, визначення таблиці керує моделлю, а не навпаки.

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


3
Інша проблема полягає в тому, що ви хочете значення за замовчуванням для іноземного ключа. Не можна жорстко кодувати значення ідентифікатора в поле іноземного ключа, оскільки для різних БД ідентифікатор може бути різним.
shmichael

2
Ще одна проблема полягає в тому, що таким чином ви не можете ініціалізувати непостійні аксесуари (атрибути, які не є db стовпцями).
Віктор Трон

2
Інша проблема полягає в тому, що ви не можете обов'язково бачити всі значення за замовчуванням в одному місці. вони можуть бути розсіяні через різні міграції.
деклан

8
declan, там db / schema.rb
Бенджамін Аткін

6
Я хотів би зазначити майбутніх читачів: принаймні з того, що я прочитав, це суперечить принципам AR. Логіка моделей повинна лежати в межах модельних класів, а бази даних повинні бути максимально неосвіченими. Значення за замовчуванням для мене складають специфічну логіку щодо моделі.
поспішає

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
Це дійсно важливий момент. Я маю уявити, що в більшості випадків встановлення за замовчуванням для запису робиться лише до збереження нового запису, а не при завантаженні збереженої записи.
Рассел Сілва

Thx чувак, ти врятував мій день.
Стефанфрідріх

2
Як щодо :before_create?
Франклін Ю

Як: before_create обробляє окремі нові та зберігає дзвінки? Я хотів би перевірити це і справді зрозуміти, перш ніж переходити на нього.
Джозеф Лорд

17

Шаблон зворотного виклику after_initialize можна поліпшити, просто виконавши наступне

after_initialize :some_method_goes_here, :if => :new_record?

Це має нетривіальну перевагу, якщо ваш код init потребує взаємодії з асоціаціями, оскільки наступний код запускає тонкий 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.
jchook

Я можу змусити :defaultцінності міграції «просто працювати» Model.new, всупереч тому, що сказав Джефф у своїй посаді. Перевірено, що працює в рейках 4.1.16.
Магне

8

Ще кращим / чистішим потенційним способом, ніж запропоновані відповіді, є перезапис аксесуара:

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

Див. "Перезапис аксесуарів за замовчуванням" в ActiveRecord :: Base документація та багато іншого з StackOverflow щодо використання self .


Статус все одно буде нульовим у хеші, поверненому з attributes. Випробувано в рейках 5.2.0.
шпиль

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

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

Найкраща відповідь: залежить від того, що ви хочете!

Якщо ви хочете, щоб кожен об'єкт починався зі значення: використовуйте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. Перевірено, що працює в рейках 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

Перегляньте офіційний посібник з довідкових запитів щодо параметрів методів зміни стовпців.

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

Канонічний шлях у Rails 5+ - це, як сказав @Lucas Caton:

class Item < ActiveRecord::Base
  attribute :scheduler_type, :string, default: 'hotseat'
end

1

Проблема з рішеннями after_initialize полягає в тому, що вам потрібно додати after_initialize до кожного об'єкта, який ви шукаєте з БД, незалежно від того, отримуєте ви цей атрибут чи ні. Я пропоную ледачий підхід.

Методи атрибутів (getters), звичайно, є самими методами, тому ви можете їх перекрити і надати за замовчуванням. Щось на зразок:

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 ("АКТИВНІ"). У цьому випадку я думаю, що вам дійсно потрібно буде встановити типові обмеження у вашій базі даних, якщо БД підтримує це.


Це рішення та запропонована альтернатива не працюють в моєму випадку: у мене є ієрархія класів 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хеші.
шпиль

1
class Item < ActiveRecord::Base
  def status
    self[:status] or ACTIVE
  end

  before_save{ self.status ||= ACTIVE }
end

2
Mmmhh ... Спочатку здається геніальним, але, подумавши трохи, я бачу кілька проблем. По-перше, всі значення за замовчуванням знаходяться не в одній точці, а розкидані по класу (уявіть їх пошук чи зміну). По-друге, і найгірше, згодом ви не можете поставити нульове значення (або навіть помилкове!).
paradoja

навіщо вам потрібно встановити нульове значення за замовчуванням? Ви виймаєте це з коробки з AR, не роблячи взагалі нічого. Що стосується помилок при використанні булевого стовпчика, то ви праві, це не найкращий підхід.
Майк Брін

Я не можу говорити за інших звичаїв кодування, у мене не було проблеми, тому що я не розкидаю свої геттери / сетери навколо файлу класу. Крім того, будь-який сучасний текстовий редактор повинен полегшити перехід до методу (shift-cmd-t у текстовому папері).
Майк Брін

@paradoja - Я повертаю це назад, тепер я бачу, де воно виходить з ладу і з використанням null. Не обов’язково використовувати null як типовий, але якщо ви насправді хотіли змінити значення на null в якийсь момент. Хороший улов @paradoja, дякую.
Майк Брін

Я використовую цей метод, оскільки він добре працює з динамічно створеними атрибутами.
Дейл Кемпбелл

0

Хоча зробити це для встановлення значень за замовчуванням в більшості випадків заплутано і незручно, ви можете також використовувати :default_scope. Ознайомтеся з коментарем squil тут .


0

Метод after_initialize застарілий, замість цього використовуйте зворотний виклик.

after_initialize :defaults

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

однак використання : за замовчуванням для міграцій все ще є найчистішим способом.


4
У Rails 3: after_initializeметод є НЕ рекомендується . Насправді зворотний виклик макро-стилю, який ви наводите як приклад застарілого IS . Детальніше: guides.rubyonrails.org/…
Забба

0

Я виявив, що використання методу перевірки забезпечує великий контроль над налаштуваннями за замовчуванням. Ви навіть можете встановити за замовчуванням (або помилку перевірки) для оновлень. Ви навіть встановлюєте інше значення за замовчуванням для вставок проти оновлень, якщо цього дуже хотіли. Зауважте, що типово значення не буде встановлено до # недійсного? це називається.

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 Це хороший момент, і я можу зрозуміти, чому ви цього хотіли іноді, але ОП не згадувало це як вимогу. Ви повинні вирішити, чи хочете ви накладні витрати на встановлення значень за замовчуванням для кожного примірника, навіть якщо це не збережено. Альтернативою є збереження манекенового предмета навколо newдії для повторного використання.
Келвін

0

Якщо стовпець є стовпцем типу "статус", і ваша модель піддається використанню державних машин, подумайте про використання дорогоцінного каміння Aasm , після чого ви можете просто зробити

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

Він все ще не ініціалізує значення для збережених записів, але це трохи чистіше, ніж прокручувати свої власні initчи з чим завгодно, і ви пожинаєте інші переваги аазму, такі як сфери застосування для всіх ваших статусів.



0

Я настійно пропоную використовувати дорогоцінний камінь "default_value_for": https://github.com/FooBarWidget/default_value_for

Є кілька складних сценаріїв, які дуже потребують переосмислення методу ініціалізації, що робить цей дорогоцінний камінь.

Приклади:

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

Більшість рішень тут не зможуть встановити значення нульовим, а замість цього встановлять його за замовчуванням.

Гаразд, тож замість того, щоб приймати ||=підхід, ви переходите до my_attr_changed?...

Але тепер уявіть, що вашим db за замовчуванням є "деяка рядок", ваша модель / визначений в рубіні за замовчуванням "якась інша рядок", але за певного сценарію ви хочете встановити значення "деяка рядок" (db за замовчуванням):MyModel.new(my_attr: 'some_string')

Це призведе до my_attr_changed?бути помилковим , оскільки значення відповідає дб по замовчуванням, який , в свою чергу , буде спрацьовувати ваш рубіновий певний код за замовчуванням і встановити значення «який - то інша рядок» - знову ж , не те , що ви хотіли.


З цих причин я не вважаю, що це можна зробити належним чином лише за допомогою гачка after_initialize.

Знову ж таки, я думаю, що дорогоцінний камінь "default_value_for" бере правильний підхід: https://github.com/FooBarWidget/default_value_for


0

Ось рішення, яке я використав, що я трохи здивований, ще не доданий.

Є дві частини. Перша частина - це встановлення за замовчуванням фактичної міграції, а друга частина - додавання валідації в модель, що забезпечує наявність справжнього.

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

Тож ви побачите тут, що за замовчуванням уже встановлено. Тепер у валідації ви хочете переконатися, що для рядка завжди є значення, так що просто зробіть

 validates :new_team_signature, presence: true

Для цього потрібно встановити значення за замовчуванням для вас. (для мене я "Ласкаво просимо до команди"), і тоді це піде на крок далі, щоб гарантувати, що для цього об'єкта завжди є значення.

Сподіваюся, що це допомагає!


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

-2

використовувати default_scope в рейках 3

api док

ActiveRecord приховує різницю між дефолтом, визначеним у базі даних (схемою), і дефолтом, зробленим у додатку (моделі). Під час ініціалізації він аналізує схему бази даних і відзначає всі вказані там значення за замовчуванням. Пізніше, створюючи об'єкти, він призначає ці значення, визначені схемою за замовчуванням, не торкаючись бази даних.

обговорення


якщо ви використовуєте meta_where, default_scope може не працювати для призначення за замовчуванням новим об’єктам AR через помилку.
Віктор Трон

ця проблема мета-десь уже виправлена ​​[ metautonomous.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

Здивований, що його тут не запропонували


before_validation не встановить за замовчуванням, поки об'єкт не буде готовий до збереження. Якщо процес потребує зчитування за замовчуванням до збереження, значення не будуть готові.
mmell

Ви ніколи не встановлюєте значення за замовчуванням під час перевірки перевірок. Це навіть не будь-який хак. Робіть це під час ініціалізації
Сачин
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.