2

簡単に言うと、恐ろしい2(n)クエリの問題が発生しました。n =データベース内のスキルの数の場合、私のcharacters#editフォームはページをロードするために2(n)クエリを実行します。スキルごとにPlayerSkill(結合テーブル)を選択し、スキルごとに1回スキルを検索します。

これが状況に関連すると私が信じるいくつかのコードです。本質的に、このプロセスに関係するモデル、ビュー、およびコントローラーは、モデルの検証が少なく、私が気にしないアクションも少なくなります。

コントローラー:

  # GET /characters/1/edit
  def edit
    @character = Character.find(params[:id], :include => {:player_skills => :skill})
    stub_player_skills
  end

  private
    def stub_player_skills
      @skills = Skill.find(:all)
      @skills.each do |skill|
        if (skill.player_skills.empty?)
          ps = @character.player_skills.build(:skill_id => skill.id, :name => skill.name)
        end
      end
    end

モデル:

class Character < ActiveRecord::Base
  belongs_to :user
  belongs_to :campaign
  has_many :sheets, :dependent => :destroy
  has_many :tokens, :dependent => :destroy

  has_many :player_skills, :dependent => :destroy
  has_many :skills, :through => :player_skills
  accepts_nested_attributes_for :player_skills, :allow_destroy => true
end

問題のあるビュー(HAML):

%h1
  Editing Character

- form_for @character do |f|
  = f.error_messages
  %p
    = f.label :name
    %br
    = f.text_field :name
  %p
    = f.label :race
    %br
    = f.text_field :race
  %p
    = f.label :char_class
    %br
    = f.text_field :char_class
  %p
    -f.fields_for :player_skills do |ps|
      =ps.object.skill.name
      =ps.text_field :level
      =ps.hidden_field :skill_id
      -unless ps.object.new_record?
        =ps.check_box '_destroy'
        =ps.label '_destroy', 'Remove'
      %br
  %p
    = f.submit

状況についての私の理解は、(大まかに)単一の追加クエリで関連付けを取得するための積極的な読み込みが存在することです。

2つの領域で積極的な読み込みを適切に適用する必要がありますが、その方法については途方に暮れています。

steb_player_skillsメソッドでは、キャラクターにPlayerSkillオブジェクトがまだないことを前提として、 PlayerSkillオブジェクトを作成する必要があります。 データベース内の各スキルをループするため、ここで積極的に読み込むことでメリットが得られる可能性があります。これが最初の「nクエリ」の出所です。

次に、ビューで、fields_forは、取得したすべてのPlayerSkillをループします。これは、ここで熱心にロードする方法がないため、= ps.object.skill.nameを呼び出してスキルの名前を出力すると、スキルルックアップが実行されます。 、「n-クエリ」の2番目のセットを取り込みます。

私の主な関心事はビューレイヤーにあります。fields_forを使用してネストされたフォームを生成する場合に、関連付けを熱心にロードする方法を説明するドキュメント(Rails APIなど)が見つかりません。

ありとあらゆる回答をありがとう:)〜Robbie

4

2 に答える 2

1

これを試して、機能するかどうかを確認できますか?

モデルをそのままにしておくことができます。

コントローラは次のようになります

def edit
  # Get all the skill objects once only
  skills = Skill.find(:all)

  # Used to extract Skill#name
  skills_hash = {}
  skills.map { |s| skills_hash[s.id] = s.name }

  # Create an array of the skill-ids
  skill_ids = skills.map { |s| s.id }

  @character = Character.find(params[:id])

  # Determine the character's missing skills
  skill_ids -= @character.player_skill_ids

  # Build all of the missing skills
  skill_ids.each do |id|
    @character.player_skills.build(:skill_id => id, :name => skills_hash[id])
  end
end
于 2010-09-27T23:30:11.720 に答える
0

この問題に対する私の「最終的な」解決策に興味がある人がいる場合:

ここに示すように、スキル名の配列を格納し、カウンターを介してビューでそれを参照することに頼りました。

  %p
    - index = 0
    -f.fields_for :player_skills do |ps|
      =@skill_arr[index]
      =ps.text_field :level
      =ps.hidden_field :skill_id
      -unless ps.object.new_record?
        =ps.check_box '_destroy'
        =ps.label '_destroy', 'Remove'
      - index += 1
      %br

コントローラでは、ほとんどすべてのロジックを、それが属するstub_player_skillsメソッドに移動し、Coderamaの本からページを取り出して、次のことを思いつきました。

  private
    def stub_player_skills
      @skills = Skill.find(:all)
      @skills.each do |skill|
        skill_exists = @character.player_skills.select do |i|
          i.skill_id == skill.id
        end
        if skill_exists.empty?
          ps = @character.player_skills.build(:skill_id => skill.id, :name => skill.name)
        end
      end

      @skill_arr = @character.player_skills.map do |el|
        el.name.nil? ? el.skill.name : el.name
      end
    end

モデルレイヤーでは:include => :skill、has_many:through関係を使用して、さらにいくつかのクエリを削除する必要がありました。

于 2010-09-28T03:14:11.287 に答える