我如何保持has_many:在序列化为JSON并返回Rails 4.0.3时通过关系?
如何转换为JSON并返回并保持关系? 它认为当我取消包裹对象时它们不存在!
irb(main):106:0* p = Post.last => # p.tags => #<ActiveRecord::Associations::CollectionProxy [# 2 #### !!!!!!!!!!!! irb(main):110:0> json = p.to_json => "{\"id\":113,\"title\":... }" irb(main):111:0> p2 = Post.new( JSON.parse(json) ) => # p2.tags => # irb(main):113:0> p2.tags.count => 0 #### !!!!!!!!!!!!
这是模型
class Post :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)
我模拟了与你完全相同的场景,并发现:
每当一个模型(Post)有一个has_many through
关联然后在创建该模型的实例时,即Post
传递一个Hash
,例如: Post.new( JSON.parse(json) )
或Post.new(id: 113)
似乎Rails虽然指向相同的记录,但对待它们的方式却不同。
我按顺序运行以下命令:
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
我没有直接使用Hash创建Post的新实例,而是使用从反序列化json获得的id
从数据库中获取记录。 在这种情况下,实例p3
和实例p2
引用相同的Post,但Rails正在以不同的方式解释它们。
免责声明:这绝不是一个理想的解决方案(我称之为右下角俗气),但它是我能够为你的场景提出的唯一一件事。
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
的键,因此通过覆盖Post
类中的as_json
方法,您as_json
在呈现JSON时更改JSON,如下所示:
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构建gem,例如acts_as_api
,可以让你删除这个as_json
覆盖业务
因此,现在您呈现的JSON具有所有Post
属性,以及键tags_attributes
下的tag
属性键值对tags_attributes
。
从技术上讲,如果你以Kirti建议的方式反序化反序列化这个渲染的JSON,它会工作,你会得到一个正确填充的活动记录对象。 但是 ,遗憾的是,父Post
对象和依赖tag
对象中都存在id
属性意味着活动记录将触发至少一个SQL查询。 它将根据has_many
关系的规范(具体地, collection=objects
部分)快速查找标记以确定是否需要添加或删除任何内容。
既然你说你想避免命中数据库,我能找到的唯一解决方案是以leesungchul建议的方式渲染JSON,但具体排除id
字段:
p_json = p.to_json(except: [:id], include: {tags: {except: :id}})
如果你这样做:
p2 = Post.new(JSON.parse(p_json))
您应该在没有任何数据库调用的情况下返回完全呈现的Post
对象。
当然,这假设您不需要那些id
字段。 如果你这样做......坦白说,除了重命名as_json
方法中的id
字段之外,我不确定一个更好的解决方案。
另请注意:使用此方法时,由于缺少id
字段,您将无法使用p2.tags.count
; 它将返回零。 你必须使用.length
代替。
你可以试试
p2.as_json( :include => :tags )
你打电话的时候
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
你得到正确的数量
- Ruby on Rails:无法安装JSON gem
- 在Rails中使用ActiveModel :: Serializer – JSON数据在json和index响应之间有所不同
- 如何使用rails remote:true参数和JSON?
- Rails escape_javascript通过转义单引号创建无效的JSON
- 如何关闭仅用于json的rails protect_from_forgeryfilter
- Rails:通过Ajax传递Params
- 如何覆盖’as_json’或’to_json’方法以便’respond_to’而不包含指定的信息?
- 通过json设计失败认证发回html而不是json
- 在rails中提供静态JSON目标文件