ActiveRecord::使用默认值存储

Posted

技术标签:

【中文标题】ActiveRecord::使用默认值存储【英文标题】:ActiveRecord::Store with default values 【发布时间】:2012-03-28 12:28:36 【问题描述】:

使用新的 ActiveRecord::Store 进行序列化,the docs 给出如下示例实现:

class User < ActiveRecord::Base
  store :settings, accessors: [ :color, :homepage ]
end

是否可以使用默认值声明属性,类似于:

store :settings, accessors:  color: 'blue', homepage: 'rubyonrails.org' 

?

【问题讨论】:

【参考方案1】:

不,没有办法在 store 调用中提供默认值。 store macro 很简单:

def store(store_attribute, options = )
  serialize store_attribute, Hash
  store_accessor(store_attribute, options[:accessors]) if options.has_key? :accessors
end

所有store_accessor 所做的只是遍历:accessors 并为每个方法创建访问器和修改器方法。如果您尝试将哈希与 :accessors 一起使用,您最终会在您的 store 中添加一些您无意添加的内容。

如果你想提供默认值,那么你可以使用 after_initialize 钩子:

class User < ActiveRecord::Base
  store :settings, accessors: [ :color, :homepage ]
  after_initialize :initialize_defaults, :if => :new_record?
private
  def initialize_defaults
    self.color    = 'blue'            unless(color_changed?)
    self.homepage = 'rubyonrails.org' unless(homepage_changed?)
  end
end

【讨论】:

+1, @mu 通常在这种情况下我使用set if not set 成语,即self.color ||= 'blue'; self.homepage ||= 'rubyonrails.org'。这避免了dirty 检查.. @KandadaBoggu:||= 的唯一缺点是如果你有布尔属性,使用脏值会使它们都保持一致。可惜没有像 Perl 的 //= 这样的“如果未定义则设置”。 是的,没错,如果布尔值使用||= idiom,就必须区别对待。【参考方案2】:

我也想解决这个问题,最终贡献了Storext:

class Book < ActiveRecord::Base
  include Storext.model

  # You can define attributes on the :data hstore column like this:
  store_attributes :data do
    author String
    title String, default: "Great Voyage"
    available Boolean, default: true
    copies Integer, default: 0
  end
end

【讨论】:

【参考方案3】:

尝试使用https://github.com/byroot/activerecord-typedstore gem。它允许您设置默认值,使用验证结束其他。

【讨论】:

【参考方案4】:

以下代码的优点是默认值不会保存在每个用户记录上,这减少了数据库存储的使用,并且在您想要更改默认值时很容易

class User < ApplicationRecord
  DEFAULT_SETTINGS =  color: 'blue', homepage: 'rubyonrails.org' 

  store :settings, accessors: DEFAULT_SETTINGS.keys

  DEFAULT_SETTINGS.each do |key,value|
    define_method(key) 
      settings[key] or value
    
  end
end

【讨论】:

【参考方案5】:

这是我为了解决这个问题而拼凑起来的:

# migration
  def change
    add_column :my_objects, :settings, :text
  end

# app/models/concerns/settings_accessors_with_defaults.rb
module SettingsAccessorsWithDefaults
  extend ActiveSupport::Concern

  included do
    serialize :settings, Hash
    cattr_reader :default_settings
  end

  def settings
    self.class.default_settings.merge(self[:settings])
  end

  def restore_setting_to_default(key)
    self[:settings].delete key
  end

  module ClassMethods
    def load_default_settings(accessors_and_values)
      self.class_variable_set '@@default_settings', accessors_and_values

      self.default_settings.keys.each do |key|
        define_method("#key=") do |value|
          self[:settings][key.to_sym] = value
        end

        define_method(key) do
          self.settings[key.to_sym]
        end
      end
    end
  end
end

# app/models/my_object.rb
  include SettingsAccessorsWithDefaults
  load_default_settings(
    attribute_1: 'default_value',
    attribute_2: 'default_value_2'
  )
  validates :attribute_1, presence: true


irb(main):004:0> MyObject.default_settings
=> :attribute_1=>'default_value', :attribute_2=>'default_value_2'
irb(main):005:0> m = MyObject.last
=> #<MyObject ..., settings: >
irb(main):005:0> m.settings
=> :attribute_1=>'default_value', :attribute_2=>'default_value_2'
irb(main):007:0> m.attribute_1 = 'foo'
=> "foo"
irb(main):008:0> m.settings
=> :attribute_1=>"foo", :attribute_2=>'default_value_2'
irb(main):009:0> m
=> #<MyObject ..., settings: :attribute_1=>"foo">

【讨论】:

以上是关于ActiveRecord::使用默认值存储的主要内容,如果未能解决你的问题,请参考以下文章

Rails:如何为 Rails activerecord 模型中的属性创建默认值? [复制]

为啥 Rails 5 使用 ApplicationRecord 而不是 ActiveRecord::Base?

为 ActiveMerchant CreditCard 对象实现 find()、save() ActiveRecord 方法

Rails ActiveRecord Timestamp in Epoch 而不是 DateTime 格式

在 Ruby on Rails 3.2.14 / Ruby 2.0.0 / PostgreSQL 9.2.4 中使用 activerecord 从序列中检索 nextval

如何存储和比较:ActiveRecord 中的符号(Ruby on Rails)