I'm having an extremely difficult time figuring out how to serialize the nested attributes of a model in rails. I have a RecipeTemplate which will store an already existing Recipe in it's template_data attribute. Recipe has nested attributes two levels deep.
This is on rails 3.1.0.rc4
class RecipeTemplate < ActiveRecord::Base
serialize :template_data, Recipe
...
end
class Recipe < ActiveRecord::Base
has_many :ingredients
accepts_nested_attributes_for :ingredients
...
end
Ingredients in Recipe also has nested attributes (SubIngredients).
If I set the template_data with an object like so:
Recipe.includes(:ingredients => [:sub_ingredients]).find(1)
I'll get a TypeError "can't dump anonymous class Class" which makes sense, since it doesn't know how to serialize the Ingredients or SubIngredients.
How can you serialize the nested attributes in a model so that you can use:
serialize :template_data, Recipe
Or do I have to serialize the data in some other manner and perform the type safety checks myself?
Thanks in advance for any help
I can see why you would want the template itself to be stored inside of a serialized column, but you need a little more manipulation of the data being stored than is permitted by that type of column. Here's what I would do:
app/models/recipe_template.rb
class RecipeTemplate < ActiveRecord::Base
serialize :template_data
attr_accessible :name, :recipe
def recipe=(r)
self.template_data = r.serializable_hash_for_template
end
def recipe
Recipe.new(template_data)
end
end
app/models/recipe.rb
class Recipe < ActiveRecord::Base
has_many :ingredients, as: :parent
accepts_nested_attributes_for :ingredients
attr_accessible :name, :ingredients_attributes
def serializable_hash_for_template(options={})
options[:except] ||= [:id, :created_at, :updated_at]
serializable_hash(options).tap do |h|
h[:ingredients_attributes] = ingredients.map(&:serializable_hash_for_template)
end
end
end
app/models/ingredient.rb
class Ingredient < ActiveRecord::Base
belongs_to :parent, polymorphic: true
has_many :sub_ingredients, class_name: 'Ingredient', as: :parent
accepts_nested_attributes_for :sub_ingredients
attr_accessible :name, :sub_ingredients_attributes
def serializable_hash_for_template(options={})
options[:except] ||= [:id, :parent_id, :parent_type, :created_at, :updated_at]
serializable_hash(options).tap do |h|
h[:sub_ingredients_attributes] = sub_ingredients.map(&:serializable_hash_for_template)
end
end
end
Then to create and use a template:
# create a recipe to use as a template
taco_meat = Ingredient.create(name: "Taco Meat")
taco_seasoning = taco_meat.sub_ingredients.create(name: "Taco Seasoning")
sams_tacos = Recipe.create(name: "Sam's Tacos")
sams_tacos.ingredients << taco_meat
# create a template from the recipe
taco_recipe = RecipeTemplate.create(name: "Taco Recipe", recipe: sams_tacos)
# build a new recipe from the template
another_taco_recipe = taco_recipe.recipe
The difference is that you're using the serialized column to store a Hash to use in the Recipe contructor. If you just wanted to serialize the object, the other posters are correct–just associate an object.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With