#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'
— iircdefault_scope
применяется и к ассоциациям2. Он применяется к ассоциациям, но только с
first/last
, напримерcat.whiskers.first
. Это не применяется при повторении ассоциации, напримерcat.whiskers.each
(ни сWhisker.all.each
).3. @Zubin это хорошее разъяснение того, что я имел в виду под «На самом деле это не меняет никаких других областей, созданных вне класса». — когда вы вызываете
each
, вы неявно порождаете область.