Как редактировать сериализованное поле Rails в форме? - PullRequest
56 голосов
/ 16 июня 2009

В моем проекте 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?

Ответы [ 8 ]

57 голосов
/ 17 июня 2009

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

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
38 голосов
/ 21 мая 2012

Ну, у меня была та же проблема, и я старался не перегружать ее. Проблема в том, что, хотя вы можете передать сериализованный хеш в fields_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

Хорошего дня

29 голосов
/ 26 января 2011

Эмм почти там. Я думаю, что 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 голосов
/ 31 августа 2016

Я боролся с очень похожей проблемой. Решения, которые я нашел здесь, были очень полезны для меня. Спасибо @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, чтобы отображать хеш, только если он не пустой, а затем отображать только пары ключ / значение, в которых значение не равно nil.

3 голосов
/ 07 мая 2012

Нет необходимости в сеттере / геттере, я только что определил в модели:

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}

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

2 голосов
/ 21 апреля 2012

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

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

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

1 голос
/ 23 ноября 2018

Я столкнулся с той же проблемой, после некоторых исследований я нашел решение, использующее Rails 'store_accessor, чтобы сделать ключи сериализованного столбца доступными в качестве атрибутов.

С этим мы можем получить доступ к «вложенным» атрибутам сериализованного столбца…

# post.rb
class Post < ApplicationRecord
  serialize :options
  store_accessor :options, :value1, :value2, :value3
end

# set / get values
post = Post.new
post.value1 = "foo"
post.value1
#=> "foo"
post.options['value1']
#=> "foo"

# strong parameters in posts_controller.rb
params.require(:post).permit(:value1, :value2, :value3)

# form.html.erb
<%= form_with model: @post, local: true do |f| %>
  <%= f.label :value1 %>
  <%= f.text_field :value1 %>
  # …
<% end %>
1 голос
/ 26 января 2011

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

<%= 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])

так что его "наполовину" работает ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...