Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails 4 - eager loading on dependent select causing error (Rails4/Active Admin)

I have an Active panel with a dependent select i.e. the choice on the first dropdown select impacts what appears in the second drop-down.

All was working perfectly well a few months ago but i just realized yesterday it is not working any more.

I managed to find what cause the error: if I remove the eager loading ("include"), then it works again.

NOT WORKING: (current version):

@deal_subsectors = DealSector.find(params[:deal_sector_id],
   include: :deal_subsectors).dealsubsectors

I get this error (form chrome dev tools's Console)

GET http://localhost:3000/admin/deals/deal_subsectors_by_deal_sector?deal_sector_id=2 404 (Not Found)    
send @ jquery.js?body=1:9660    
jQuery.extend.ajax @ jquery.js?body=1:9211    
jQuery.(anonymous function) @ jquery.js?body=1:9357    
jQuery.extend.getJSON @ jquery.js?body=1:9340    
update_deal_subsector @ active_admin.js?body=1:19    
(anonymous function) @ active_admin.js?body=1:12    
jQuery.event.dispatch @ jquery.js?body=1:4666    
elemData.handle @ jquery.js?body=1:4334    
ListPicker._handleMouseUp @ about:blank:632

WORKING, when I remove "include" / eager loading :

@deal_subsectors = DealSector.find(params[:deal_sector_id]).deal_subsectors

It works perfecty in that case.

But I really want to eager load deal subsectors so I wonder what is causing this error, what has changed since it was working. I have a few assumptions but can't find the culprit.

  • Did Rails 4 change the way I should use find(params[:id]..) or the way i should use eager loading

  • did active Admin change the way it handles eager loading: maybe it only works on index and not on edit pages...

  • did turbolinks now on Rails 4 change the way i must eager load?

Here's the code:

- on Active Admin

ActiveAdmin.register Deal do

# controller for the multi-select sector/subsector in the form
# does 2 things at same time: creates method and automatically defines the route of the method defined in controller
    if params[:deal_sector_id] # pass the id
      @deal_subsectors = DealSector.find(params[:deal_sector_id], include: :deal_subsectors).dealsubsectors
    else
      @deal_subsectors = []
    end

    render json: @deal_subsectors
  end   

end

- the form with the 2 dependent selects

form do |f|

f.inputs "Client" do


      f.input :deal_sector_id,
        :label      => "Select industry:",
        :as         => :select,
        :prompt     => true,
        :collection => DealSector.order("name").all.to_a
      f.input :deal_subsector_id,
        :label      => "Select subindustry:",
        :as         => :select,
        :prompt     => true,
        :collection => DealSubsector.order("name").all.to_a        
    end

end

- the javascript powering it:

        // for edit page
        var deal_subsector = { };

       $(document).ready(function() {
         $('#deal_deal_sector_id').change(function() {
            update_deal_subsector();
         });
       });

       function update_deal_subsector() {
           deal_sector_id = $('#deal_deal_sector_id').val(); //get the value of sector id
           url = '/admin/deals/deal_subsectors_by_deal_sector?deal_sector_id=' + deal_sector_id; //make a query to the url passing the deal sector id as a parameter
          $.getJSON(url, function(deal_subsectors) {
              console.log(deal_subsectors);
                  $('#deal_deal_subsector_id').html("") //just blanks the id, blank it before populating it again if sector changes
              for( i = 0; i < deal_subsectors.length; i++) {
                 console.log(deal_subsectors[i]);
                 $('#deal_deal_subsector_id').append("<option value=" + deal_subsectors[i].id + ">" + deal_subsectors[i].name + "</option>")
      };
    }); //pass the url and function to get subsector ids and all we get is assigned to the variable subsector_id
  };

          // for index page (filters)
          $(document).ready(function() {
              $('#q_deal_sector_id').change(function() {
                 update_deal_subsector_filter();
              });
              });

          function update_deal_subsector_filter() {
               deal_sector_id = $('#q_deal_sector_id').val(); //get the value of sector id
               url = '/admin/deals/deal_subsectors_by_deal_sector?deal_sector_id=' + deal_sector_id; //make a query to the url passing the deal sector id as a parameter
               $.getJSON(url, function(deal_subsectors) {
                      console.log(deal_subsectors);
                $('#q_deal_subsector_id').html("") //just blanks the id, blank it before populating it again if sector changes
                 for( i = 0; i < deal_subsectors.length; i++) {
                     console.log(deal_subsectors[i]);
                     $('#q_deal_subsector_id').append("<option value=" + deal_subsectors[i].id + ">" + deal_subsectors[i].name + "</option>")
      };
    }); //pass the url and function to get subsector ids and all we get is assigned to the variable subsector_id
        };

ADDED file

class DealSector < ActiveRecord::Base
  has_many    :deal_subsectors
end

class DealSubsector < ActiveRecord::Base    
  belongs_to  :deal_sector,   :foreign_key => 'deal_sector_id'
end
like image 568
Mathieu Avatar asked Jun 08 '15 18:06

Mathieu


1 Answers

When you call ActiveRecord::find – it means you are expecting to get a one, single model from the DB. Then, you referring to this model and call dealsubsectors – I assume it's has_many relation to your model. It will produce 2 queries: first to fetch original DealSelector, second – all related DealSubsectors. There's nothing you can optimise (unless you dealsubsectors is a custom method in your model, not the relation).

If you see that something hits your DB with queries – you have to look in another place. Say, your form – do you display only one Client per form? If not, it will iterate and fetch all DealSector and DealSubsector again for every new client. Try to provide more code.

like image 134
shlajin Avatar answered Nov 07 '22 04:11

shlajin