В чем разница между implicit_order_column и default_scope в Rails?

#ruby-on-rails #ruby-on-rails-6

#ruby-on-rails #ruby-on-rails-6

Вопрос:

В чем разница между:

 self.implicit_order_column = 'id'
 

и

 default_scope { order('id ASC') }
 

Ответ №1:

self.implicit_order_column позволяет использовать другой столбец, а затем первичный ключ в качестве столбца неявного упорядочения. Это влияет на то, как методы .first работают и .last работают:

 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]]
 

Настройка self.implicit_order_column = 'id' совершенно бессмысленна, поскольку по умолчанию используется столбец первичного ключа. Конечно, implicit_order_column не используется, если вы указываете явный порядок. На самом деле это не меняет никаких других областей, созданных вне класса.

default_scope с другой стороны, привязывает область по умолчанию к любым областям, которые вы создаете из класса.

 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> 
 

Разница здесь не сразу очевидна. Но если вы считаете, что в мире SQL запрос без предложения order не вернет записи в определенном порядке (зависит от его реализации), и здесь мы фактически получаем записи в определенном порядке. Во многих СУБД результаты будет трудно отличить, поскольку они могут возвращать записи в том порядке, в котором они были изменены (если им этого захочется).

Кажется блестящим, пока вы не поймете, насколько это неинтуитивно default_scope и к скольким ошибкам это приводит в дальнейшем.

 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>
 

Или этот пример:

 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>
 

Блин! default_scope таким образом, широко считается злом.

См:

Комментарии:

1. Вот ошибка: к сожалению, implicit_order_column применяется только к областям самого объекта, а не к ассоциациям, поэтому вам нужно указать class Cat; has_many :whiskers, -> { order(timestamp: "asc") } , даже если class Whisker; self.implicit_order_column = 'timestamp' — iirc default_scope применяется и к ассоциациям

2. Он применяется к ассоциациям, но только с first/last , например cat.whiskers.first . Это не применяется при повторении ассоциации, например cat.whiskers.each (ни с Whisker.all.each ).

3. @Zubin это хорошее разъяснение того, что я имел в виду под «На самом деле это не меняет никаких других областей, созданных вне класса». — когда вы вызываете each , вы неявно порождаете область.