12

Jako przykład kołowej dependent: :destroy numerze:Czy istnieje zależny od kodu Rails 4:: niszczyć obejście?

class User < ActiveRecord::Base 
    has_one: :staff, dependent: :destroy 
end 

class Staff < ActiveRecord::Base 
    belongs_to :user, dependent: :destroy 
end 

Jeśli zadzwonię user.destroy, powiązany staff powinny zostać zniszczone, jak również. Odwrotnie, wywołanie staff.destroy powinno również zniszczyć powiązany z nim user.

To działało świetnie w Railsach 3.x, ale zachowanie zmieniło się w Rails 4.0 (i kontynuuje w 4.1), tak że tworzy się pętla i ostatecznie pojawia się błąd, "poziom stosu zbyt głęboki". Jednym z oczywistych sposobów obejścia tego problemu jest utworzenie niestandardowego wywołania zwrotnego za pomocą before_destroy lub after_destroy w celu ręcznego zniszczenia powiązanych obiektów zamiast korzystania z mechanizmu dependent: :destroy. Nawet sytuacja issue in GitHub opened for this miała kilka osób polecających to obejście.

Niestety, nie mogę nawet obejść tego problemu. To jest to, co mam:

class User < ActiveRecord::Base 
    has_one: :staff 

    after_destroy :destroy_staff 

    def destroy_staff 
    staff.destroy if staff and !staff.destroyed? 
    end 
end 

Powodem tego nie działa jest to, że zawsze zwraca falsestaff.destroyed?. Więc tworzy cykl.

+0

czy zaimplementowałeś tę logikę do obu modeli, tak jak sugeruje Jken13579? – xlembouras

+0

@xlembouras, nie próbowałem, ale jestem prawie pewien, że to nie zadziała. Spróbuję tego kiedyś w tym tygodniu, żeby się upewnić. Poniżej napisałem, dlaczego to nie zadziała. –

+0

@at. jakiekolwiek szczęście z moją odpowiedzią? –

Odpowiedz

2

Zmierzyłem się także z tą kwestią i zaproponowałem rozwiązanie, które nie jest ładne, ale działa. Zasadniczo wystarczy użyć wartości destroy_user podobnej do destroy_staff.

class User < ActiveRecord::Base 
    has_one: :staff 

    after_destroy :destroy_staff 

    def destroy_staff 
    staff.destroy if staff && !staff.destroyed? 
    end 
end 

class Staff < ActiveRecord::Base 
    belongs_to :user 

    after_destroy :destroy_user 

    def destroy_user 
    user.destroy if user && !user.destroyed? 
    end 
end 
+0

Dziwne, stackoverflow nie zaktualizował mojej skrzynki odbiorczej, że miałeś odpowiedź. Spróbuję tego, ale jestem pewien, że to nie zadziała. Próbowałem tak wielu odmian. Zasadniczo każde '.destroyed?' Wywołanie *** zawsze *** zwraca wartość false w Railsach 4.1, jeśli zostało wywołane z wywołania 'before_destroy' lub' after_destroy'. –

+0

Mam nadzieję, że działa on dla ciebie w Rail 4.1, ponieważ przetestowałem go tylko w Rails 4.0, chociaż wydaje mi się, że to nie ma znaczenia. –

4

Jeśli jedna strona cyklu ma tylko jeden zwrotna że można zastąpić jedną z dependent: :destroy z dependent: :delete

class User < ActiveRecord::Base 
    # delete prevents Staff's :destroy callback from happening 
    has_one: :staff, dependent: :delete 
    has_many :other_things, dependent: :destroy 
end 

class Staff < ActiveRecord::Base 
    # use :destroy here so that other_things are properly removed 
    belongs_to :user, dependent: :destroy 
end 

Pracował dla mnie świetnie, dopóki jedna ze stron nie potrzebuje innego wywołania zwrotne na ogień.