6

Estoy tratando de guardar un hash de opciones en un solo campo de base de datos. El formulario puede guardar los datos en la base de datos, pero no puede recuperarlos nuevamente cuando voy a editarlos (por ejemplo, todos los demás campos están rellenos excepto los campos wp_options).Uso de Rails Form Helpers con clases personalizadas serializadas

class Profile < ActiveRecord::Base 
serialize :wp_options 
end 

Ésta es mi clase personalizada:

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

En mi formulario:

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

En mi controlador:

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

En mi columna DB 'wp_options':

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

Cualquier consejo sería muy apreciado.

Respuesta

10

En realidad es fácil. Debe utilizar la clase con los métodos reader. Puede crearlo de diferentes maneras, pero la más fácil es usar la clase OpenStruct (observe que no podrá ver los campos que están en los métodos de instancia de OpenStruct ... esta clase no puede redefinir métodos).

En su forma se debe añadir:

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

En lugar de @profile (si tiene variable dinámica) que puede utilizar f.object.wp_options.

Y para el modelo Profile debe agregar el método wp_options.

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

En ese caso, sólo funcionará si sus wp_options serializados es una clase Hash.

Espero que ayude.

PS. Usé la misma técnica, pero como tenía type hash keys, OpenStruct no pudo crearlo, así que utilicé la clase Struct simple. Tenía data columna:

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

Un poco menos trivial, pero de todos modos el mismo enfoque (antes de que he creado clase especial, pero ahora sé que Struct es la mejor manera de crear ese tipo de cosas más. las ideas se pueden encontrar aquí: How do I use hash keys as methods on a class?)

+0

Necesitaba mi equivalente de wp_options para mantenerme como un hash, así podía repetir sus entradas. Así que no hice ningún cambio en el modelo y puse 'OpenStruct.new' en el siguiente formulario: <% = f.fields_for: wp_options, OpenStruct.new (@ profile.wp_options) do | wp_options | %> Quizás tenga la vista asomándose a la implementación del modelo, pero todo lo que sabe es que wp_options es un hash en lugar de un objeto con métodos attr. – Turadg

1

para aquellos que utilizan la solución de Dmitry, en los carriles 3.0.5 (no estoy seguro sobre prev próximo versiones/a fin de tratar antes), añadir

require 'ostruct' 

a su archivo environment.rb para tener OpenStruct inc agotado

Si va a serializar con JSON, en su modelo agregue (oh y soy bastante nuevo en RoR, así que siéntase libre de examinar y sugerir una mejor solución de "trabajo" que esta) (mis nombres de las variables cambian para coincidir con el ejemplo anterior):

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 

en una nota final: sinceramente, no estoy seguro de por qué tiene que ser almacenado en self.wp_options y no self.attributes [ 'wp_options '], si alguien pudiera explicar, sería genial.

0

Tuve el mismo problema. Mi solución fue la siguiente:

  1. definir un método en su ayudante:

    def wp_options_text_field(profile, wp_options, name) 
        wp_options.text_field name, :value => profile.wp_options[name] 
    end 
    
  2. En su opinión:

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

El único problema aquí es que usted' Necesitaré definir métodos para cada método de ayuda que usará. En mi caso solo tenía 2 métodos usados ​​y no fue doloroso.

Cuestiones relacionadas