1

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
Nested Software
  • 303
  • 4
  • 15
  • 1
    I'm confused. How can you order records in the `quiz_category_weights` table by a column in the `quiz_categories` table without explicitly joining to it? – Jason Oct 01 '15 at 21:59
  • Please add more information about your the quiz_categories table/model, and how it is connected to quiz_category_weight – Meier Oct 01 '15 at 22:53
  • @Jason, I apologise. I think this was working as I described because I had a default scope set up on QuizCategoryWeight. I've updated the question to clarify what works and what doesn't. – Nested Software Oct 02 '15 at 00:32
  • @Meier, I've updated the question accordingly. Thank you also for editing the sql. I'm new to stackoverflow and I'm still getting the hang of how to format things nicely. – Nested Software Oct 02 '15 at 00:33

2 Answers2

0

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
Nested Software
  • 303
  • 4
  • 15
  • I probably would have wound up recommending that eventually, since I use a `joins().order()` scope structure in my latest app, but you were doing something else that I had to learn about first. You're using single-table inheritance, right? – Jason Oct 02 '15 at 15:53
  • I doubt it is windows. Rails is talking to a (interchangeable) sql driver, and the sql driver is talking to the database, which parses the sql. I can not imagine that they change the sql parser on different platforms. – Meier Oct 02 '15 at 21:58
  • @Jason - Yes, that's why I have to specify additional information for my associations. I didn't remove that because I thought maybe that was the source of the bug. I am hoping that I can get around to creating a simple "gist" that I can submit to highlight this problem on its own. – Nested Software Oct 03 '15 at 02:10
  • @Jason this is OT but if you want to use STI, you may run into problems with paths for your model subclasses. Rails creates the path helpers using the name of the descendant model, not its parent class. See http://stackoverflow.com/questions/4507149/best-practices-to-handle-routes-for-sti-subclasses-in-rails for more information. Sadly this has not really been clarified in the docs as far as I can tell. For the time being I am borrowing this solution: https://gist.github.com/sj26/5843855 – Nested Software Oct 03 '15 at 17:32
  • I actually am going to use STI for a client's project pretty soon. I really appreciate you sharing that! – Jason Oct 03 '15 at 17:33
  • @Jason, no worries, glad that helped! – Nested Software Oct 03 '15 at 20:17
0

The includes method was introduces for relations to give Rails a hint to reduce database queries. It says: When you fetch Objects of type A, also fetch associated objects, because I need them later, and they should not fetched one by one (the N+1 queries problem)

The includes was first implemented with two database queries. First all A, then all B with one of the ids from A. Now includes often uses a sql join to have only one database query. But this is an internal optimisation. The concept is object oriented, you want objects from A, then you retrieve the B through the A. So I think, if you set the order from the included B back to A, you are doing more than was meant for the original includes.

Meier
  • 3,858
  • 1
  • 17
  • 46
  • That's a good point. I am pretty new to Rails and I somehow came across "includes" and missed "joins." I agree the 'joins' method is more appropriate to this use case since I don't need an outer join at all and I am also not brining in any additional attributes besides the sort_order. In general my goal wasn't any kind of query optimisation in the first place. This does strike me as a bug in Rails of some kind though, particularly since sorting this way does work with a named scope. I think at the very least there should be consistent and well defined behaviour for this case... – Nested Software Oct 03 '15 at 02:47