Search code examples
rubyactiverecordrackruby-grape

ActiveRecord::Base has_many fetch all for a given parent


Question

Below I give my current API result, however, given the SQL that is executed it isn't my Expected or Desired Result. Can anyone assist me in getting the second SQL queries results instead of the first?

Models

console_game.rb

class ConsoleGame < ActiveRecord::Base
    self.table_name = 'console_game'
    self.primary_key = :id

    has_many :configurations, :class_name => 'Configuration'
end

configuration.rb

class Configuration < ActiveRecord::Base
    self.table_name = 'configuration'
    self.primary_key = :id
end

API

resource :configuration do
  desc "Finds all configurations associated with a game."
  params do
    requires :game, type: String, desc: "Game search.", documentation: { example: 'Battlefield 3' }
  end
  route_param :game do
    get do
      ConsoleGame.where(value: 'Battlefield 3').includes(:configurations)
    end
  end
end

Debug Console

D, [2014-05-06T15:11:59.071493 #31068] DEBUG -- : ConsoleGame Load (0.4ms) SELECT console_game.* FROM console_game WHERE console_game.value = 'Battlefield 3' D, [2014-05-06T15:11:59.073289 #31068] DEBUG -- : Configuration Load (0.3ms) SELECT configuration.* FROM configuration WHERE configuration.console_game_id IN (2) 127.0.0.1 - - [06/May/2014 15:12:08] "GET /configuration/game HTTP/1.1" 200 34 25.9740

Result - get /configuration/game

[
    {
        id: 2,
        value: "Battlefield 3"
    }
]

Desired Result - get /configuration/game

[
    {
        id: 2,
        value: "x",
        device: 4,
        system: null,
        console_game_id: 2,
        creator: "yrdy",
        created_date: null,
        positive_votes: 0,
        negative_votes: 0
    },
    {
        id: 4,
        value: "ffds",
        system: 2,
        console_game_id: 2,
        creator: "gdfs",
        created_date: null,
        positive_votes: 0,
        negative_votes: 0
    }
]

Note: There are a lot of foreign indexes on this table and i'd like to resolve them all but perhaps thats for another day.

Result of ConsoleGame.where(value: 'Battlefield 3').includes(:configurations).as_json(include: :configurations)

[
    {
        id: 2,
        value: "Battlefield 3",
        configurations: [
            {
                id: 2,
                value: "ffds",
                device: 4,
                system: null,
                console_game_id: 2,
                creator: "yrdy",
                created_date: null,
                positive_votes: 0,
                negative_votes: 0
            },
            {
                id: 4,
                value: "x",
                device: 4,
                system: 2,
                console_game_id: 2,
                creator: "gdfs",
                created_date: null,
                positive_votes: 0,
                negative_votes: 0
            }
        ]
    }
]

Solution

  • Does this work?

    class ConsoleGame < ActiveRecord::Base
      #self.table_name = 'console_game'
      #self.primary_key = :id
    
      has_many :configurations #, :class_name => 'Configuration'
    end
    
    class Configuration < ActiveRecord::Base
      #self.table_name = 'configuration'
      #self.primary_key = :id
      belongs_to :console_game #, class_name: "ConsoleGame", primary_key: 'console_game_id', foreign_key: 'id'  #added relationship for ConsoleGame
    end
    
    resource :configuration do
      desc "Finds all configurations associated with a game."
        params do
          requires :game, type: String, desc: "Game search.", documentation: { example: 'Battlefield 3' }
        end
        route_param :game do
          get do
            #Search Configurations based on console game
            Configuration.joins(:console_game).where(console_game:{value: 'Battlefield 3'})
          end
        end
    end
    

    Better yet add a scope

    class Configuration < ActiveRecord::Base
      #self.table_name = 'configuration'
      #self.primary_key = :id
      belongs_to :console_game #, class_name: "ConsoleGame", primary_key: 'console_game_id', foreign_key: 'id'  #added relationship for ConsoleGame
      scope :by_game,->(game){
               #verbosely
               #game_ids = ConsoleGame.where(value: game).pluck(:id)
               #where(console_game_id: game_ids)
               #one line
               joins(:console_game).where(console_game:{value: game})
               }
    end    
    

    Use as

    Configuration.by_game('Battlefield 3')
    

    EDIT

    I have commented out the lines that are not needed because they are implied by design