【问题标题】:Rails has_many :through, cocoonRails has_many:通过,茧
【发布时间】:2017-06-02 21:18:15
【问题描述】:

播放器.rb

class Player < ApplicationRecord
belongs_to :user
has_many :player_games, dependent: :destroy, inverse_of: :player
has_many :games, :through => :player_games

validates :firstname, presence: true, length: { minimum: 3, maximum: 88 }
validates :lastname, presence: true, length: { minimum: 3, maximum: 88 }
validates :user_id, presence: true

accepts_nested_attributes_for :player_games, reject_if: :reject_posts, allow_destroy: true

def reject_posts(attributes)
    attributes['game_id'].to_i == 0
    attributes['score'].blank?
    attributes['time'].blank?
end

def initialized_player_games # this is the key method
    [].tap do |o|
        Game.all.each do |game|
            if g = player_games.find { |g| g.game_id == game.id }
                o << g.tap { |g| g.enable ||= true }
            else
                o << PlayerGame.new(game: game)
            end
        end
    end
end
end

players_controller.rb

class PlayersController < ApplicationController
before_action :set_player, only: [:edit, :update, :show, :destroy]
before_action :require_user, except: [:index, :show]
before_action :require_same_user, only: [:edit, :update, :destroy]

before_filter :process_player_games_attrs, only: [:create, :update]

def process_player_games_attrs
  params[:player][:player_games_attributes].values.each do |game_attr|
    game_attr[:_destroy] = true if game_attr[:enable] != '1'
  end
end

.......

private
  # Use callbacks to share common setup or constraints between actions.
  def set_player
    @player = Player.find(params[:id])
  end

  # Never trust parameters from the scary internet, only allow the white list through.
  def player_params
    params.require(:player).permit(:id, :firstname, :lastname, player_games_attributes: [:id, :game_id, :score, :time, :enable, :_destroy] )
  end

  def require_same_user
    if current_user != @player.user and !current_user.admin?
      flash[:danger] = "You can edit or delete only your own player"
      redirect_to root_path
    end 
  end

end

_form of 'PLAYER'

<%= form_for(@player, :html => {class: "az-form", role: "form"}) do |player_form| %>

  <%= player_form.label :firstname, class: "az-form__label" %> <br/>
  <%= player_form.text_field :firstname, class: "az-form__input", placeholder: "Firstname of player", autofocus: true %>

  <%= player_form.label :lastname, class: "az-form__label" %> </br>
  <%= player_form.text_field :lastname, class: "az-form__input", placeholder: "Lastname of player" %>

  <%= player_form.fields_for :player_games, @player.initialized_player_games do |builder| %>

    <% @game = builder.object.game %>

    <%= render 'result_fields', f: builder %>

    <div class="links">
      <%= link_to_add_association 'add result', player_form, :player_games, :partial => 'players/result_fields' %>
    </div>

    <hr>

  <% end %>

  <div class="text-center">
    <%= button_tag(type: "submit", class: "az-form__submit") do %>
        <%= player_form.object.new_record? ? "Create player" : "Update player" %>
    <% end %>
  </div>

<% end %>

_result_fields.html.erb

<div class="nested-fields">

<%= f.hidden_field :game_id, :value => @game.id%>

<div class="row">
  <div class="col-md-12">
    <label class="az-form__label az-form__label--unable js-az-form__checkbox" data-check="<%= @game.id %>">
      <%= f.check_box :enable %>
      <%= @game.title %>
     </label>
  </div>
 </div>

<div class="row">
  <div class="col-md-6">
    <%= f.label :score,
                    class: "az-form__label", :data => {:check => @game.id } %> </br>
     <%= f.number_field :score, step: :any, :data => {:check => @game.id },
                           class: "az-form__input az-form__input--disabled",
                           placeholder: "Score for '#{@game.title}'", disabled: true %>
      </div>
      <div class="col-md-6">
     <%= f.label :time,
                    class: "az-form__label", :data => {:check => @game.id } %> </br>
  <%= f.number_field :time, step: :any, :data => {:check => @game.id },
                           class: "az-form__input az-form__input--disabled",
                           placeholder: "Time for '#{@game.title}'", disabled: true %>
   </div>
 </div>

 <div class="row">
  <div class="col-md-12">
   <%= link_to_remove_association "remove result", f %>
    </div>
  </div>

</div>

问题:

当编辑“玩家”时,每个游戏只能访问一个结果,不能更改其他结果,与“initialized_player_games”方法冲突,但如果我从表单中删除此方法效果很好,但如果不创建新游戏则无法创建另一个游戏操作,我该如何正确更改此方法?

【问题讨论】:

    标签: ruby-on-rails ruby nested-forms has-many-through cocoon-gem


    【解决方案1】:

    如果我理解正确,您想为所有游戏添加一个条目(有效),但添加一个新的Result 不会。问题是@game 可能未定义或设置为最后一场比赛。

    我也不太喜欢这种方法(给fields_for 一个特定的集合)。相反,我会稍微调整这种方法。我不会使用initialized_player_games,而是使用一种方法,在控制器中调用,例如add_default_player_games,类似

    def add_default_player_games 
      Game.all.each do |game|
        if g = player_games.find { |g| g.game_id == game.id }
          g.enable ||= true 
        else
          player_games.build(game: game)
        end
      end
    end
    

    有效地将新实例添加到集合中,而不保存它们。

    所以在你的控制器中你会写

     @player = Player.new
     @player.add_default_player_games
    

    edit

     @player = Player.find(params[:id])
     @player.add_default_player_games
    

    然后您的视图将遍历 player_games

    <%= player_form.fields_for :player_games do |builder| %>
      <%= render 'result_fields', f: builder %>
    <% end %>
    

    然后,如果您使用simple-form,那么如果尚未选择游戏,那么选择游戏将非常容易,并且不需要丑陋的@game

    所以在 _result_fields 中做类似的事情(在 haml 中,因为我是一个懒惰的打字员)

    .nested-fields
      - game_id = f.object.game_id 
      - if game_id.present? 
        = f.hidden_field :game_id
      - else 
        = f.collection_select :game_id, Game.all, :id, :name
      ...
    

    简而言之:如果有game_id,则不允许更改它(显示游戏标题或其他内容会很有用),但如果没有,请使用下拉选择来选择游戏。 其余部分保持不变(仅使用game_id 而不是@game.id)。

    【讨论】:

    • 您的方法工作正常,现在我正在尝试使用 'sample_form' 重建表单,顺便说一下,我在 'f.collection_select :game_id, Game.all.map{|g| 上有错误[g.name, g.id]} ' -> '错误数量的参数',所以我删除了这个 if 语句,它现在可以工作了
    • 我写了这段代码作为例子,没有测试。我修复了collection_select 的定义。如果您根本不需要设置游戏,那么您不需要选择,对我来说,实际上能够选择要为其添加分数的游戏似乎很重要。但这只是一个示例(您可以使用任何替代方法来创建下拉选择选项)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-01-11
    • 2011-11-19
    • 2013-11-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多