2013-09-11 10 views
6

Mam dość typowy Zamówienie modelu, który has_manyLinieJak mogę potwierdzić, że stosunki has_many nie są zmieniane

class Order < ActiveRecord::Base 
    has_many :lines 
    validates_associated :lines 

Gdy zamówienie jest zakończone, to nie powinno być możliwe, aby zmienić atrybuty lub powiązane linie (choć możesz zmienić status, aby nie został ukończony).

validate do 
    if completed_at.nil? == false && completed_at_was.nil? == false 
     errors.add(:base, "You can't change once complete") 
    end 
    end 

Działa to dobrze, aleJeśli dodawać, usuwać lub zmieniać związane Lines, to nie jest niemożliwe.

W moim linii modelu, mam następujący Walidacja:

validate do 
    if order && order.completed_at.nil? == false 
    errors.add(:base, "Cannot change once order completed.") 
    end 
end 

ten skutecznie zatrzymuje linie wypełnionego zamówienia została zmodyfikowana i zapobiega linia dodawane do wypełnionego zamówienia.

Muszę również zabezpieczyć linie przed usunięciem z kompletnego zamówienia. Próbowałem to w modelu linia:

validate do 
    if order_id_was.nil? == false 
    if Order.find(order_id_was).completed_at.nil? == false 
     errors.add(:base, "Cannot change once order completed.") 
    end 
    end 
end 

Działa to dobrze, aby zapobiec Linia wycofywana o Zakonu podczas modyfikacji Linia bezpośrednio. Jednak podczas edytowania Zamówienie i usunąć linii, sprawdzanie poprawności nigdy nie działa, ponieważ zostało już usunięte z Zamówienie.


... Tak w skrócie, jak mogę potwierdzić, że Linie związane ze Zakonu nie zmieniają i nie są dodawane lub usuwane?

Myślę, że brakuje mi czegoś oczywistego.

Odpowiedz

3

z sekcji "Stowarzyszenie wywołania zwrotne" z ActiveRecord::Associations, zobaczysz, że istnieje kilka wywołania zwrotne, które można dodać do swojej definicji has_many:

  • before_add
  • after_add
  • before_remove
  • after_remove

również z tych samych dokumentów:

Jeżeli którykolwiek z before_add callbacków wyjątek, obiekt nie zostanie dodany do kolekcji. To samo dotyczy wywołań zwrotnych before_remove; jeśli zostanie zgłoszony wyjątek, obiekt nie zostanie usunięty.

Może można dodać metody wywołania zwrotnego do before_add i before_remove sprawia, że ​​na pewno tak nie jest zamarznięta i zgłasza wyjątek, jeśli nie jest to dozwolone.

has_many :lines, 
     before_add: :validate_editable!, 
     before_remove: :validate_editable! 

private 

def validate_editable_lines!(line) 
    # Define the logic of how `editable?` works based on your requirements 
    raise ActiveRecord::RecordNotSaved unless editable?(line) 
end 

Inną rzeczą warto spróbować byłoby dodać błąd sprawdzania poprawności i powrócić false ciągu validate_editable_lines! Jeśli test poprawności nie powiedzie się. Jeśli to działa, polecam oczywiście zmianę nazwy metody na validate_editable_lines (sans ! bang). :)

+0

Ah, to jest bliskie ideału. Dziękuję Ci. Chciałbym, aby powodowało błąd, a nie wyjątek, jeśli jest to możliwe, jak sugerujesz na końcu, jednak dodanie błędu/zwrócenie false nie działa. Jeśli masz dalsze przemyślenia na ten temat, byłoby wspaniale. Zauważ, że polecenie before_add/remove przekazuje wiersz w args, więc jest to 'validate_editable! (Line)'. – Colin

+0

Krótki google wydaje się pokazywać, że nie da się tego zrobić ... więc myślę, że jest to sposób na wymuszenie tego, ale potrzebuję też czegoś w kontrolerze. – Colin

+0

Masz rację, pojawia się. Prawdopodobnie możesz rzucić własny wyjątek i "uratować" go w kontrolerze. Nadal będzie całkiem czysty. –

0

Jest to interesujący problem, a według mojej najlepszej wiedzy nieco trudny do rozwiązania.

Oto jeden sposób: http://anti-pattern.com/dirty-associations-with-activerecord

Innym podejściem, które moim zdaniem jest nieco czystsze byłoby po prostu sprawdzić na poziomie kontrolera przed dodaniem/usunąć Line, a nie używać walidacji.

Jeszcze innym sposobem jest można dodać before_create i before_destroy zwrotnych do Line, i sprawdzić, czy instancja Order została zakończona.

1

Może dodać do modelu atrybut locked, a po zakończeniu zamówienia ustawić wartość locked na true. Następnie dodaj do kontrolera before_filter, który zostanie wywołany przed aktualizacją, aby sprawdzić wartość flagi locked.Jeśli jest ustawiony na true, wówczas podnieś błąd/powiadomienie/cokolwiek do użytkownika, że ​​tego elementu zamówienia nie można zmienić.

Powiązane problemy