Как я могу установить значение по умолчанию в 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?
Ответы:
Есть несколько проблем с каждым из доступных методов, но я считаю, что определение
after_initialize
обратного вызова - это путь по следующим причинам:default_scope
будет инициализировать значения для новых моделей, но тогда это станет областью, в которой вы найдете модель. Если вы просто хотите инициализировать некоторые числа в 0, то это не то, что вы хотите.initialize
может работать, но не забудьте позвонитьsuper
!after_initialize
устарело с Rails 3. Когда я переопределяюafter_initialize
в rails 3.0.3, я получаю следующее предупреждение в консоли:Поэтому я бы сказал написать
after_initialize
обратный вызов, который позволяет вам использовать атрибуты по умолчанию в дополнение к тому, что вы можете установить значения по умолчанию для ассоциаций следующим образом:Теперь у вас есть только одно место для поиска инициализации ваших моделей. Я использую этот метод, пока кто-то не придумает лучшего.
Предостережения:
Для логических полей:
self.bool_field = true if self.bool_field.nil?
Смотрите комментарий Пола Рассела к этому ответу для более подробной информации.
Если вы выбираете только подмножество столбцов для модели (т. Е. Используете
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 был другим (см. Комментарий Клиффа Дарлинга ниже)
источник
initialize
, просто кажется действительно замысловатым для чего-то, что должно быть ясно и четко определено. Я провел часы, просматривая документацию, прежде чем искать здесь, потому что я предполагал, что эта функция уже была где-то там, и я просто не знал об этом.self.bool_field ||= true
поводу - если у вас есть логическое поле, которое вы хотите использовать по умолчанию, не делайте этого , поскольку это заставит поле иметь значение true, даже если вы явно инициализируете его как false. Вместо этого делайself.bool_field = true if self.bool_field.nil?
.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?
, и вам нужна строка вместо символа.initialize
с того,return if !new_record?
чтобы избежать проблем с производительностью.Рельсы 5+
Вы можете использовать метод атрибута в своих моделях, например:
Вы также можете передать лямбду в
default
параметр. Пример:источник
Value
и преобразование типов не будет выполнено.store_accessor :my_jsonb_column, :locale
вы можете определитьattribute :locale, :string, default: 'en'
nil
. Если они не могут бытьnil
БДnot null
+ БД по умолчанию + github.com/sshaw/keep_defaults - это путь из моего опытаМы помещаем значения по умолчанию в базу данных посредством миграций (указав
:default
параметр в каждом определении столбца) и позволяем Active Record использовать эти значения, чтобы установить значение по умолчанию для каждого атрибута.ИМХО, этот подход согласуется с принципами AR: соглашение по конфигурации, DRY, определение таблицы определяет модель, а не наоборот.
Обратите внимание, что значения по умолчанию все еще находятся в коде приложения (Ruby), но не в модели, а в миграциях.
источник
Некоторые простые случаи могут быть обработаны путем определения значения по умолчанию в схеме базы данных, но это не обрабатывает ряд более сложных случаев, включая вычисленные значения и ключи других моделей. Для этих случаев я делаю это:
Я решил использовать after_initialize, но не хочу, чтобы он применялся к объектам, которые были найдены только новыми или созданными. Я думаю, что это почти шокирует, что обратный вызов after_new не предусмотрен для этого очевидного варианта использования, но я справился, подтвердив, сохранился ли объект уже, указывая, что он не новый.
Увидев ответ Брэда Мюррея, становится еще чище, если условие переносится в запрос обратного вызова:
источник
:before_create
?Шаблон обратного вызова after_initialize можно улучшить, просто выполнив следующее
Это имеет нетривиальное преимущество, если ваш код инициализации должен иметь дело с ассоциациями, так как следующий код вызывает тонкое n + 1, если вы читаете начальную запись, не включая связанную с ней.
источник
У парней Phusion есть хороший плагин для этого.
источник
:default
«просто работать» со значениями в миграциях схемыModel.new
.:default
ценности в миграциях «просто работать»Model.new
, вопреки тому, что сказал Джефф в своем посте. Проверено работает в Rails 4.1.16.Еще лучше / чище потенциальный способ, чем предложенные ответы, - переписать метод доступа, например так:
См. «Перезапись доступа по умолчанию» в документации ActiveRecord :: Base и многое другое из StackOverflow по использованию self .
источник
attributes
. Проверено на рельсах 5.2.0.Я использую
attribute-defaults
драгоценный каменьИз документации: запустите
sudo gem install attribute-defaults
и добавьтеrequire 'attribute_defaults'
в свое приложение.источник
Подобные вопросы, но все они имеют немного другой контекст: - Как мне создать значение по умолчанию для атрибутов в модели Rails activerecord?
Лучший ответ: зависит от того, что вы хотите!
Если вы хотите, чтобы каждый объект начинался со значения: используйте
after_initialize :init
Вы хотите, чтобы
new.html
форма открывала страницу по умолчанию? используйте https://stackoverflow.com/a/5127684/1536309Если вы хотите, чтобы каждый объект имел значение, рассчитанное на основе пользовательского ввода: используйте
before_save :default_values
Вы хотите, чтобы пользователь ввел,X
а затемY = X+'foo'
? использовать:источник
Для этого и нужны конструкторы! Переопределите метод моделиinitialize
.Используйте
after_initialize
метод.источник
after_initialize
метод вместо этого.Привет, ребята, я закончил тем, что сделал следующее:
Работает как шарм!
источник
На это уже давно отвечают, но мне часто нужны значения по умолчанию, и я предпочитаю не помещать их в базу данных. Я создаю
DefaultValues
проблему:А затем используйте его в моих моделях так:
источник
Канонический способ Rails, до Rails 5, на самом деле заключался в том, чтобы установить его при переносе, и просто искать в
db/schema.rb
каждый раз, когда хочет увидеть, какие значения по умолчанию устанавливаются БД для любой модели.Вопреки тому, что в ответе @Jeff Perrin говорится (что немного устарело), подход к миграции будет даже применять по умолчанию при использовании
Model.new
из-за некоторой магии Rails. Проверено работает в Rails 4.1.16.Самая простая вещь часто самая лучшая. Меньшая задолженность по знаниям и потенциальная путаница в кодовой базе. И это «просто работает».
Или для изменения столбца без создания нового, выполните одно из следующих действий:
Или, может быть, даже лучше:
Проверьте официальное руководство RoR для вариантов в методах изменения столбца.
В
null: false
Запрещает NULL значения в БД, и, как дополнительное преимущество, он также обновляет так , что все предварительно существующие БД записи , которые ранее были нуль устанавливается значение по умолчанию для этого поля , а также. Вы можете исключить этот параметр при переносе, если хотите, но я нашел его очень удобным!Канонический путь в Rails 5+, как сказал @Lucas Caton:
источник
Проблема с решениями after_initialize заключается в том, что вы должны добавить after_initialize к каждому объекту, который вы просматриваете из БД, независимо от того, имеете ли вы доступ к этому атрибуту или нет. Я предлагаю ленивый подход.
Методы-атрибуты (методы получения), конечно, сами являются методами, поэтому вы можете переопределить их и предоставить значение по умолчанию. Что-то вроде:
Если, как кто-то указал, вам нужно выполнить Foo.find_by_status ('ACTIVE'). В этом случае, я думаю, вам действительно нужно установить ограничения по умолчанию в вашей базе данных, если БД это поддерживает.
источник
Я столкнулся с проблемами с
after_initialize
выдачейActiveModel::MissingAttributeError
ошибок при выполнении сложных находок:например:
«поиск» в
.where
хеше условийВ итоге я сделал это путем переопределения инициализации следующим образом:
super
Вызов необходимо убедиться , что объект правильно инициализируется с ,ActiveRecord::Base
прежде чем делать свой подгоняет код, то есть: default_valuesисточник
def initialize(*); super; default_values; end
в Rails 5.2.0. Кроме того, значение по умолчанию доступно даже в.attributes
хэше.источник
Хотя сделать это для установки значений по умолчанию в большинстве случаев непонятно и неудобно, вы также можете использовать их
:default_scope
. Проверьте комментарий Squil здесь .источник
Метод after_initialize устарел, вместо этого используйте обратный вызов.
однако использование : default в ваших миграциях по-прежнему является самым чистым способом.
источник
after_initialize
метод является НЕ рекомендуется . На самом деле обратный вызов в стиле макроса, который вы привели в качестве примера, устарел . Подробности: guides.rubyonrails.org/…Я обнаружил, что использование метода проверки обеспечивает большой контроль над настройками по умолчанию. Вы даже можете установить значения по умолчанию (или провалить проверку) для обновлений. Вы даже можете установить другое значение по умолчанию для вставок и обновлений, если вы действительно этого хотите. Обратите внимание, что значение по умолчанию не будет установлено до #valid? называется.
Относительно определения метода after_initialize могут быть проблемы с производительностью, поскольку after_initialize также вызывается каждым объектом, возвращаемым: find: http://guides.rubyonrails.org/active_record_validations_callbacks.html#after_initialize-and-after_find
источник
new
повторного использования действия.Если столбец является столбцом типа «состояние», и ваша модель пригодна для использования конечных автоматов, рассмотрите возможность использования гема aasm , после чего вы можете просто сделать
Он по-прежнему не инициализирует значение для несохраненных записей, но он немного чище, чем использование собственного
init
или чего-то еще, и вы пожинаете другие преимущества Aasm, такие как область действия для всех ваших статусов.источник
https://github.com/keithrowell/rails_default_value
источник
Я настоятельно рекомендую использовать гем "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
источник
Вот решение, которое я использовал, что я был немного удивлен, еще не было добавлено.
Есть две части к этому. Первая часть устанавливает значение по умолчанию для фактической миграции, а вторая часть добавляет проверку в модель, гарантирующую, что присутствие является истинным.
Итак, вы увидите здесь, что значение по умолчанию уже установлено. Теперь в проверке вы хотите убедиться, что всегда есть значение для строки, поэтому просто сделайте
Что это будет делать, это установить значение по умолчанию для вас. (для меня у меня есть «Добро пожаловать в команду»), а затем он пойдет на шаг вперед, гарантируя, что для этого объекта всегда присутствует значение.
Надеюсь, это поможет!
источник
источник
использовать default_scope в рельсах 3
Api Doc
ActiveRecord скрывает разницу между значениями по умолчанию, определенными в базе данных (схема), и значениями по умолчанию, выполненными в приложении (модель). Во время инициализации он анализирует схему базы данных и записывает любые значения по умолчанию, указанные там. Позже, при создании объектов, он назначает эти значения по умолчанию, заданные схемой, не касаясь базы данных.
обсуждение
источник
default_scope
. Это заставит все ваши запросы добавить это условие в заданное вами поле. Это почти никогда, что вы хотите.Из документации API http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html Используйте
before_validation
метод в вашей модели, он дает вам возможность создания определенной инициализации для вызовов создания и обновления, например, в этом примере (опять код взят из примера api docs) поле номера инициализируется для кредитной карты. Вы можете легко адаптировать это, чтобы установить любые значения, которые вы хотитеУдивлен, что его здесь не предложили
источник