2010-10-15 11 views
9

Wierzę, że jest to błąd w Rails 3. Mam nadzieję, że ktoś tutaj może sterować mną we właściwym kierunku. Zamieszczony poniżej kod jest jedynie ilustracją tego problemu. Mam nadzieję, że to nie myli problemu.default_scope breaks (update | delete | destroy) _all w niektórych przypadkach

Biorąc pod uwagę Mam model Post i model komentarza. Post has_many Komentarze i komentarze belongs_to Post.

Za pomocą zestawu default_scope w modelu Post, definiowanie powiązań() i where(). W tym przypadku gdzie() zależy od złączeń().

Zwykle posty nie będą zależne od komentarzy. Ponownie, chcę tylko dać prosty przykład. Może to być przypadek, gdy funkcja where() zależy od join().

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 

uruchamiając następującą komendę:

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

produkuje następujące zapytanie, a ostatecznie rzuca ActiveRecord :: StatementInvalid:

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

Znowu update_all, delete_all, destroy_all zachowują się tak samo . Odkryłem to zachowanie, gdy moja aplikacja skarżyła się, próbując zaktualizować counter_cache. Które ostatecznie wierci w dół do update_all.

Odpowiedz

4

I ran into this as well.

Jeśli masz

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

i robisz

Topic.update_all(...) 

to będzie niepowodzeniem z

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

Prace wokół tego jest:

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

Można małpa załatać za pomocą tego kodu (i wymagające go w environment.rb lub gdzie indziej)

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 

koniec

Wtedy tylko ty update_all! lub delete_all! kiedy ma zasięg domyślny.

+0

Hej, Ben, otworzyłeś problem na ten temat? –

1

Można również zrobić to na poziomie klasy, bez tworzenia nowych metod, jak w przykładzie:

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 
7

miałem ten problem też, ale my naprawdę potrzebne, aby móc korzystać z update_all złożonych warunkach w default_scope (na przykład bez domyślnego zakresu ładowanie-ładowanie jest niemożliwe, a wklejenie nazwanego zakresu dosłownie wszędzie nie jest zabawne).Otworzyłem wniosek Pociągnąć tutaj z moim Fix:

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

Dla delete_all mam podniesione błąd, jeśli nie jest to warunek przyłączenia aby uczynić go bardziej oczywiste, co trzeba zrobić (zamiast po prostu rzucając przyłączyć warunek i uruchomienie delete_all we wszystkim, pojawia się błąd).

Nie jestem pewien, co koledzy zrobią z moją prośbą o pociągnięcie, ale myśleli, że to było istotne dla tej dyskusji. (Jeśli ten błąd zostanie naprawiony, możesz wypróbować mój oddział i opublikować komentarz na temat żądania pobrania.)

0

Nie sądzę, że bym to nazwał błędem. Zachowanie wydaje mi się logiczne, chociaż nie jest to od razu oczywiste. Ale opracowałem rozwiązanie SQL, które wydaje się działać dobrze. Na twoim przykładzie będzie to:

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 

W rzeczywistości używam refleksji, aby uczynić ją bardziej wytrzymałą, ale powyższe daje ideę krzyżową. Przeniesienie logiki WHERE do JOIN zapewnia, że ​​nie zostanie zastosowane w nieodpowiednich miejscach. Opcja :readonly ma przeciwdziałać domyślnemu zachowaniu się Railsów, powodując, że obiekty stają się tylko do odczytu.

Ponadto wiem, że niektórzy ludzie wyśmiewają się z używania default_scope. Ale w przypadku aplikacji dla wielu dzierżawców jest to idealne dopasowanie.

Powiązane problemy