2009-02-17 83 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列表中可用。希望这可以帮助那些在谷歌搜索同一个问题的人,并且这个过时的帖子成为了最重要的结果。