Railsy - jak zarządzać zagnieżdżonymi atrybutami bez użycia accepts_nested_attributes_for?

Mój problem polega na tym, że natknąłem się na ograniczenia accepts_nested_attributes_for, więc muszę wymyślić, jak zreplikować tę funkcję samodzielnie, aby uzyskać większą elastyczność. (Zobacz poniżej, co dokładnie mnie wisi).Więc moje pytanie brzmi: jak powinien wyglądać mój formularz, kontroler i modele, jeśli chcę mimikować i powiększać accepts_nested_attributes_for? Prawdziwa sztuczka polega na tym, że muszę być w stanie zaktualizować zarówno istniejące, jak i nowe modele za pomocą istniejących skojarzeń / atrybutów.

Buduję aplikację, która używa zagnieżdżonych formularzy. Początkowo użyłem tego RailsCast jako planu (wykorzystując accepts_nested_attributes_for):Railscast 196: Zagnieżdżony formularz modelu.

Moja aplikacja to listy kontrolne z zadaniami (zadaniami) i pozwalam użytkownikowi zaktualizować listę kontrolną (nazwa, opis) i dodać / usunąć powiązane zadania w jednym formularzu. Działa to dobrze, ale napotykam problemy, gdy włączę to do innego aspektu mojej aplikacji: historii poprzez wersjonowanie.

Duża część mojej aplikacji polega na tym, że muszę rejestrować informacje historyczne dla moich modeli i skojarzeń. Skończyło się na przetaczaniu własnego wersjonowania (tutaj to moje pytanie, gdzie opisuję mój proces decyzyjny / rozważania), a duża część tego jest przepływem pracy, w którym muszę utworzyć nową wersję starej rzeczy, zaktualizować nową wersję, zarchiwizować starą wersję. Jest to niewidoczne dla użytkownika, który postrzega to doświadczenie jako zwykłe aktualizowanie modelu za pomocą interfejsu użytkownika.

Modele kodu

#checklist.rb
class Checklist < ActiveRecord::Base
  has_many :jobs, :through => :checklists_jobs
  accepts_nested_attributes_for :jobs, :reject_if => lambda { |a| a[:name].blank? }, :allow_destroy => true
end

#job.rb
class Job < ActiveRecord::Base
  has_many :checklists, :through => :checklists_jobs
end

Kod - aktualny formularz (UWAGA: @jobs jest zdefiniowany jako niezarchiwizowane zadania dla tej listy kontrolnej w liście kontrolnej edycji akcji kontrolnej; tak samo jest w @checklist)

<%= simple_form_for @checklist, :html => { :class => 'form-inline' } do |f| %>
  <fieldset>
    <legend><%= controller.action_name.capitalize %> Checklist</legend><br>

    <%= f.input :name, :input_html => { :rows => 1 }, :placeholder => 'Name the Checklist...', :class => 'autoresizer'  %>
    <%= f.input :description, :input_html => { :rows => 3 }, :placeholder => 'Optional description...', :class => 'autoresizer' %>

    <legend>Jobs on this Checklist - [Name] [Description]</legend>

    <%= f.fields_for :jobs, @jobs, :html => { :class => 'form-inline' } do |j| %>
        <%= render "job_fields_disabled", :j => j %>
    <% end %>
    </br>
    <p><%= link_to_add_fields "+", f, :jobs %></p>

    <div class="form-actions">
      <%= f.submit nil, :class => 'btn btn-primary' %>
      <%= link_to 'Cancel', checklists_path, :class => 'btn' %>
    </div>
  </fieldset>
<% end %>

Kod - fragment kodu z checklists_controller.rb # Aktualizacja

def update
  @oldChecklist = Checklist.find(params[:id])

# Do some checks to determine if we need to do the new copy/archive stuff
  @newChecklist = @oldChecklist.dup
  @newChecklist.parent_id = (@oldChecklist.parent_id == 0) ? @oldChecklist.id : @oldChecklist.parent_id
  @newChecklist.predecessor_id = @oldChecklist.id
  @newChecklist.version = (@oldChecklist.version + 1)
  @newChecklist.save

# Now I've got a new checklist that looks like the old one (with some updated versioning info).

# For the jobs associated with the old checklist, do some similar archiving and creating new versions IN THE JOIN TABLE
  @oldChecklist.checklists_jobs.archived_state(:false).each do |u|
    x = u.dup
    x.checklist_id = @newChecklist.id
    x.save
    u.archive
    u.save
  end

# Now the new checklist's join table entries look like the old checklist's entries did
# BEFORE the form was submitted; but I want to update the NEW Checklist so it reflects 
# the updates made in the form that was submitted.
# Part of the params[:checklist] has is "jobs_attributes", which is handled by
# accepts_nested_attributes_for. The problem is I can't really manipulate that hash very
# well, and I can't do a direct update with those attributes on my NEW model (as I'm 
# trying in the next line) due to a built-in limitation.
  @newChecklist.update_attributes(params[:checklist])

I tu trafiam na ograniczenie accepts_nested_attributes_for (jest to udokumentowane całkiem dobrze)tutaj. Mam wyjątek „Nie można znaleźć Model1 z ID = X dla Model2 z ID = Y”, który jest zasadniczo tak samo zaprojektowany.

Jak więc mogę utworzyć wiele zagnieżdżonych modeli i dodać / usunąć je w formularzu modelu macierzystego, podobnie jak w przypadku modelu accepts_nested_attributes_for, ale samodzielnie?

Opcje, które widziałem - są jednym z tych najlepszych?Prawdziwa sztuczka polega na tym, że muszę być w stanie zaktualizować zarówno istniejące, jak i nowe modele za pomocą istniejących skojarzeń / atrybutów. Nie mogę ich połączyć, więc po prostu je wymienię.

Redtape (na github) Virtus (także github)

Dzięki za pomoc!

questionAnswers(2)

yourAnswerToTheQuestion