Mögliche zu eifrig Last Assoziationen mit nested_attributes?
-
25-09-2019 - |
Frage
Nur kurz, ich habe in einen gefürchteten 2 (n) lief abfragt Problem. Wenn n = die Anzahl der Fähigkeiten in der Datenbank, dann wird meine Zeichen # bearbeiten Form 2 (n) Abfragen, um die Seite zu laden. Es wird eine PlayerSkill SELECT (die Join-Tabelle) einmal jedes Können, und es wird die Fähigkeit einmal pro Fertigkeit nachschlagen.
Hier ist ein Code, den ich glaube, um die Situation relevant sind. Im Wesentlichen sind die Modelle, Ansichten und Controller an diesem Prozess beteiligt, weniger die Modellvalidierungen und weniger die Handlungen ich etwa nicht betroffen.
Der Controller:
# 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
Das Modell (e):
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
Die fehlbare Ansicht (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
Mein Verständnis für die Situation ist, dass eifrig Laden existiert der Verein in (ungefähr) eine einzige zusätzliche Abfrage zu greifen.
Ich brauche eifrig Laden in zwei Bereichen richtig anzuwenden, und ich bin nur mit einem Verlust in Bezug auf, wie es geht.
In der stub_player_skills Methode, braucht es eine PlayerSkill Objekt erstellen das Zeichen unter der Annahme nicht bereits eine haben. Es könnte hier von eifrigen Läden profitieren, weil sie durch jede Fertigkeit in der Datenbank-Schleifen. Dies ist, wo die ersten „n-Abfragen“ kommen aus.
Dann auf der Ansicht, fields_for Schleifen durch alle player wir zerbrochen haben, denn es gibt keine Möglichkeit zu eifrig Last hier, wenn ich rufe = ps.object.skill.name die Fähigkeit des Namens auszudrucken, es tut eine Fähigkeit Lookup, die in dem zweiten Satz bringt "N-Abfragen."
Mein Hauptanliegen liegt in der View-Ebene, finde ich keine Dokumentation (Rails API oder auf andere Weise), dass die Staaten, wie Sie eifrig Last könnten die Assoziationen, wenn Sie fields_for verwenden eine verschachtelte Form zu erzeugen.
Vielen Dank für jeden und alle Antworten :) ~ Robbie
Lösung
Können Sie dies versuchen und sehen, ob es funktionieren wird?
Sie können Ihr Modell den Weg halten es ist.
Ihr Controller kann dann so aussehen:
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
Andere Tipps
Bei anyones interessierte in meiner "endgültigen" Lösung für dieses Problem:
Ich habe zu speichern ein Array der Geschicklichkeit Namen zurückgegriffen, und Referenzierung in der Ansicht über einen Zähler, wie hier zu sehen:
%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
In der Steuerung, ich habe fast die ganze Logik auf die stub_player_skills Methode bewegt, wo es hingehört, und eine Seite aus Coderama Buch nehmen, ich habe kommen mit diesem:
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
In der Modellschicht, musste ich einfach :include => :skill
auf dem has_many. Durch Beziehung loswerden ein paar Anfragen bekommen