2

Вкратце, я столкнулся с проблемой проблем 2 (n) запросов. Если n = количество навыков в базе данных, то мои символы # edit form будут принимать 2 (n) запросов для загрузки страницы. Он будет ВЫБРАТЬ PlayerSkill (таблица соединения) после каждого навыка, и он будет искать навык один раз за умение.Возможно ли загружать ассоциации с помощью nested_attributes?

Вот какой-то код, который, я считаю, уместен в этой ситуации. По сути, модели, представления и контроллеры, участвующие в этом процессе, меньше валидности модели и меньше действий, которые меня не волнуют.

Контроллер:

# 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 

Мое понимание ситуации заключается в том, что нетерпеливой загрузке существует, чтобы захватить ассоциацию в (примерно) одном дополнительном запросе.

Мне нужно правильно применить нетерпеливую загрузку в двух областях, и я не понимаю, как это сделать.

В методе stub_player_skills ему необходимо создать объект PlayerSkill , предполагая, что персонаж его еще не имеет. Здесь можно извлечь выгоду от загружаемой загрузки, поскольку она проходит через каждое умение в базе данных. Здесь появляются первые «n-запросы».

Затем, при просмотре, fields_for проходит через все PlayerSkills, которые мы собрали, потому что здесь нет возможности загружать нагрузку, когда я вызываю = ps.object.skill.name, чтобы распечатать имя умения, это выполняет поиск навыка, который включает второй набор «n-запросов».

Моя основная проблема заключается в уровне представления, я не могу найти никакой документации (Rails API или иначе), в которой говорится, как можно было бы загружать ассоциации, если вы используете поля для создания вложенной формы.

Спасибо за любые ответы :) ~ Робби

ответ

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 
+0

Это не совсем работает, потому что мне все равно нужно перебирать все возможные навыки (см. Stub_player_skills) и проверить, существует ли PlayerSkill. Представьте, что у нас есть навык №1, №2 и №3. Если у меня есть навык №1 и №3, тогда нам нужно заглушить №2. Я думаю, проблема в том, что я использую ActiveRecord для выполнения другой «находки», нужно ли вместо этого перебирать массив в моей бизнес-логике? – Robbie

+0

Прошу прощения, я полностью пропустил эту часть. Я вижу, что вы сейчас пытаетесь сделать ... Я пересмотрю свой пост. – Coderama

+0

С небольшими изменениями я получил эту работу, и контроллер больше не требует n-запросов для загрузки PlayerSkills. Тем не менее, просмотр все еще принимает n-запросы, потому что я вызываю вызов ps.object.skill.name; Мне кажется, что это может быть ограничением в том, как Rails обрабатывает поля, хотя это и довольно неудачно. – Robbie

0

В случае anyones заинтересован в моем «окончательном» решении этой проблемы:

Я прибег к запоминанию массива имен навыков и ссылаться на него в представлении с помощью счетчика, как показано здесь:

%p 
    - index = 0 
    -f.fields_for :player_skills do |ps| 
     [email protected]_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: через отношения чтобы избавиться от еще нескольких запросов.