如何在表单中编辑Rails序列化字段?

cpj*_*eur 56 forms serialization ruby-on-rails

我的Rails项目中有一个数据模型,它有一个序列化字段:

class Widget < ActiveRecord::Base
  serialize :options
end
Run Code Online (Sandbox Code Playgroud)

选项字段可以包含可变数据信息.例如,以下是fixtures文件中一条记录的选项字段:

  options:
    query_id: 2 
    axis_y: 'percent'
    axis_x: 'text'
    units: '%'
    css_class: 'occupancy'
    dom_hook: '#average-occupancy-by-day'
    table_scale: 1
Run Code Online (Sandbox Code Playgroud)

我的问题是让用户在标准表单视图中编辑此信息的正确方法是什么?

如果您只是为选项字段使用一个简单的文本区域字段,您将只获得一个yaml转储表示,并且该数据将仅作为字符串发回.

在Rails中编辑这样的序列化哈希字段的最佳/正确方法是什么?

aus*_*ton 57

如果你事先知道选项键是什么,你可以为它们声明特殊的getter和setter,如下所示:

class Widget < ActiveRecord::Base
  serialize :options

  def self.serialized_attr_accessor(*args)
    args.each do |method_name|
      eval "
        def #{method_name}
          (self.options || {})[:#{method_name}]
        end
        def #{method_name}=(value)
          self.options ||= {}
          self.options[:#{method_name}] = value
        end
        attr_accessible :#{method_name}
      "
    end
  end

  serialized_attr_accessor :query_id, :axis_y, :axis_x, :units
end
Run Code Online (Sandbox Code Playgroud)

关于这个的好处是它将options数组的组件作为属性公开,这允许你像这样使用Rails表单助手:

#haml
- form_for @widget do |f|
  = f.text_field :axis_y
  = f.text_field :axis_x
  = f.text_field :unit
Run Code Online (Sandbox Code Playgroud)


Chr*_*zke 37

好吧,我有同样的问题,并试图不过度设计它.问题是,尽管您可以将序列化哈希传递给fields_for,但函数的字段会认为,它是一个选项哈希(而不是您的对象),并将表单对象设置为nil.这意味着,虽然您可以编辑这些值,但在编辑后它们不会出现.它可能是rails的错误或意外行为,并且可能在将来修复.

然而,就目前而言,它很容易让它工作(尽管我整个上午花了很多时间才弄明白).

您可以按原样保留模型,在视图中需要为对象提供字段作为开放结构.这将正确设置记录对象(因此f2.object将返回您的选项),其次它允许text_field构建器访问您的对象/参数的值.

由于我包含了"|| {}",因此它也适用于new/create表单.

= form_for @widget do |f|
  = f.fields_for :options, OpenStruct.new(f.object.options || {}) do |f2|
    = f2.text_field :axis_y
    = f2.text_field :axis_x
    = f2.text_field :unit
Run Code Online (Sandbox Code Playgroud)

祝你有美好的一天

  • 我认为有一点错误:你应该在`f2`上调用`text_field`,而不是`f` (2认同)

sbz*_*oom 29

呃几乎就在那里.我认为Rails会将值返回到表单字段,但事实并非如此.因此,您可以在每个字段的":value =>"参数中手动将其放在那里.它看起来不漂亮,但它确实有效.

这是从上到下:

class Widget < ActiveRecord::Base
    serialize :options, Hash
end

<%= form_for :widget, @widget, :url => {:action => "update"}, :html => {:method => :put} do |f| %>
<%= f.error_messages %>
    <%= f.fields_for :options do |o| %>
        <%= o.text_field :axis_x, :size => 10, :value => @widget.options["axis_x"] %>
        <%= o.text_field :axis_y, :size => 10, :value => @widget.options["axis_y"] %>
    <% end %>
<% end %>
Run Code Online (Sandbox Code Playgroud)

您在"fields_for"中添加的任何字段都将显示在序列化哈希中.您可以随意添加或删除字段.它们将作为属性传递给"options"哈希并存储为YAML.


小智 5

我一直在努力解决一个非常相似的问题。我在这里找到的解决方案对我非常有帮助。谢谢@austinfromboston、@Christian-Butske、@sbzoom 和其他所有人。但是,我认为这些答案可能有点过时了。以下是 Rails 5 和 ruby​​ 2.3 对我有用的内容:

在形式:

<%= f.label :options %>
<%= f.fields_for :options do |o| %>
  <%= o.label :axis_y %>
  <%= o.text_field :axis_y %>
  <%= o.label :axis_x %>
  <%= o.text_field :axis_x %>
  ...
<% end %>
Run Code Online (Sandbox Code Playgroud)

然后在控制器中,我必须像这样更新强参数:

def widget_params
    params.require(:widget).permit(:any, :regular, :parameters, :options => [:axis_y, :axis_x, ...])
end
Run Code Online (Sandbox Code Playgroud)

序列化哈希参数位于参数列表的末尾似乎很重要。否则,Rails 会期望下一个参数也是一个序列化的哈希值。

在视图中,我使用了一些简单的 if/then 逻辑来仅显示非空的哈希,然后仅显示值不为零的键/值对。