2009-02-17 86 views
5

我的目標是通過一個REST請求創建嵌套的資源。 REST請求通過XML文檔表示。這對單個資源來說工作正常,但我無法管理它嵌套的資源。好的,接下來我會給你一個小例子。如何在XML中使用嵌套資源來使用REST?

首先創建一個新的Rails項目

rails forrest 

接下來我們產生兩個資源,樹木和燕窩的支架。

./script/generate scaffold tree name:string 
./script/generate scaffold bird_nest tree_id:integer bird_type:string eggs_count:integer 

在./forrest/app/models/tree.rb我們下面插入,因爲一棵樹可以有很多鳥的巢「的has_many」行的文件:-)

class Tree < ActiveRecord::Base 
    has_many :bird_nests 
end 

在文件./forrest/app/models/bird_nest.rb我們在下面插入「belongs_to」這一行,因爲每個鳥巢都應該屬於一棵樹。

class BirdNest < ActiveRecord::Base 
    belongs_to :tree 
end 

後來我們建立數據庫並啓動服務器:

rake db:create 
rake db:migrate 
./script/server 

只需複製並粘貼此XML sniplet到一個名爲 「tree.xml」 文件...

<tree> 
    <name>Apple</name> 
</tree> 

...並通過cURL將其發佈到服務以創建新樹:

curl -H 'Content-type: application/xml' -H 'Accept: application/xml' -d @tree.xml http://localhost:3000/trees/ -X POST 

這工作正常。另外爲鳥巢XML(文件名「bird-nest.xml」)分開。如果我們發送此...

<bird-nest> 
    <tree-id>1</tree-id> 
    <bird-type>Sparrow</bird-type> 
    <eggs-count>2</eggs-count> 
</bird-nest> 

...也通過以下cURL語句。該資源已正確創建!

curl -H 'Content-type: application/xml' -H 'Accept: application/xml' -d @bird-nest.xml http://localhost:3000/bird_nests/ -X POST 

確定一切都很好。現在來到橡膠與路面相遇的地方。我們在一個請求中創建兩個資源。因此,這裏是包含一個鳥巢爲我們的樹中的XML:

<tree> 
    <name>Cherry</name> 
    <bird-nests> 
    <bird-nest> 
     <bird-type>Blackbird</bird-type> 
     <eggs-count>2</eggs-count> 
    </bird-nest> 
    </bird-nests> 
</tree> 

我們觸發相應的請求通過捲曲再次使用...

curl -H 'Content-type: application/xml' -H 'Accept: application/xml' -d @tree-and-bird_nest.xml http://localhost:3000/trees/ -X POST 

...現在我們會得到一個在(生成)「創造」樹的控制器的方法,服務器錯誤:AssociationTypeMismatch(燕窩預期,得到了陣列)

在我的角度來看,這是服務器的日誌中關於接收的屬性和錯誤MESSA的重要組成部分ge:

Processing TreesController#create (for 127.0.0.1 at 2009-02-17 11:29:20) [POST] 
    Session ID: 8373b8df7629332d4e251a18e844c7f9 
    Parameters: {"action"=>"create", "controller"=>"trees", "tree"=>{"name"=>"Cherry", "bird_nests"=>{"bird_nest"=>{"bird_type"=>"Blackbird", "eggs_count"=>"2"}}}} 
    SQL (0.000082) SET NAMES 'utf8' 
    SQL (0.000051) SET SQL_AUTO_IS_NULL=0 
    Tree Columns (0.000544) SHOW FIELDS FROM `trees` 


    ActiveRecord::AssociationTypeMismatch (BirdNest expected, got Array): 
     /vendor/rails/activerecord/lib/active_record/associations/association_proxy.rb:150:in `raise_on_type_mismatch' 
     /vendor/rails/activerecord/lib/active_record/associations/association_collection.rb:146:in `replace' 
     /vendor/rails/activerecord/lib/active_record/associations/association_collection.rb:146:in `each' 
     /vendor/rails/activerecord/lib/active_record/associations/association_collection.rb:146:in `replace' 
     /vendor/rails/activerecord/lib/active_record/associations.rb:1048:in `bird_nests=' 
     /vendor/rails/activerecord/lib/active_record/base.rb:2117:in `send' 
     /vendor/rails/activerecord/lib/active_record/base.rb:2117:in `attributes=' 
     /vendor/rails/activerecord/lib/active_record/base.rb:2116:in `each' 
     /vendor/rails/activerecord/lib/active_record/base.rb:2116:in `attributes=' 
     /vendor/rails/activerecord/lib/active_record/base.rb:1926:in `initialize' 
     /app/controllers/trees_controller.rb:43:in `new' 
     /app/controllers/trees_controller.rb:43:in `create' 

所以我的問題是我做錯了關於嵌套的XML資源。這將是正確的XML語法?或者是否必須手動修改樹的控制器,因爲這種情況不在生成的範圍內?

+0

看起來您可能需要編輯TreesController創建操作來處理創建BirdsNest對象。您可以發佈TreesController創建操作的副本嗎? – hernan43 2009-02-17 12:45:35

+0

創建操作是之前步驟中生成的默認操作(請參閱「生成腳手架」)。 – 2009-02-17 14:04:47

回答

3

您可以完成此操作的一種方法是覆蓋您的樹模型上的bird_nests =方法。

def bird_nests=(attrs_array) 
    attrs_array.each do |attrs| 
    bird_nests.build(attrs) 
    end 
end 

這裏唯一的問題是,你失去的二傳,這可能會或可能不會在你的應用存在問題的默認行爲。

如果你正在運行的Rails你可以打開質量分配的一個較新版本如下所述:

http://github.com/rails/rails/commit/e0750d6a5c7f621e4ca12205137c0b135cab444a

在這裏:

http://ryandaigle.com/articles/2008/7/19/what-s-new-in-edge-rails-nested-models

class Tree < ActiveRecord::Base 
    has_many :bird_nests, :accessible => true 
end 

這是首選。

1

默認控制器將有一個像

@tree = Tree.new(params[:tree]) 

這樣一行apprently不會自動地分析你發送的參數。你需要改變你的控制器來分開參數hashf,創建並保存樹,然後使用樹的ID(它不會被創建,直到你保存後)創建樹並保存樹。

清澈如泥?

2

覆蓋的bird_nests =樹模型的方法是一個有效的解決方案,指Patrick Richie以前的帖子(謝謝)。所以不需要對控制器進行更改。下面是詳細的代碼將處理在上面的例子中提到的給定的XML請求(也處理非陣列巢):

def bird_nests=(params) 
    bird_nest=params[:bird_nest] 
    if !bird_nest.nil? 
     if bird_nest.class==Array 
     bird_nest.each do |attrs| 
      bird_nests.build(attrs) 
     end 
     else 
     bird_nests.build(bird_nest) 
     end 
    end 
    end 
2

儘管這個問題是兩年半前問,發生了很多變化現在:第一Rails中2.3 has_many :bird_nests, :accessible => true和現在與accepts_nested_attributes_for方法滑軌3 ...所以這些天中的Rails 3你將實現用下面的代碼實現上述目的:

class Tree < ActiveRecord::Base 
    has_many :bird_nests 
    accepts_nested_attributes_for :bird_nests 
end 

class BirdNest < ActiveRecord::Base 
    belongs_to :tree 
end 

這生成bird_nests_attributes訪問者(吸氣/樹對象。因此XML看起來像以下:

<tree> 
    <name>Cherry</name> 
    <bird_nests_attributes type='array'> 
     <bird_nest> 
      <bird-type>Blackbird</bird-type> 
      <eggs-count>2</eggs-count> 
     </bird_nest> 
     <bird_nest> 
      <bird-type>Bluebird</bird-type> 
      <eggs-count>3</eggs-count> 
     </bird_nest> 
    </bird_nests_attributes> 
</tree> 

Rails會上面的XML轉換爲適當PARAMS散...和樹對象與相關bird_nests對象將與下面的語句來創建

@tree = Tree.new(params[:tree]) 
@tree.save 

這是使其工作的最低代碼。如果你在你的模型,你必須時刻,那麼不要忘記添加:bird_nests_attributesattr_accessible列表如下一樣使用attr_accessible

attr_accessible :a_tree_attribute, :another_tree_attr, :bird_nests_attributes 

同樣可以爲屬性各自的模型添加驗證。如果驗證失敗,嵌套屬性錯誤也會在@tree.errors列表中可用。希望這可以幫助那些在谷歌搜索同一個問題的人,並且這個過時的帖子成爲了最重要的結果。