ruby-on-railssortingruby-on-rails-4has-manydefault-scope

Rails 4 Relation is not being sorted: 'has_many' association w/ default scope, 'includes' and 'order' chained together


I am trying to use a default scope to impose a sort order on the model QuizCategoryWeight. The goal is to get @possible_answer.quiz_category_weights to return the weights in sorted order.

Update: I have narrowed the problem down to the fact that default scopes seem to work for me as long as they just have an 'order' method but not when the 'includes' method is chained with the 'order' method. However, this chaining does work for named scopes.

Could it be my development environment? Or is this a bug in Rails perhaps?

I am using windows, so maybe that's the problem. Currently on ruby 2.0.0p645 (2015-04-13) [i386-mingw32] and Rails 4.2.4...

The following, using a default scope on QuizCategoryWeight, does not seem to work:

class QuizCategoryWeight < ActiveRecord::Base
    #trying to use a default scope, but does not work
    default_scope { includes(:quiz_category).order("quiz_categories.sort_order") }

    belongs_to :possible_answer, inverse_of: :quiz_category_weights,
        class_name: 'QuizPossibleAnswer', foreign_key: 'possible_answer_id'

    belongs_to :quiz_category

end

class QuizPossibleAnswer < PossibleAnswer
    has_many :quiz_category_weights, 
        #does not work whether the line below is used or not
        ->{ includes(:quiz_category).order("quiz_categories.sort_order") }, 
        inverse_of: :possible_answer, 
        dependent: :destroy, 
        foreign_key: 'possible_answer_id' 
end

class QuizCategory < ActiveRecord::Base
    default_scope { order :sort_order }
end  

With a named scope, it does work. However, this means that I have to add an argument to my form builder to use the collection 'f.object.quiz_category_weights.sorted'.

class QuizCategoryWeight < ActiveRecord::Base
    # named scope works...
    scope :sorted,  ->{ includes(:quiz_category).order("quiz_categories.sort_order") }

    belongs_to :possible_answer, inverse_of: :quiz_category_weights,
        class_name: 'QuizPossibleAnswer', foreign_key: 'possible_answer_id'

    belongs_to :quiz_category

end

class QuizPossibleAnswer < PossibleAnswer
    has_many :quiz_category_weights, 
        inverse_of: :possible_answer, 
        dependent: :destroy, 
        foreign_key: 'possible_answer_id' 
end

Solution

  • I think there is a bug with using 'includes' with a default scope, either in the Rails framework generally or in my windows version.

    However, I've found that using 'joins' does work. I'm not using any of other the attributes from QuizCategory so it's more appropriate to my use case as well: I only want to sort using the 'sort_order' attribute from the joined table.

    The fixed code is:

    class QuizCategoryWeight < ActiveRecord::Base
        default_scope { joins(:quiz_category).order("quiz_categories.sort_order") }
    
        belongs_to :quiz_category
    end