Rails app creating join records (has_many through)

2019-09-08 05:07发布

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.

0条回答
登录 后发表回答