Rails: Force user to create child object before saving parent

强颜欢笑 提交于 2019-12-21 05:41:34

问题


I am a beginner at Ruby on Rails. Currently, I have the following problem: I have a class Game that has an array of pictures and sentences alternating. I want that a user who creates a new Game is required to give one starting picture OR sentence. If he doesn't do so I'd like to not save the newly created game to the data base.

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

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

  [...]
end

My approach was that on /games/new, the user has to give either one painting or one sentence to begin with, but I am unsure how to enforce this, especially how to create and save a child object along with the parent object in one step.


回答1:


So you've got two questions. The first (though second in your question) is "how to create and save a child object along with the parent object in one step." This is a common pattern and looks something like this:

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

  accepts_nested_attributes_for :sentences, :paintings # <-- the magic
end

Then in, say, views/games/new.html.erb you can have something like this:

<%= 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 %>

When this form is submitted Rails will interpret the POST parameters and you'll end up with a params object that looks something like this:

# params ==
{ :game => {
    :name     => "Hollywood Squares",
    :sentence => {
      :text => "Buffalo buffalo Buffalo buffalo buffalo buffalo Buffalo buffalo."
    },
    :painting => {
      :title => "Les Demoiselles d'Avignon"
    }
  }
}

And finally, in the controller that receives those params:

def create
  new_game = Game.create params[:game] # magic! the associated Sentence and/or
end                                    # Painting will be automatically created

That's a very very high-level peek at what you'll be doing. Nested attributes have their very own section in the docs.

Your other question is how to enforce this. To do that you'll need to write some custom validations. There's two ways to do this. The simplest way is with 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

The other method is creating a custom validator class which lives in another file. This is particularly useful if you need to do a lot of custom validations or if you want to use the same custom validations on several classes. This, along with the single method, er, method, are both covered in the Validations Rails Guide.

Hope that's helpful!



来源:https://stackoverflow.com/questions/7851046/rails-force-user-to-create-child-object-before-saving-parent

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!