default_scope перерывы (update | delete | destroy) _all в некоторых случаях - PullRequest
9 голосов
/ 15 октября 2010

Я считаю, что это ошибка в Rails 3. Я надеюсь, что кто-то здесь может направить меня в правильном направлении.Код, размещенный ниже, предназначен исключительно для иллюстрации этой проблемы.Надеюсь, это не запутает проблему.

Учитывая, что у меня есть модель Post и модель Comment.Post has_many Comments, а Comment принадлежит_ Post.

С установленным по умолчанию параметром_области для модели Post, определяющим отношения joins () и where ().В этом случае, где () зависит от joins ().

Обычно сообщения не будут зависеть от комментариев.Опять же, я просто хочу привести простой пример.Это может быть любой случай, когда where () зависит от joins ().

class Post < ActiveRecord::Base
  has_many :comments, :dependent => :destroy

  default_scope joins(:comments).where("comments.id < 999")
end

class Comment < ActiveRecord::Base
  belongs_to :post, :counter_cache => true
end

Выполнение следующей команды:

Post.update_all(:title => Time.now)

Создает следующий запрос и, в конечном итоге, выдает ActiveRecord ::StatementInvalid:

UPDATE `posts` SET `title` = '2010-10-15 15:59:27'  WHERE (comments.id < 999)

Опять, update_all, delete_all, destroy_all ведут себя одинаково.Я обнаружил такое поведение, когда мое приложение жаловалось при попытке обновить counter_cache.Что в конечном итоге приводит к update_all.

Ответы [ 4 ]

7 голосов
/ 07 декабря 2012

У меня тоже была эта проблема, но нам действительно нужно было иметь возможность использовать update_all со сложными условиями в default_scope (например, без стандартной области видимости невозможна полная загрузка, а вставка именованной области буквально вездеэто совсем не весело).Я открыл запрос на удаление с моим исправлением:

https://github.com/rails/rails/pull/8449

Для delete_all я выдал ошибку, если есть условие соединения, чтобы сделать более очевидным, что вы должны сделать (вместо того, чтобы просто бросить условие соединения и запустить delete_all для всего, вы получите ошибку).

Не уверен, что ребята из rails собираются сделать с моим запросом на получение, но подумал, что это имеет отношение к этой дискуссии(Кроме того, если вам нужно исправить эту ошибку, вы можете попробовать мою ветку и опубликовать комментарий к запросу pull).

4 голосов
/ 05 февраля 2011

I столкнулся с этим также .

Если у вас есть

class Topic < ActiveRecord::Base
  default_scope :conditions => "forums.preferences > 1", :include => [:forum]
end

, и вы делаете

Topic.update_all(...)

, это будетошибка с

Mysql::Error: Unknown column 'forums.preferences' in 'where clause'

Обходной путь для этого:

Topic.send(:with_exclusive_scope) { Topic.update_all(...) }

Вы можете исправить это, используя этот код (и требуя его в environment.rb или еще где)

module ActiveRecordMixins
  class ActiveRecord::Base
    def self.update_all!(*args)
      self.send(:with_exclusive_scope) { self.update_all(*args) }
    end
    def self.delete_all!(*args)
      self.send(:with_exclusive_scope) { self.delete_all(*args) }
    end
  end
end

end

Тогда только вы update_all!или delete_all!когда он имеет область действия по умолчанию.

1 голос
/ 28 августа 2012

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

def self.update_all(*args)
  self.send(:with_exclusive_scope) { super(*args) }
end

def self.delete_all(*args)
  self.send(:with_exclusive_scope) { super(*args) }
end
0 голосов
/ 21 марта 2013

Не думаю, что я бы назвал это ошибкой.Поведение кажется мне достаточно логичным, хотя и не сразу очевидным.Но я разработал решение SQL, которое, кажется, работает хорошо.Используя ваш пример, это было бы:

class Post < ActiveRecord::Base
  has_many :comments, :dependent => :destroy

  default_scope do
    with_scope :find => {:readonly => false} do 
      joins("INNER JOIN comments ON comments.post_id = posts.id AND comments.id < 999")
    end
  end
end

На самом деле я использую рефлексию, чтобы сделать ее более надежной, но вышеприведенная идея пересекает идею.Перемещение логики WHERE в JOIN гарантирует, что она не будет применяться в неподходящих местах.Опция :readonly заключается в противодействии стандартному поведению Rails по созданию объектов joins только для чтения.

Также я знаю, что некоторые люди высмеивают использование default_scope.Но для мультитенантных приложений это идеально подходит.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...