Чому використання рейок default_scope часто рекомендують проти?


126

Всюди на тих інтернет - людей , кажучи , що використання рейок default_scopeє поганою ідеєю, і топ хіти для default_scopeна StackOverflow це про те , як переписати його. Це переплутано і заслуговує на чітке запитання (я думаю).

Отже: чому default_scopeрекомендується використовувати рейки ?

Відповіді:


192

Завдання 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"

=> Нескопічне видалення ВСІХ областей, які зазвичай можуть застосовуватися до вибраного вами, включаючи (але не обмежуючись ними) асоціації.

Існує кілька способів перезаписати різні ефекти default_scope. Отримати це право ускладнюється дуже швидко, і я б стверджував, що не використовувати default_scopeв першу чергу, було б більш безпечним вибором.


2
Складати: єдиний раз, коли я вважаю default_scope корисним, це коли ви абсолютно хочете завантажувати деякі асоціації за замовчуванням. default_scope {eager_load ([: категорія,: коментарі])}. Однак !!! Якщо ви робите запит підрахунку для такої моделі, як Product.count, це охоче асоціює всі товари. І якщо у вас є записи 50K, ваш запит підрахунку просто перейшов від 15 мс до 500 мс, оскільки, хоча все, що ви хочете, рахується, ваш default_scope залишиться приєднатися до всього іншого.
конунг

16
Мені здається, що проблема полягає в unscopedзамість default_scopeпроблеми 2
Капітан Фогетті

4
@CaptainFogetti Дійсно. Я все ще думаю, що це гарна ідея представити недоліки незробленого як можливий мінус default_scope. У більшості нетривіальних випадків використання default_scope призведе до того, що вам потрібно буде скористатися без набору. Це застереження другого ступеня (за відсутності кращого терміну), яке легко пропустити при дослідженні методу.
wrtsprt

1
Проблема з випадком використання у вашій відповіді полягає в тому, що є багато випадків, коли ви хочете знайти неопубліковані публікації. Насправді я б заперечував, що пошук опублікованих дописів є особливим випадком. Єдиний раз, коли ви хочете публікувати публікації - це коли хтось переглядає загальнодоступну сторінку. Але буває багато разів, коли хочеться бачити неопубліковані публікації.
B Сім

3
Я думаю , добре використання default_scope, коли ви хочете що - то для сортування: default_scope { order(:name) }.
користувач2985898

9

Ще одна причина , щоб не використовувати 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. Отже, база даних призведе до порушення зовнішнього ключа, оскільки вона містить записи, що посилаються на користувача, якого ви хочете видалити.


6

default_scope часто рекомендується проти, оскільки іноді неправильно використовується для обмеження набору результатів. Добре використовувати default_scope - замовити набір результатів.

Я б утримався від використання whereв default_scope і скоріше створив би поле для цього.


1
Друга проблема "Нескопічне видалення ВСІХ областей, які зазвичай можуть бути застосовані до вашого вибору, включаючи (але не обмежуючись ними) асоціації", як і раніше, існує, навіть якщо вона default_scopeмістить order. Така поведінка Росії unscopedдосить несподівана.
Зак Сю

1

Для мене це НЕ погана ідея , але слід використовувати з обережністю!. Є випадок, коли я завжди хотів приховати певні записи, коли встановлюється поле.

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

Так це буде залежати і від реальних потреб.


0

Я вважаю default_scopeсебе корисним лише для того, щоб в будь-якій ситуації входити ascчи впорядковуватись якісь параметри desc. Інакше я уникаю цього, як чуми

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.