问题
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