Почему использование рельсов default_scope часто рекомендуют против?

Везде в Интернете люди упоминают, что с помощью рельсовdefault_scopeэто плохая идея, и лучшие хиты дляdefault_scope на stackru о том, как перезаписать его. Это кажется запутанным, и заслуживает явного вопроса (я думаю).

Итак: зачем использовать рельсы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 и скорее создайте область для этого.

Для меня это не плохая идея, но ее следует использовать с осторожностью! Есть случай, когда я всегда хотел скрыть определенные записи, когда поле установлено.

  1. Предпочтительно default_scope должен соответствовать значению БД по умолчанию (например: { where(hidden_id: nil) })
  2. Когда вы абсолютно уверены, что хотите показать эти записи, всегда есть unscoped метод, который позволит избежать вашего default_scope

Так что это будет зависеть и от реальных потребностей.

Я только нахожу default_scope быть полезным только при упорядочении некоторых параметров в asc или же desc порядок в любой ситуации. В противном случае я избегаю этого как чума

Другие вопросы по тегам