Rails嵌套属性创建Match与MatchPlayer时的参数及重复创建问题
Let's break down why you're ending up with 3 extra MatchPlayer entries, and get this sorted properly:
Why the Extra Records Are Happening
You're creating 3 MatchPlayer records for two key reasons:
- In your
newaction, you call@match.match_players.build, and since yourMatchmodel usesaccepts_nested_attributes_for :match_players, Rails automatically creates a MatchPlayer for the opponent you select in the form when you save@match. - On top of that, your original
createaction manually creates two more MatchPlayer records: one forcurrent_player, and a duplicate one for the opponent.
Adding those up: 1 (auto-created from nested attributes) + 1 (current_player) + 1 (duplicate opponent) = 3 total records.
The Fix
We can simplify this to create only the two MatchPlayer records you need (current user + selected opponent) by leveraging Rails' nested attributes properly, without redundant manual creates.
1. Update the create Action
Remove the manual MatchPlayer.create calls, and instead build the current user's MatchPlayer before saving the match. This way, both records get saved in one go:
def create @match = Match.new(match_params) # Add the current player to the match's players before saving @match.match_players.build(player: current_player) if @match.save redirect_to(root_path, notice: "Success!") else render :new end end
Note: I replaced save! with save because save! will raise an exception on validation failures, while save returns false and lets you render the form again with error messages.
2. Keep Your Updated Parameters & Form
Your current match_params and form code are already correct now that you're using the plural match_players (both in the form's simple_fields_for and the params permit):
- Params:
match_players_attributes: [:player_id]correctly matches thehas_many :match_playersassociation. - Form:
f.simple_fields_for :match_playersis the right plural syntax for a has_many nested form.
Optional: Clean Up the Form's Label Method
You can make the player label a bit cleaner with string interpolation:
= mp.input :player_id, collection: Player.all, label_method: ->(player) { "#{player.first_name} #{player.last_name}" }
With these changes, saving the match will create exactly two MatchPlayer records: one for the current user, and one for the opponent you select in the form—no duplicates, no extra entries.
内容的提问来源于stack exchange,提问作者DavidM

