所以你有两个问题。第一个(尽管问题中的第二个)是“如何一步创建和保存子对象以及父对象”。这是一种常见的模式,看起来像这样:
class Game < ActiveRecord::Base
has_many :sentences
has_many :paintings
accepts_nested_attributes_for :sentences, :paintings # <-- the magic
end
然后,说,views/games/new.html.erb
你可以有这样的东西:
<%= form_for :game do |f| %>
<%= label :name, "Name your game!" %>
<%= text_field :name %>
<%= fields_for :sentence do |s| %>
<%= label :text, "Write a sentence!" %>
<%= text_field :text %>
<% end %>
<%= fields_for :painting do |s| %>
<%= label :title, "Name a painting!" %>
<%= text_field :title %>
<% end %>
<% end %>
当提交此表单时,Rails 将解释POST
参数,你最终会得到一个params
对象看起来像这样:
# params ==
{ :game => {
:name => "Hollywood Squares",
:sentence => {
:text => "Buffalo buffalo Buffalo buffalo buffalo buffalo Buffalo buffalo."
},
:painting => {
:title => "Les Demoiselles d'Avignon"
}
}
}
最后,在接收这些的控制器中params
:
def create
new_game = Game.create params[:game] # magic! the associated Sentence and/or
end # Painting will be automatically created
这是对您将要做的事情的非常非常高层次的了解。嵌套属性有自己的属性文档中的部分 http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html.
您的另一个问题是如何执行这一点。为此,您需要编写一些自定义验证。有两种方法可以做到这一点。最简单的方法是validate
, e.g.:
class Game < ActiveRecord::Base
# ...
validate :has_sentence_or_painting # the name of a method we'll define below
private # <-- not required, but conventional
def has_sentence_or_painting
unless self.sentences.exists? || self.paintings.exists?
# since it's not an error on a single field we add an error to :base
self.errors.add :base, "Must have a Sentence or Painting!"
# (of course you could be much more specific in your handling)
end
end
end
另一种方法是创建一个位于另一个文件中的自定义验证器类。如果您需要执行大量自定义验证或者想要在多个类上使用相同的自定义验证,这尤其有用。这个,以及单一方法,呃,方法,都包含在验证导轨指南 http://guides.rubyonrails.org/active_record_validations_callbacks.html.
希望这有帮助!