不属于belongs_to的ActiveRecord Eager Load模型

时间:2016-01-16 05:40:07

标签: ruby-on-rails activerecord

我遇到了n + 1个查询的问题,我想加载一个关系,除非我在定义关系时遇到问题。这很复杂,哈哈,听我说。

我有两个模特。

class Pokemon < ActiveRecord::Base
  belongs_to :pokemon_detail, primary_key: "level", foreign_key: "level"
end

class PokemonDetail < ActiveRecord::Base
  has_one :pokemons, primary_ley: "level", foreign_key: "level"
end

让我们说,我有以下记录:

<Pokemon id: 1, name: "squirtle", level: 1>

这显然与以下PokemonDetail

相对应
<PokemonDetail id: 1, name: "squirtle", level: 1, health: 150>

这可以像Pokemon.all.includes(:pokemon_detail)那样轻松加载,但是,我希望将信息加载到更高的一级。

<PokemonDetail id: 2, name: "squirtle", level: 2, health: 300>

我目前在Pokemon模型中使用以下方法找到关于更高级别的信息。

def next_level_info
  PokemonDetail.where(level: self.level + 1)
end

但这并不急于加载。有什么想法吗?

1 个答案:

答案 0 :(得分:0)

  1. 首先重构架构,使其更有意义:

    pokemons { current_level_value, name }
    pokemon_levels {value, pokemon_id (foreign key), health }
    
  2. 重新定义这样的模型:

    class PokemonLevel < ActiveRecord::Base
      belongs_to :pokemon
    end
    
    
    class Pokemon < ActiveRecord::Base
      has_many :pokemon_levels
      has_one :current_pokemon_level, -> { joins(:pokemon).where('pokemons.current_level_value = pokemon_levels.value') },
           foreign_key: :pokemon_id, class_name: 'PokemonLevel'
      has_one :next_pokemon_level, -> { joins(:pokemon).where('pokemons.current_level_value + 1 = pokemon_levels.value') },
           foreign_key: :pokemon_id, class_name: 'PokemonLevel'
    end
    
  3. 只需使用它,例如:

    Pokemon.includes(:current_pokemon_level, :next_pokemon_level).find(123)
    
  4. 我使用PokemonLevel代替PokemonDetail,因为它对我来说更清楚