Подтвердить что ты не робот

Как отредактировать сериализованное поле Rails в форме?

У меня есть модель данных в моем проекте Rails с сериализованным полем:

class Widget < ActiveRecord::Base
  serialize :options
end

Поле опций может содержать информацию о переменных данных. Например, вот поле опций для одной записи из файла приборов:

  options:
    query_id: 2 
    axis_y: 'percent'
    axis_x: 'text'
    units: '%'
    css_class: 'occupancy'
    dom_hook: '#average-occupancy-by-day'
    table_scale: 1

Мой вопрос в том, как правильно разрешить пользователю редактировать эту информацию в стандартном представлении формы?

Если вы просто используете поле простой текстовой области для поля параметров, вы просто получите представление дампа yaml и данные будут просто отправлены обратно в виде строки.

Каков наилучший/правильный способ редактирования сериализованного хэш-поля, подобного этому в Rails?

4b9b3361

Ответ 1

Если вы знаете, какие ключи опций будут заранее, вы можете объявить для них специальные геттеры и сеттеры следующим образом:

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

Самое приятное в том, что он предоставляет компоненты массива параметров в качестве атрибутов, что позволяет использовать помощники формы Rails следующим образом:

#haml
- form_for @widget do |f|
  = f.text_field :axis_y
  = f.text_field :axis_x
  = f.text_field :unit

Ответ 2

Ну, у меня была такая же проблема, и я старался не переучивать ее. Проблема в том, что, хотя вы можете передать сериализованный хеш в поля_for, поля для функции будут думать, это хэш-вариант (а не ваш объект) и установить для объекта формы значение nil. Это означает, что, хотя вы можете редактировать значения, они не появятся после редактирования. Это может быть ошибка или неожиданное поведение рельсов и, возможно, исправление в будущем.

Однако на данный момент довольно легко заставить его работать (хотя мне потребовалось все утро, чтобы понять).

Вы можете оставить модель как есть, и в представлении вам нужно указать поля для объекта как открытой структуры. Это правильно установит объект записи (так что f2.object вернет ваши параметры), а во-вторых, позволяет конструктору text_field получить доступ к значению из вашего объекта /params.

Так как я включил "|| {}", он также будет работать с новыми/создаваемыми формами.

= 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

Отличный день

Ответ 3

emh почти есть. Я бы подумал, что 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 %>

Любое поле, которое вы добавляете в поле "fields_for", будет отображаться в сериализованном хэше. Вы можете добавлять или удалять поля по своему усмотрению. Они будут переданы как атрибуты хеша "options" и сохранены как YAML.

Ответ 4

Не требуется setter/getters, я просто определил в модели:

serialize :content_hash, Hash

Тогда в представлении я делаю (с помощью simple_form, но аналогично с vanilla Rails):

  = f.simple_fields_for :content_hash do |chf|
    - @model_instance.content_hash.each_pair do |k,v|
      =chf.input k.to_sym, :as => :string, :input_html => {:value => v}

Моя последняя проблема заключается в том, как дать пользователю возможность добавить новую пару ключ/значение.

Ответ 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 %>

а затем в контроллере мне пришлось обновить сильные параметры следующим образом:

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

Кажется важным, чтобы сериализованный хеш-параметр попал в конец списка параметров. В противном случае Rails ожидает, что следующий параметр также будет сериализованным хэшем.

В представлении я использовал некоторую простую логику if/then, чтобы отображать только хэш, если он не пуст, а затем отображать только пары ключ/значение, где значение не равно нулю.

Ответ 6

Я пытаюсь сделать что-то подобное, и я нашел такие работы:

<%= form_for @search do |f| %>
    <%= f.fields_for :params, @search.params do |p| %>
        <%= p.select "property_id", [[ "All", 0 ]] + PropertyType.all.collect { |pt| [ pt.value, pt.id ] } %>

        <%= p.text_field :min_square_footage, :size => 10, :placeholder => "Min" %>
        <%= p.text_field :max_square_footage, :size => 10, :placeholder => "Max" %>
    <% end %>
<% end %>

за исключением того, что поля формы не заполняются при визуализации формы. когда форма отправляется, значения проходят через штраф, и я могу сделать:

@search = Search.new(params[:search])

поэтому его "половина" работает...

Ответ 7

Я предлагаю что-то простое, потому что все время, когда пользователь будет сохранять форму, вы получите строку. Таким образом, вы можете использовать, например, перед фильтром и анализировать такие данные:

before_save do
  widget.options = YAML.parse(widget.options).to_ruby
end 

Конечно, вы должны добавить проверку, если это правильный YAML. Но он должен работать.