开发者

Rails Model-Association Form Question

Hey guys, I'm having trouble understanding a Rails construct. I'm using Rails 3 but I doubt this is specific to this version.

I have a model, Goal that has_many :commits and naturally, a Commit model that belongs_to :goal. I created the proper migration so that commits_table.references :goal.

I am actually going through the Rails Getting Started guide, except in that article they use a Post and Comment respectively.

Now that I've described the situation I can express my confusion. On the Goal's show view, I have embedded a form to create a new Commit which is 'attached' to the currently viewed Goal. This works fine and all. However, I am having trouble understanding why we do this

<%= form_for([@goal, @goal.commits.build]) do |f| %>

Shouldn't it be form_for(@commit)? I understand why we want the @goal, to provide some context since the commit is a nested resource. However, in the actual generated source, the form is appropriately named as commit, that is, the fields are named commit_blah. How did Rails know this? I understand that there's this whole system of "trust and magic" and all, but I mean at least at the high level, what from this code hinted to Rails that I wanted a commit?

I looked at the documentation for form_for and it seems like one of the parameters could be the action to take for the form. I imagine that in this case, that's what the @goal.commits.build parameter is for? To designate the action to take? Is this how Rails deduces that I want a commit? Would this also explain why this form is handled by the Commit controller even though this code is in the Goal's view?

Also, why are these parameters passed as an array ([])? In Ruby, will the method still just take it as two separate parameters, or is there a reason开发者_JAVA百科 why this was passed this way?

Finally, rails generate automatically gave me some error showing code in my other _form.html.erb partials:

<% if @commit.errors.any? %>
  <div id="error_explanation">
    <h2><%= pluralize(@commit.errors.count, "error") %> prohibited this commit from being saved:</h2>

    <ul>
    <% @commit.errors.full_messages.each do |msg| %>
      <li><%= msg %></li>
    <% end %>
    </ul>
  </div>
<% end %>

If I want to add this to this embedded form, how would I do so? I guess my question is, what would I use in place of @commit?

Thanks. I'm just trying to get my head around these new concepts.


If you go back to the documentation and click 'show source', you'll see

def form_for(record_or_name_or_array, *args, &proc)
  ...
  case record_or_name_or_array
  when String, Symbol
    ...
  when Array
    object = record_or_name_or_array.last
    object_name = options[:as] || ActiveModel::Naming.singular(object)
    apply_form_for_options!(record_or_name_or_array, options)
    args.unshift object
  else
    ...
  end
  ...
  output << fields_for(object_name, *(args << options), &proc)
  ...

For form_for, the first parameter can be a record, name or an array. In your case, you pass it an array. The code then determines the 'object' as the last member of that array, which is your @goal.commits.build object. The object name is determined from the ActiveModel::Naming.singular method.

console > ActiveModel::Naming.singular(@goal.commits.build)
=> "commit"

Then it generated the appropriate form fields using fields_for and 'commit'.


It looks like you are using nested resources. Check your routes.rb file to see if you have something like:

map.resources :commits, :has_many => :goals

or perhaps:

map.resources :commits do |commit| commit.resources :goals end

If that is the case, then you will need to supply both the commit and goal objects to the form_for method.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜