Search code examples
ruby-on-railsruby-on-rails-6

What the difference between implicit_order_column and default_scope in Rails?


What the difference between:

self.implicit_order_column = 'id'

and

default_scope { order('id ASC') }

Solution

  • self.implicit_order_column lets you use another column then the primary key as the implicit ordering column. This effects the way that methods like .first and .last work:

    User.class_eval do 
      self.implicit_order_column = 'created_at'
    end
    
    User.first
    # => SELECT "users".* FROM "users" ORDER BY "users"."updated_at" ASC LIMIT $1  [["LIMIT", 1]]
    
    User.last
    # => SELECT "users".* FROM "users" ORDER BY "users"."updated_at" DESC LIMIT $1  [["LIMIT", 1]]
    

    Setting self.implicit_order_column = 'id' is utterly pointless since the default is the primary key column anyways. The implicit_order_column is of course not used if you provide an explicit order. It does not actually change any other scopes spawned off the class.

    default_scope on the other hand tacks on a default scope to any scopes that you spawn off the class.

    irb(main):001:0> User.all
       (0.5ms)  SELECT sqlite_version(*)
      User Load (0.1ms)  SELECT "users".* FROM "users" LIMIT ?  [["LIMIT", 11]]
    => #<ActiveRecord::Relation [#<User id: 1, role: "admin", created_at: "2020-11-08 19:31:31", updated_at: "2020-11-08 19:31:47">]>
    irb(main):002:1* User.class_eval do
    irb(main):003:1*   default_scope { order(id: :asc) }
    irb(main):004:0> end
    => [#<Proc:0x00000000043703a8 (irb):3>]
    irb(main):005:0> User.all
      User Load (0.2ms)  SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ?  [["LIMIT", 11]]
    => #<ActiveRecord::Relation [#<User id: 1, role: "admin", created_at: "2020-11-08 19:31:31", updated_at: "2020-11-08 19:31:47">]>
    irb(main):006:0> 
    

    The difference here is not immediately apparent. But if you consider that in the SQL world a query with no order clause will not return the records in a determinate order (it's implementation dependent) and here we are actually getting the records in a determinate order. On many RDBMS the results will be hard to destinguish as they might return the records in the order they are modified (if they feel like it).

    Seems brilliant until you realize how non-intuitive default_scope is and how many bugs it leads to down the line.

    irb(main):006:0> User.all.order(:created_at)
      User Load (0.2ms)  SELECT "users".* FROM "users" ORDER BY "users"."id" ASC, "users"."created_at" ASC LIMIT ?  [["LIMIT", 11]]
    => #<ActiveRecord::Relation [#<User id: 1, role: "admin", created_at: "2020-11-08 19:31:31", updated_at: "2020-11-08 19:31:47">]>
    irb(main):007:0>
    

    Or this example:

    irb(main):001:1* User.class_eval do
    irb(main):002:1*   default_scope { where(admin: true) }
    irb(main):003:0> end
    => [#<Proc:0x0000000002bde460 (irb):2>]
    irb(main):004:0> User.new
       (0.6ms)  SELECT sqlite_version(*)
    => #<User id: nil, role: "visitor", created_at: nil, updated_at: nil, admin: true>
    

    Yikes! default_scope is thus widely considered evil.

    See: