I'm tearing my hair out with this. I have three models: Game
, Genre
and GameGenre
. Games have many Genres through GameGenres, Genres have many Games through GameGenres.
I have a games/new
form which creates new game records. To this form, I've added a fields_for :game_genres
section, which is supposed to allow the user to add game_genre relationship records to the new game records they create.
I think I'm pretty close to getting this working. At the moment, on submission it returns the error: "Game genres game can't be blank." I take this to mean the form is connecting to the game_genres table. I'm not sure why the controller isn't submitting the :game_id value.
Here's my code.
new.html.erb
<%= form_for(@game) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="form-group">
<%= f.label :title_en, "Game title (English)" %>
<%= f.text_field :title_en, class: 'form-control',
placeholder: "E.g. Marble Madness" %>
</div>
<div class="form-group">
<%= f.label :year, "Year" %>
<%= f.select :year, options_for_select((1950..2017).reverse_each),
{},
{:class => 'form-control'} %>
</div>
<div class="form-group">
<%= f.label :description_en, "Summary" %>
<%= f.text_area :description_en, class: 'form-control' %>
</div>
<hr />
<div class="form-group">
<%= f.fields_for :game_genres do |gf| %>
<%= gf.label :genre_id, "Select a genre" %>
<%= gf.collection_select(:genre_id, Genre.all, :id, :name,
{},
{:class => "form-control"}) %>
<% end %>
</div>
<%= f.submit "Save", class: "btn btn-primary center-block" %>
<% end %>
games_controller.rb (excerpt)
def new
@game = current_user.games.build
@game.game_genres.build
end
def create
@game = current_user.games.build(game_params)
if @game.save
flash[:success] = "Game saved: #{@game.title_en} (#{@game.year})"
redirect_to game_path(@game.id)
else
render 'new'
end
end
private
def game_params
params.require(:game).permit(:title_en, :year, :description_en,
game_genres_attributes: [:genre_id])
game.rb
class Game < ActiveRecord::Base
belongs_to :user
has_many :game_genres, foreign_key: :game_id,
dependent: :destroy
has_many :genres, through: :game_genres
accepts_nested_attributes_for :game_genres,
allow_destroy: true
genre.rb
class Genre < ActiveRecord::Base
belongs_to :user
has_many :game_genres, foreign_key: :genre_id,
dependent: :destroy
has_many :games, through: :game_genres
game_genre.rb
class GameGenre < ActiveRecord::Base
belongs_to :game, inverse_of: :game_genres
belongs_to :genre, inverse_of: :game_genres
As I understand it I do not need to add resources for game_genres to routes.rb.
This is the output from the rails development server when I try to submit a new game record:
Started POST "/games" for ::1 at 2015-06-30 18:59:24 +0100
Processing by GamesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"cUbIu3i9niI0R8b9E3KZeYEbNpx8NDj9Jx1MeGvylYDKQj54d3BwSdLsMiPsRBoUTzoTEuMGTk5/KTKAXH7DMA==", "game"=>{"title_en"=>"fdsdcdc", "year"=>"2017", "description_en"=>"dsdc", "game_genres_attributes"=>{"0"=>{"genre_id"=>"8"}}}, "commit"=>"Save"}
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT 1 [["id", 1]]
(0.1ms) BEGIN
Genre Load (0.3ms) SELECT "genres".* FROM "genres" WHERE "genres"."id" = $1 ORDER BY "genres"."name" ASC LIMIT 1 [["id", 8]]
(0.1ms) ROLLBACK
Rendered shared/_error_messages.html.erb (0.6ms)
Genre Load (0.3ms) SELECT "genres".* FROM "genres" ORDER BY "genres"."name" ASC
As I understand it, the game_genres_attributes
are properly nested and permitted. Why isn't game_id being recognised and saved?
Thanks so much to anyone who can help.
EDIT:
I got the form working by removing this validation from the GameGenre model:
validates :game_id, presence: true
I want this line in though, so I'm now wondering how to work around it?
EDIT 2:
I guess this is the answer (from http://makandracards.com/makandra/1346-popular-mistakes-when-using-nested-forms):
Nested records are validating the presence of their nesting parent record’s foreign key. If you do this, you cannot create a new parent record together with a new child record and will need to save the parent before you can save the child. You can opt to only show the nested child form when the parent has been saved before, or simply let go of the validation.