Rails: удалить каскад против зависимого уничтожения
Предполагая, что у меня есть две таблицы: users
а также orders
, У пользователя много заказов, поэтому, естественно, в моей таблице заказов есть внешний ключ user_id.
Какова наилучшая практика в рельсах (с точки зрения скорости, стиля и ссылочной целостности), чтобы гарантировать, что, если пользователь будет удален, все зависимые заказы также будут удалены? Я рассматриваю следующие варианты:
Случай 1. Использование :dependent => :destroy
в пользовательской модели
Случай 2. Определение таблицы заказов в postgres и записи
user_id integer REFERENCES users(id) ON DELETE CASCADE
Есть ли какая-то причина, почему я должен использовать Случай 1? Кажется, дело 2 делает все, что я хочу? Есть ли разница в скорости исполнения?
3 ответа
Это действительно зависит от поведения, которое вы хотите. В случае 1 уничтожение будет вызываться в каждом связанном порядке, и поэтому будут выполняться обратные вызовы ActiveRecord. В случае 2 эти обратные вызовы не срабатывают, но это будет намного быстрее и гарантирует ссылочную целостность.
В младенчестве приложения я бы порекомендовал :dependent => :destroy
потому что это позволяет вам развиваться таким образом, который не зависит от базы данных. Как только вы начнете масштабироваться, вы должны начать делать это в базе данных по соображениям производительности / целостности.
has_many :orders, dependent: :destroy
- Самый безопасный вариант для автоматического поддержания целостности данных.
- У вас есть полиморфные ассоциации, и вы не хотите использовать триггеры.
add_foreign_key :orders, :users, on_delete: :cascade
(в миграции базы данных)
- Вы не используете какие-либо полиморфные ассоциации или хотите использовать триггеры для каждой полиморфной ассоциации.
has_many :orders, dependent: :delete_all
- Используйте только в том случае, если has_many является листовым узлом в дереве ассоциации (т. Е. У дочернего элемента нет другой ассоциации has_many со ссылками на внешний ключ)
Я бы использовал вариант 1. Хотя он может работать, я вижу ряд проблем с вариантом 2:
- ActiveRecord не будет знать, что эти записи были удалены, что может привести к нестабильному поведению
- всем, кто читает код, будет неясно, что удаление пользователя означает, что все его заказы также будут удалены
- любой
destroy
обработчики на ордене не стреляют
Конечно, я ожидал бы, что вариант 2 будет быстрее, но это зависит от вас, если компромиссы того стоят. Является ли удаление пользователя обычной операцией в вашем приложении?
Другой вариант будет использовать :dependent => :delete_all
, Это было бы быстрее, чем :dependent => :destroy
и избегайте недостатков 1 и 2 выше. Смотрите здесь для более подробной информации.