使用带有序列化自定义类的Rails表单助手

时间:2010-09-21 19:46:20

标签: ruby-on-rails ruby serialization activerecord ruby-on-rails-3

我正在尝试在单个数据库字段中保存选项的哈希值。表单能够将数据保存到数据库,但是当我去编辑它时无法再次检索它(例如,除了wp_options字段之外,所有其他字段都是预先填充的)。

class Profile < ActiveRecord::Base  
 serialize :wp_options  
end

这是我的自定义类:

class WP_Options
 attr_accessor :wp_name, :wp_desc, :wp_limit
end

以我的形式:

<%= form_for(@profile, :remote => true) do |f| %>
 ...
   <%= f.fields_for :wp_options do |wp_options| %>
      <%= wp_options.text_field :wp_name  %>
   <% end %>
 ...

在我的控制器中:

@profile = Profile.new(:wp_options => WP_Options.new)

在我的数据库专栏'wp_options'中:

--- !map:ActiveSupport::HashWithIndifferentAccess 
wp_name: Test  

任何建议都会非常感激。

3 个答案:

答案 0 :(得分:11)

实际上这很容易。您需要使用具有reader方法的类。您可以使用不同的方式创建它,但最简单的方法是使用OpenStruct类(注意,它将无法查看OpenStruct实例方法中的字段...此类无法重新定义方法)。

在您的表单中,您应该添加:

<%= f.fields_for :wp_options, @profile.wp_options do |wp_options| %>

而不是@profile(如果你有动态变量),你可以使用f.object.wp_options

对于模型Profile,您应该添加wp_options方法。

def wp_options
  OpenStruct.new(self.attributes['wp_options'])
end

在这种情况下,只有序列化的wp_options是Hash类才会有效。

希望有所帮助。

PS。我使用相同的技术,但因为我有type哈希键,OpenStruct无法创建它,所以我使用了简单的Struct类。我有data列:

def data
  keys = current_data.keys
  data = attributes[:data]
  Struct.new(*keys).new(*keys.map { |k| data[k] })
end

有点不那么琐碎,但无论如何都采用相同的方法(在此之前我创建了特殊的类,但现在我知道Struct是创建这类东西的更好方法。你可以在这里找到更多的想法:{{3 }})

答案 1 :(得分:1)

对于那些使用Dmitry解决方案的人,在rails 3.0.5上(不确定上一个/下一个版本,所以之前尝试),添加

require 'ostruct'

到您的environment.rb文件,以便包含OpenStruct。

如果您要使用JSON进行序列化,请在您的模型中添加(哦,我对RoR很新,所以请随时仔细检查并建议一个更好的“工作”解决方案然后这个)(我的变量名称已更改为与上面的示例匹配):

before_save :json_serialize
after_save  :json_deserialize
after_find  :json_deserialize

def json_serialize  
  self.wp_options = ActiveSupport::JSON.encode(self.attributes['wp_options'])
end

def json_deserialize
  unless (self.attributes['wp_options'].nil?)
    self.wp_options = ActiveSupport::JSON.decode(self.attributes['wp_options'].to_s)
  end
end

最后一点:我真的不确定为什么必须存储到self.wp_options而不是self.attributes ['wp_options'],如果有人能解释它会很酷

答案 2 :(得分:0)

我遇到了同样的问题。我的解决方案如下:

  1. 在帮助程序中定义方法:

    def wp_options_text_field(profile, wp_options, name)
      wp_options.text_field name, :value => profile.wp_options[name]
    end
    
  2. 在您看来:

    <%= form_for(@profile, :remote => true) do |f| %>
    ...
    <%= f.fields_for :wp_options do |wp_options| %>
      <%= wp_options_text_field :wp_name  %>
    <% end %>
    ...
    
  3. 这里唯一的问题是你需要为你将使用的每个帮助方法定义方法。在我的情况下,我只使用了2种方法,并没有痛苦。