How to serialize nested Model in Rails?
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 :templat开发者_JAVA百科e_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.
Why don't you just keep a field in your RecipeTemplate model with the label recipe_id
and link that to the recipe instead of trying to serialize a recipe object?
精彩评论