如何转换为JSON并返回并保持关系?它认为当我取消包裹对象时它们不存在!
irb(main):106:0* p = Post.last
=> #<Post ...
irb(main):107:0> p.tags
=> #<ActiveRecord::Associations::CollectionProxy [#<Tag id: 41, ...
irb(main):109:0* p.tags.count
=> 2 #### !!!!!!!!!!!!
irb(main):110:0> json = p.to_json
=> "{\"id\":113,\"title\":... }"
irb(main):111:0> p2 = Post.new( JSON.parse(json) )
=> #<Post id: 113, title: ...
irb(main):112:0> p2.tags
=> #<ActiveRecord::Associations::CollectionProxy []>
irb(main):113:0> p2.tags.count
=> 0 #### !!!!!!!!!!!!
这是模型
class Post < ActiveRecord::Base
has_many :taggings, :dependent => :destroy
has_many :tags, :through => :taggings
有人建议但不起作用
irb(main):206:0* Post.new.from_json p.to_json(include: :tags)
ActiveRecord::AssociationTypeMismatch: Tag(#60747984) expected, got Hash(#15487524)
答案 0 :(得分:3)
我模拟了与你完全相同的场景并发现:
每当模型(Post)具有has_many through
关联时,则在创建该模型的实例时,Post
传递Hash
例如:Post.new( JSON.parse(json) )
或{{ 1}}似乎Rails对它们的处理方式不同,尽管它们指向相同的记录。
我按顺序运行以下命令:
Post.new(id: 113)
我没有直接使用Hash创建Post的新实例,而是使用从反序列化json获得的p = Post.last
p.tags
p.tags.count
json = p.to_json
p2 = Post.new( JSON.parse(json) )
p2.tags
p2.tags.count ## Gives incorrect count
p3 = Post.find(JSON.parse(json)["id"]) ### See notes below
p3.tags
p3.tags.count ## Gives the correct count
从数据库中获取记录。在这种情况下,实例id
和实例p3
引用相同的帖子,但Rails正在以不同的方式解释它们。
答案 1 :(得分:2)
免责声明:这绝不是一个理想的解决方案(我称之为右下角俗气),但它是关于我唯一能够做到的事情想出你的方案。
Kirti Thorat所说的是正确的;当你有一个依赖对象时,Rails希望哈希中的关联属于那个特定的类(在你的例子中,是一个Tag
对象)。因此,你得到的错误是:Tag expected...got Hash
。
这是俗气的部分:正确反序列化复杂对象的一种方法是利用accepts_nested_attributes_for
方法。通过使用此方法,您将允许Post
类将依赖的Tag
键值对正确反序列化为正确的Tag
对象。从这开始:
class Post < ActiveRecord::Base
accepts_nested_attributes_for :tags
# rest of class
end
由于accepts_nested_attributes_for
搜索给定关联的单词_attributes
的密钥,因此在通过覆盖as_json
方法呈现JSON时,您必须更改JSON以适应此目的在你的Post
课程中,如下:
def as_json(options={})
json_hash = super.as_json(options)
unless json_hash["tags"].nil?
json_hash["tags_attributes"] = json_hash["tags"] # Renaming the key
json_hash.delete("tags") # remove the now unnecessary "tags" key
end
json_hash # don't forget to return this at the end
end
旁注:有许多json构建宝石,例如acts_as_api
,可以删除此as_json
覆盖业务
现在,您呈现的JSON具有所有Post
属性,以及键tag
下的tags_attributes
个属性键值对数组。
从技术上讲,如果你以Kirti建议的方式反序列化这个呈现的JSON,那么它会工作,你会得到一个正确填充的活动记录对象。 然而,遗憾的是,父{Benis}对象和依赖id
对象中Post
属性的存在意味着活动记录将至少触发一个SQL查询。根据{{3}}关系(特别是tag
部分)的规范,它会快速查找标签以确定是否需要添加或删除任何内容。
既然你说你想避免访问数据库,我能找到的唯一解决方案是以leesungchul建议的方式渲染JSON,但明确排除collection=objects
字段:< / p>
id
如果你这样做:
p_json = p.to_json(except: [:id], include: {tags: {except: :id}})
您应该在没有任何数据库调用的情况下返回完全呈现的p2 = Post.new(JSON.parse(p_json))
对象。
当然,这假设您不需要那些Post
字段。如果您这样做......坦白说,除了重命名id
方法中的id
字段之外,我不确定是否有更好的解决方案。
另请注意:使用此方法时,由于缺少as_json
字段,您将无法使用id
;它将返回零。您必须改为使用p2.tags.count
。
答案 2 :(得分:1)
你可以尝试
p2.as_json( :include => :tags )
答案 3 :(得分:0)
致电时
p2.tags
您获得了正确的标签,但p2尚未保存在数据库中。这似乎是
的原因p2.tags.count
一直给0。 如果你真的做了类似的事情:
p2.id = Post.maximum(:id) + 1
p2.tags #Edit: This needs to be done to fetch the tags mapped to p from the database.
p2.save
p2.tags.count
您获得正确的计数