accepts_nested_attributes_for with find_or_create?

Solution 1:

When you define a hook for autosave associations, the normal code path is skipped and your method is called instead. Thus, you can do this:

class Post < ActiveRecord::Base
  belongs_to :author, :autosave => true
  accepts_nested_attributes_for :author

  # If you need to validate the associated record, you can add a method like this:
  #     validate_associated_record_for_author
  def autosave_associated_records_for_author
    # Find or create the author by name
    if new_author = Author.find_by_name(author.name)
      self.author = new_author
    else
      self.author.save!
    end
  end
end

This code is untested, but it should be pretty much what you need.

Solution 2:

Don't think of it as adding players to teams, think of it as adding memberships to teams. The form doesn't work with the players directly. The Membership model can have a player_name virtual attribute. Behind the scenes this can either look up a player or create one.

class Membership < ActiveRecord::Base
  def player_name
    player && player.name
  end

  def player_name=(name)
    self.player = Player.find_or_create_by_name(name) unless name.blank?
  end
end

And then just add a player_name text field to any Membership form builder.

<%= f.text_field :player_name %>

This way it is not specific to accepts_nested_attributes_for and can be used in any membership form.

Note: With this technique the Player model is created before validation happens. If you don't want this effect then store the player in an instance variable and then save it in a before_save callback.

Solution 3:

When using :accepts_nested_attributes_for, submitting the id of an existing record will cause ActiveRecord to update the existing record instead of creating a new record. I'm not sure what your markup is like, but try something roughly like this:

<%= text_field_tag "team[player][name]", current_player.name %>
<%= hidden_field_tag "team[player][id]", current_player.id if current_player %>

The Player name will be updated if the id is supplied, but created otherwise.

The approach of defining autosave_associated_record_for_ method is very interesting. I'll certainly use that! However, consider this simpler solution as well.