Можно ли удалить delete_all с внутренними условиями соединения? - PullRequest
7 голосов
/ 21 ноября 2010

Мне нужно удалить много записей одновременно, и я должен сделать это на основе условия в другой модели, которое связано отношением "own_to".Я знаю, что могу проходить каждую проверку для условия, но это занимает вечность с моим большим набором записей, потому что для каждого "own_to" он делает отдельный запрос.

Вот пример.У меня есть модель "Product", которая принадлежит "Artist_" и "Artist", и, скажем, у художника есть свойство "is_disabled".

Если я хочу удалить все товары, принадлежащие инвалидам, я быбыть в состоянии сделать что-то вроде:

Product.delete_all(:joins => :artist, :conditions => ["artists.is_disabled = ?", true])

Возможно ли это?Раньше я делал это непосредственно в SQL, но не уверен, что это можно сделать через rails.

Ответы [ 3 ]

4 голосов
/ 03 октября 2011

Проблема в том, что delete_all отбрасывает всю информацию о соединении (и это правильно).То, что вы хотите сделать, это зафиксировать это как внутреннее выделение.

Если вы используете Rails 3, вы можете создать область, которая даст вам то, что вы хотите:

class Product < ActiveRecord::Base
  scope :with_disabled_artist, lambda {
    where("product_id IN (#{select("product_id").joins(:artist).where("artist.is_disabled = TRUE").to_sql})")
  }
end

Вы запрашиваететогда вызов становится

Product.with_disabled_artist.delete_all

Вы можете также использовать тот же запрос в строке, но это не очень элегантно (или самодокументируется):

Product.where("product_id IN (#{Product.select("product_id").joins(:artist).where("artist.is_disabled = TRUE").to_sql})").delete_all
1 голос
/ 18 июня 2015

В Rails 4 (я тестировал на 4.2) вы можете сделать почти так, как изначально хотел OP

Application.joins(:vacancy).where(vacancies: {status: 'draft'}).delete_all

даст

DELETE FROM `applications` WHERE `applications`.`id` IN (SELECT id FROM (SELECT `applications`.`id` FROM `applications` INNER JOIN `vacancies` ON `vacancies`.`id` = `applications`.`vacancy_id` WHERE `vacancies`.`status` = 'draft') __active_record_temp)
0 голосов
/ 16 октября 2012

Если вы используете Rails 2, вы не можете сделать выше. Альтернативой является использование предложения joins в методе find и вызов удаления для каждого элемента.

    TellerLocationWidget.find(:all, :joins => [:widget, :teller_location],
      :conditions => {:widgets => {:alt_id => params['alt_id']},
      :retailer_locations => {:id => @teller_location.id}}).each do |loc|
        loc.delete
    end
...