Почему использование рельсов default_scope часто рекомендуют против?
4 ответа
Проблема 1
Давайте рассмотрим основной пример:
class Post < ActiveRecord::Base
default_scope { where(published: true) }
end
Мотивация сделать дефолт published: true
, может быть, вы должны быть уверены, что хотите показывать неопубликованные (личные) сообщения. Все идет нормально.
2.1.1 :001 > Post.all
Post Load (0.2ms) SELECT "posts".* FROM "posts" WHERE "posts"."published" = 't'
Ну, это в значительной степени то, что мы ожидаем. Теперь давайте попробуем:
2.1.1 :004 > Post.new
=> #<Post id: nil, title: nil, published: true, created_at: nil, updated_at: nil>
И там у нас есть первая большая проблема с областью действия по умолчанию:
=> default_scope повлияет на инициализацию вашей модели
Во вновь созданном экземпляре такой модели default_scope
будет отражено. Поэтому, хотя вы, возможно, и хотели, чтобы случайно не публиковать неопубликованные записи, вы по умолчанию создаете опубликованные.
Проблема 2
Рассмотрим более сложный пример:
class Post < ActiveRecord::Base
default_scope { where(published: true) }
belongs_to :user
end
class User < ActiveRecord::Base
has_many :posts
end
Получим первые сообщения пользователей:
2.1.1 :001 > User.first.posts
Post Load (0.3ms) SELECT "posts".* FROM "posts" WHERE "posts"."published" = 't' AND "posts"."user_id" = ? [["user_id", 1]]
Это выглядит как ожидалось (не забудьте прокрутить вправо, чтобы увидеть часть о user_id).
Теперь мы хотим получить список всех постов - включая неопубликованные - скажем, для пользователя, вошедшего в систему. Вы поймете, что вам нужно "перезаписать" или "отменить" эффект default_scope
, После быстрого Google, вы, вероятно, узнаете о unscoped
, Посмотрим, что будет дальше:
2.1.1 :002 > User.first.posts.unscoped
Post Load (0.2ms) SELECT "posts".* FROM "posts"
=> Unscoped удаляет ВСЕ области, которые обычно могут применяться к вашему выбору, включая (но не ограничиваясь) ассоциации.
Есть несколько способов перезаписать различные эффекты default_scope
, Получение этого права очень быстро усложняется, и я бы сказал, что не используя default_scope
во-первых, был бы более безопасный выбор.
Еще одна причина не использовать default_scope
когда вы удаляете экземпляр модели, которая имеет отношение 1 ко многим с default_scope
модель
Рассмотрим для примера:
class User < ActiveRecord::Base
has_many :posts, dependent: :destroy
end
class Post < ActiveRecord::Base
default_scope { where(published: true) }
belongs_to :user
end
призвание user.destroy
удалит все сообщения, которые published
, но это не удалит сообщения, которые unpublished
, Следовательно, база данных выдаст нарушение внешнего ключа, поскольку она содержит записи, которые ссылаются на пользователя, которого вы хотите удалить.
Часто рекомендуется использовать default_scope, потому что он иногда неправильно используется для ограничения набора результатов. Хорошее использование default_scope - упорядочить набор результатов.
Я бы держался подальше от использования where
в default_scope и скорее создайте область для этого.
Для меня это не плохая идея, но ее следует использовать с осторожностью! Есть случай, когда я всегда хотел скрыть определенные записи, когда поле установлено.
- Предпочтительно
default_scope
должен соответствовать значению БД по умолчанию (например:{ where(hidden_id: nil) }
) - Когда вы абсолютно уверены, что хотите показать эти записи, всегда есть
unscoped
метод, который позволит избежать вашегоdefault_scope
Так что это будет зависеть и от реальных потребностей.
Я только нахожу default_scope
быть полезным только при упорядочении некоторых параметров в asc
или же desc
порядок в любой ситуации. В противном случае я избегаю этого как чума