Rails:强制用户在保存父级之前创建子对象

HdM*_*HdM 3 nested-forms ruby-on-rails-3

我是Ruby on Rails的初学者.目前,我有以下问题:我有一个类Game,其中包含一系列图片和句子交替.我希望创建新用户的用户Game需要提供一个起始图片或句子.如果他不这样做,我不想将新创建的游戏保存到数据库中.

class Game < ActiveRecord::Base
  has_many :sentences
  has_many :paintings

  validates_inclusion_of :starts_with_sentence, :in => [true, false]

  [...]
end
Run Code Online (Sandbox Code Playgroud)

我的方法是在/ games/new上,用户必须先给出一个绘画或一个句子,但我不确定如何强制执行此操作,尤其是如何在一个父对象中创建和保存子对象步.

Jor*_*ing 5

所以你有两个问题.第一个(虽然你的问题中的第二个)是"如何在一个步骤中创建和保存子对象以及父对象." 这是一种常见的模式,看起来像这样:

class Game < ActiveRecord::Base
  has_many :sentences
  has_many :paintings

  accepts_nested_attributes_for :sentences, :paintings # <-- the magic
end
Run Code Online (Sandbox Code Playgroud)

然后,比方说,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 %>
Run Code Online (Sandbox Code Playgroud)

当提交此表单时,Rails将解释POST参数,您将得到一个如下所示的params对象:

# params ==
{ :game => {
    :name     => "Hollywood Squares",
    :sentence => {
      :text => "Buffalo buffalo Buffalo buffalo buffalo buffalo Buffalo buffalo."
    },
    :painting => {
      :title => "Les Demoiselles d'Avignon"
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

最后,在接收它们的控制器中params:

def create
  new_game = Game.create params[:game] # magic! the associated Sentence and/or
end                                    # Painting will be automatically created
Run Code Online (Sandbox Code Playgroud)

这是一个非常高级的窥视你将要做的事情.嵌套属性在文档中有自己的部分.

您的另一个问题是如何执行此操作.为此,您需要编写一些自定义验证.有两种方法可以做到这一点.最简单的方法是validate,例如:

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
Run Code Online (Sandbox Code Playgroud)

另一种方法是创建一个存在于另一个文件中的自定义验证器类.如果您需要进行大量自定义验证,或者想要在多个类上使用相同的自定义验证,这将特别有用." 验证导轨指南"中介绍了这一点以及单一方法er方法.

希望有用!