2014-02-28 37 views
0

如何轉換爲JSON並返回並保持關係?它認爲當我解開對象時它們不存在!我如何保持has_many:通過關係序列化到JSON和返回到Rails 4.0.3?

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

你調用'p2 = Post.new(JSON.parse(json))'這不會保存到db。並在你的db上調用'p2.tags.count'查詢,但不保存數據。 –

+0

@Monk_Code沒有,但仍然無法正常工作。另請參閱下面的答案評論。 '> p2 = Post.new(JSON.parse(json))' 'ActiveRecord :: AssociationTypeMismatch:Tag(#62190408)expected,got Hash(#15487524)' – Chloe

回答

1

您可以嘗試

p2.as_json(:include => :tags) 
+0

您是不是指'p.to_json'?這沒有工作:'p2 = Post.new(JSON.parse(json)) ActiveRecord :: AssociationTypeMismatch:標籤(#60747984)預計,得到哈希(#15487524)' – Chloe

+0

'as_json'甚至不輸出有效JSON! '{「id」=> 113,'無效,因爲'=>'不是JSON。 – Chloe

+0

@Chloe你應該可以使用'to_json'而不是以相同的方式使用包含。 –

3

我模擬了完全相同的情況像你的,並發現:

每當模型(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後的新實例,我用從反序列化JSON獲得id獲取的記錄從數據庫中。在這種情況下,實例p3和實例p2引用相同的Post,但Rails正在以不同方式解釋它們。

+0

嗯,有趣。我實際上是將它們序列化並將它們反序列化爲一個Redis緩存來加速它。我希望避免DB。也許這是不可能的? – Chloe

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 

你得到正確的計數

+1

您的方法會在數據庫中創建重複記錄。 OP正試圖訪問現有記錄,而不是創建另一個記錄。 –

+0

哦對不起!現在我明白他想做什麼......在這種情況下,我認爲像p2.tags.to_a.count這樣的東西應該可以工作,不是嗎? –

+1

@AhmedFathy問題實際上是圍繞着JSON/Hash中的'has_many'關係。 OP最初的問題是他們沒有在'to_json'中包含關聯,所以'.tags'返回空。現在,他們的問題是,當他們試圖使用JSON作爲'.new'的參數時,他們會在他們問題的底部收到錯誤。這就是他們目前的狀況。 –

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的關鍵,你必須改變JSON當它呈現在你的Post類中重寫as_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建築寶石,如acts_as_api的,讓你刪除此as_json覆蓋業務

因此,現在您的呈現的JSON具有所有Post屬性,以及在密鑰tags_attributes下的tag屬性鍵值對的數組。從技術上講,如果你要以Kirti建議的方式反序列化這個呈現的JSON,它將工作,並且你會找回一個正確填充的活動記錄對象。 但是,,不幸的是,父對象Post和對象tag對象中id屬性的存在意味着活動記錄將觸發至少一個SQL查詢。它會根據has_many關係的規範(特別是collection=objects部分)快速查找標籤以確定是否需要添加或刪除任何內容。

既然你說你想避免進入數據庫,我已經能夠找到的唯一的解決辦法是應JSON以同樣的方式leesungchul建議,但特別不包括id領域:

p_json = p.to_json(except: [:id], include: {tags: {except: :id}}) 

如果然後做:

p2 = Post.new(JSON.parse(p_json)) 

應該會得到一個完全呈現Post對象沒有任何DB調用。

這當然,假設你不需要那些id字段。如果你這樣做...坦率地說,我不確定除了重命名as_json方法中的id字段外,更好的解決方案。

還要注意:使用此方法,因爲缺乏id領域,你將無法使用p2.tags.count;它將返回零。您必須改用.length

+1

我想你的意思是說'p_json = p.to_json(除了:[:id],包括:{tags:{except:[:id]}})''p_json'由您定義的方式將給'語法錯誤'。 –

+0

@KirtiThorat哎呀,對。我在錯誤的一面使用了':'。謝謝!將更新。 –

+1

哎呀..我的意思是'p_json = p.to_json(除了:[:id],include:{tags:{except:[:id]}})''include' not'includes' –

相關問題