开发者

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?

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜