I have Tasks and Users. When a user completes a task, I create a Completion which has a field for the user to indicate how long they spent. I need a form that shows all the tasks with their completion status and time_spent attribute. On submit, completions that existed should be updated and new ones should be created. I'd like to do this in Formtastic if possible but I'll be happy with a basic Rails 3 solution.
class Completion < ActiveRecord::Base
belongs_to :task
belongs_to :user
# attribute time_spent
end
class User < ActiveRecord::Base
has_many :completions
has_many :tasks, :through => :completions
end
class Task < ActiveRecord::Base
belongs_to :milestone
has_many :completions
has_many :users, :through => :completions
end
An extra aspect is that I want to show just a certain set of tasks, such as those belonging to a Milestone. Should I have a form on the Milestone controller that posts to the Completions controller?
class Milestone < ActiveRecord::Base
has_many :tasks
has_many :completions, :through => :tasks
end
UPDATE I've looked for days now and I've found many dead ends. This Multiple objects in a Rails form is close, but it requires that all the linking objects already exist.
What sets this question apart is that some of the links don't exist yet and there is no single model for the links to be nested in. E.g. With Ryan Daigle's Nested Object Forms post) I've made this work in a form to edit all possible Completions for a user, but I need to edit a subset of possible Completions in one form. Do I need to make a redundant object MilestoneCompletions that has_many
Completions and belongs_to
User? Can an ActiveModel has_many
?
I finally solved this. One key is the collection argument to fields_for. The other is to generate the collection with a mix of existing and new records.
So in the view, something like:
With a helper method:
Notice in the view that completions already in the DB are checked and disabled so they can't be unchecked. The unchecked state gets the value "unreported" and the User model can filter out those records so they don't go in the DB:
I also had to make
completions_attributes
attr_accessible on the User model. If you maketask_ids
accessible thenupdate
will delete completions that were left out of thePUT
.