2012-07-14 11 views
17

Używam klejnotu nested_form dla mojej relacji AddressBook. Kiedy wykroje użytkownika z wartości istniejącego Addr, chcę usunąć Addr że zamiast oszczędności z pustym valuejak uniknąć zapisywania pustych rekordów na szynie zagnieżdżonej formularz

class Person < ActiveRecord::Base 
    has_many :addrs, dependent: :destroy 
    attr_accessible :name, :addrs_attributes 
    accepts_nested_attributes_for :addrs, reject_if: :addr_blank, allow_destroy: true 

    def addr_blank(a) 
    valid? && a[:id].blank? && a[:value].blank? 
    end 

class Addr < ActiveRecord::Base 
    belongs_to :person 
    attr_accessible :kind, :label, :value, :person_id 

My :reject_if metoda działa dobrze, ale to nie daje mi wszystko, czego potrzebuję

  1. valid? utrzymuje moje puste Addrs wokół poprzez walidację
  2. a[:id].blank? uniknięcie odrzucenia, gdy użytkownik wychodzi i wykroje istniejący rekord

Teraz muszę usunąć (a nie zapisać) istniejący Addr, gdy użytkownik opróżnia numer value. Ponadto udostępniam osoby i dodatki za pośrednictwem interfejsu API RESTful. Widzę dwie możliwe opcje:

  1. procesu POST params skrótu, aby dodać magiczną _destroy=1 param. IOW, emuluje aktywność użytkownika polegającą na naciśnięciu przycisku kasowania.
  2. Zawiera to wewnątrz modelu Addr, tak że aktualizacja pustą value jest skutecznie uznawana za usunięcie.

oparciu o poradę Oto jak I wdrożone go:

people_controller.rb

def update 
    @person = Person.find(params[:id]) 
    @person.destroy_blank_addrs(params[:person]) 
    respond_to do |format| 
    ... 

person.rb

def destroy_blank_addrs(person_params) 
    if valid? && person_params[:addrs_attributes] 
    person_params[:addrs_attributes].each do |addr_params_array| 
     addr_params= addr_params_array[1] 
     addr_params[:_destroy] = '1' if !addr_params[:id].blank? && addr_params[:value].blank? 
    end 
    end 
end 
+0

Z tych dwóch, użyj opcji 1. Nie chcesz „magię” jak „jeżeli wartość X pole jest puste, a następnie usunąć rekord”. – Zabba

+0

Zaktualizowałem pytanie, proponując rozwiązanie. –

+0

@Zabba, przebudowuję ten kod 18 miesięcy później i miałeś rację. Mój pomysł zatarcia wartości jako "magicznego" 'destroy_blank_addrs' był martwy mózg. Wierzę również, że każde rozwiązanie, które wymaga bezpośredniej modyfikacji tablicy 'params'ów jest złą praktyką. Wszelkie przetwarzanie powinno być wykonywane po 'assign_attributes', ale przed' save' –

Odpowiedz

5

Trzecią alternatywą byłoby aby dodać wywołanie zwrotne before_save w Perso n spowoduje usunięcie wszystkich adresów, które są puste. Ten pomysł ma pewną wartość, ale prawdopodobnie nie pójdę z tym.

Spośród dwóch przedstawionych opcji, nie przejdę do przetwarzania kolejnych parametrów. To się uda, ale to za dużo pracy. Poza tym, do kodu kontrolera będzie trochę bałaganu i jestem zwolennikiem bardzo wąskiego kontrolera.

Najprostszym rozwiązaniem w mojej głowie jest usunięcie pustych adresów po zapisaniu. Możesz dodać Person#remove_blank_addresses(), a następnie wywołać go po pomyślnym zapisaniu. Nie musisz przekazywać parametrów - możesz po prostu powtórzyć adresy i usunąć puste. Ma to tę wadę, że tworzy puste adresy, a następnie je niszczy, ale i tak trzeba by to zaktualizować.

Jeśli mówimy o najczystszym rozwiązaniu (moim zdaniem), wprowadziłbym trzecią klasę, która obsłużyłaby całą tę logikę i zleciłaby to kontrolerowi. Sterownik byłby łatwy do przetestowania w izolacji, a następnie można napisać specyfikację modelu, która sprawdza wszystkie drobiazgowe szczegóły. To jest trochę więcej pracy i nie mogę teraz wymyślić dobrego imienia (PersonUpdater?), Ale może to być pomysł, o którym warto pomyśleć.

+1

Dzięki za przemyślaną odpowiedź, Stefan. Trzecia klasa to najczystsze rozwiązanie, ale za dużo wysiłku. Pomysł umieszczenia rekordów w bazie danych, które są dostępne dla niektórych procesów asynchronicznych (nawet na chwilę), wydaje się błędny. Innym pomysłem jest zarządzanie nim z przodu w JS. Blanking to kolejny sposób naciśnięcia przycisku kasowania. Wtedy Blanking nigdy nie robi tego w moim apelu REST. –

+0

W takim przypadku powinienem mieć ten kod w kontrolerze. Trochę trudniej jest przetestować i jakoś wygląda na niechlujną w moich przekonaniach co do tego, co powinno być w kontrolerze, ale powinno to być najczystsze rozwiązanie w twoim przypadku. –

+0

Dzięki, poszedłem z podejściem kontrolera, ponieważ czułem, że jest to prostsze i dodaje funkcjonalność do mojego API, gdzie konsumenci mogą usuwać Addrs, przez wygaszanie wartości. Jeśli zaktualizujesz swoją odpowiedź, zaakceptuję. –

14
accepts_nested_attributes_for :addrs, 
    allow_destroy: true, 
    :reject_if => proc { |att| att[:name].blank? && attr[:description].blank? } 
8
accepts_nested_attributes_for :addrs, 
    allow_destroy: true, 
    reject_if: -> { |attr| [name, description].any? &:blank? } 
+0

Bardzo zwięzłe. Dołączam test "valid?", Więc wiersze nie są odrzucane, dopóki użytkownik nie zakończy walidacji. –

1
accepts_nested_attributes_for :addrs, 
    allow_destroy: true, 
    reject_if: :all_blank 
+0

Umożliwia określenie Proca lub Symbolu wskazującego metodę, która sprawdza, czy należy utworzyć rekord dla określonego skrótu atrybutu. Hash jest przekazywany do dostarczonego procesu lub metody i powinien zwracać wartość true lub false. Jeśli nie podano: reject_if, zostanie utworzony rekord dla wszystkich skrótów atrybutów, które nie mają wartości _destroy, która jest równa true. Przekazanie: all_blank zamiast Proc stworzy proces, który odrzuci rekord, w którym wszystkie atrybuty są puste z wyłączeniem wartości dla _destroy. – hadees

Powiązane problemy