Rails 3.2.2简单表单嵌套模型(has_many through)有效但希望改进

时间:2012-04-02 13:49:29

标签: ruby-on-rails-3 collections nested-forms simple-form

在Rails 3.2.2中使用simple_forms gem,我已经构建了一个表单来在嵌套模型中创建对象(在railscasts.com上再次向Ryan Bates致敬)但我正在努力掌握has_many通过关系的复杂性无需向我的游戏控制器的create方法添加额外的代码。

我在我的游戏模型中使用accepts_nested_attributes_for:参与者,希望能够批量分配2个必需的参与者对象。

我有三个课程:游戏,团队和参与者

参与者存在,因此团队可以被视为游戏中的主队或客队。

目前,我正在使用表单中的select_tag来创建一些psuedo参数,然后我用它来保存新的@game对象后在控制器的create方法中创建参与者我正在创建一个@game 。主队和客队的参赛者。这一切都有效但有点过于繁重,如果我想要创建一个游戏锦标赛而不需要编写更多的控制器代码,则无法扩展。

所以现在我在新方法中构建了2次参与者,并尝试在不添加额外代码的情况下创建和更新工作,并在create方法中删除创建@ games.participants。

好的......问题......

  1. builder.input:team_id,:collection => Team.all

    这会将对象id放在选择器中,我希望Team.full_name在当前生成 select_tag“home”,options_from_collection_for_select(Team.all,:id,:full_name)和id为be 推进了team_id param。我正在努力寻找合适的simple_forms文档和 透过宝石来源看到了我的头痛(我不够好解码它)

  2. 我需要区分在新Game对象中创建的两个参与者。该 simple_form f.simple_fields_for:参与者do | builder |不允许我这样做 :participant.first或:participant.last。我想创建一个隐藏字段设置:home_team 对于第一个参与者来说是真的。

  3. 如果我能让这两件作品发挥作用,那么我认为我的解决方案可行。任何帮助或线索一如既往地非常受欢迎。

    游戏模型:

    class Game < ActiveRecord::Base
      belongs_to :venue
      belongs_to :game_type
      has_and_belongs_to_many :tournaments
      has_many :participants, :dependent => :destroy
      has_many :teams, :through => :participants
    
      validates :kickoff, :venue_id, :presence => true
      accepts_nested_attributes_for :participants,
        :reject_if => lambda {|a| a[:game_id].blank? },
           :allow_destroy => :true
      def home_team
        self.teams.joins(:participants).where("participants.home_team = ?", true).first
      end
      def away_team
        self.teams.joins(:participants).where("participants.home_team = ?", false).first
      end
    end
    

    参与者模型:

    class Participant < ActiveRecord::Base
      belongs_to :game
      belongs_to :team
    end
    

    团队模型:

    class Team < ActiveRecord::Base
      has_many :participants, :dependent => :destroy
      has_many :games, :through => :participants
      validates :full_name, :presence => true
      validates :short_name, :presence => true, :length => { :within => 1..5 }
    end
    

    游戏控制器(新建和创建方法)

    class GamesController < ApplicationController
      def new
        @game = Game.new
        ### This section is not currently implemented
        # 2.times { @game.participants.build }
        ### End of unimplemented section
        respond_to do |format|
          format.html # new.html.erb
        end
      end
    
      def create
        @game = Game.new(params[:game])
        respond_to do |format|
        if @game.save
          @game.participants.create(:team_id => params[:home], :home_team => true)
          @game.participants.create(:team_id => params[:away], :home_team => false)
          format.html { redirect_to games_path, notice: 'Game was successfully created.' }
        else
          format.html { render action: "new" }
        end
      end
    end
    

    游戏视图表单部分(实际上有另一部分,但为了便于阅读而包括在内)

    <%= simple_form_for(@game) do |f| %>
      <%= f.error_notification %>   
      <div class="form-inputs">
        <%= label_tag "home", "Home Team"%>
        <%= select_tag "home", options_from_collection_for_select(Team.all, :id, :full_name)%>
        <!-- This section is not working properly -->
        <!-- <%=# f.simple_fields_for :participants do |builder| %> -->
          <!-- <%=# builder.input :team_id, :collection => Team.all, :selected => :full_name %> -->
        <!-- <%# end %> -->
        <!-- End of not working properly section -->
        <%= f.input :home_goals, :input_html => {:maxlength => 5, :size => 5 }%>
        <%= f.input :away_goals, :input_html => {:maxlength => 5, :size => 5 }%>
        <%= select_tag "away", options_from_collection_for_select(Team.all, :id, :full_name)%>
        <%= label_tag "away", "Away Team"%>
        <%= f.input :kickoff %>
        <%= f.input :completed %>
        <%= f.input :game_type_id %>
        <%= f.input :venue_id ,:collection => Venue.all, prompt:  'Choose the venue'%>
      </div> 
      <div class="form-actions">
        <%= f.button :submit %>
      </div>
    <% end %>
    

1 个答案:

答案 0 :(得分:3)

好的......如果有人感兴趣我解决了以下两个问题:

  1. 使用类上的collect功能而不是任何simple_form助手,因此Games View Form Partial对收集行进行了以下更改:

    <%= builder.input :team_id, :collection => Team.all.collect{ |t| [t.full_name, t.id ]}%>
    
  2. 我使用计数比较来设置可靠的注意隐藏字段的值。这不是非常对象代码,但意味着代码使用simple_form帮助程序,控制器只使用.save(在create中)和update_attributes(在更新中),而不需要任何进一步的代码。非常好。还略微修改了accepts_nested_attributes_for以查找空白的team_id而不是game_id(永远不会是空白!)。所以代码现在看起来如下......

  3. 游戏模型:

        class Game < ActiveRecord::Base
          belongs_to :venue
          belongs_to :game_type
          has_and_belongs_to_many :tournaments
          has_many :participants, :dependent => :destroy
          has_many :teams, :through => :participants
    
          validates :kickoff, :venue_id, :presence => true
    
          accepts_nested_attributes_for :participants,
            :reject_if => lambda {|a| a[:team_id].blank? },
              :allow_destroy => :true
    
          def home_team
            self.teams.joins(:participants).where("participants.home_team = ?", true).first
          end
    
         def away_team
           self.teams.joins(:participants).where("participants.home_team = ?", false).first
         end
       end
    

    游戏控制器(新建和创建方法)

        class GamesController < ApplicationController
          def new
            @game = Game.new
            2.times { @game.participants.build }
            respond_to do |format|
              format.html # new.html.erb
            end
          end
          def create
            @game = Game.new(params[:game])
            respond_to do |format|
              if @game.save
                format.html { redirect_to games_path, notice: 'Game was successfully created.' }
              else
                format.html { render action: "new" }
              end
            end
          end
        end
    

    游戏视图表单部分(实际上有另一部分,但为了便于阅读而包括在内)

        <%= simple_form_for(@game) do |f| %>
          <%= f.error_notification %>
        <div class="form-inputs">
          <% count = 0 %>
          <%= f.simple_fields_for :participants do |builder| %>
            <% if count == 0 %>
              <% value = true %>
              <% count += 1 %>
            <% else %>
              <% value = false%>
            <% end %>
            <%= builder.input :home_team, :as => :hidden, :input_html => {:value => value} %>
            <%= builder.input :team_id, :collection => Team.all.collect{ |t| [t.full_name, t.id ]}%>
          <% end %>
          <%= f.input :home_goals, :input_html => {:maxlength => 5, :size => 5 }%>
          <%= f.input :away_goals, :input_html => {:maxlength => 5, :size => 5 }%>
          <%= f.input :kickoff %>
          <%= f.input :completed %>
          <%= f.input :game_type_id %>
          <%= f.input :venue_id ,:collection => Venue.all, prompt:  'Choose the venue'%>
        </div>
        <div class="form-actions">
           <%= f.button :submit %>
        </div>
      <% end %>
    

    希望这对某人有用。我认为这是一个很好的解决方案,但如果有人提出改进视图逻辑的建议,我会很感兴趣。

    此致

    彼得