RoR: Mark an object complete, save completed_on in

2019-08-02 13:53发布

问题:

I am creating an action whereby a user can mark homework as complete.

Homework is stored mostly in homework table but homework_student holds the completed_on and a boolean complete attribute:

homework_student.rb

 id             :integer          not null, primary key
    #  school_user_id :integer          not null
    #  homework_id    :integer          not null
    #  completed_on   :datetime
    #  created_at     :datetime         not null
    #  updated_at     :datetime         not null
    #  complete       :boolean

belongs_to :homework, :class_name => 'Homework', :foreign_key => :homework_id, dependent: :destroy

homework.rb

 has_many :homework_students, :class_name => 'HomeworkStudent', dependent: :destroy

My attempt...

In my show view for homework I want the user to be able to click complete on their homework and to have the date stored in the completed_on attribute in homework_students.

Routes:

 resources :homeworks do 
    resources :homework_students do
    member do
      patch :complete
    end
  end
end

homeworks_controller.rb:

def complete
        @complete_item = Homework.homework_students.update_attribute(:completed_on, Time.now)
    end

In my view:

<% @homework.homework_students.each do |homework_student| %> 
          <%= link_to "complete", complete_homework_path(:homework_id => @homework, :home_work_students_id => homework_student), method: :patch %> 
          <% end %>

I am trying a PATCH method but it is not working. I'm not sure whether it is better to use the boolean attribute for this but i'm not sure how. Homework has many users and each user updates whether they completed or not.

Error:

undefined local variable or method `homework

Much appreciate any guidance.

UPDATE: homeworks_controller.rb

        before_action :find_homework, only: [:show, :complete, :edit, :update]
                    #before_action :set_homework, only: [:show]
                    #before_action :set_homework_student, except: [:create]
                    before_action :authenticate_user!

            ....
    def complete
        # Loop over each matching homework_student for the @homework where the id matches and update the completed_on attribute.
        # You need to get the indivual homework_student record and update it. You could do it other ways but this seemed to work for me.
        @homework.homework_students.where(id: params[:home_work_students_id]).each do |homework_student| homework_students.update_attributes(:completed_on => Time.now)
        end
        # Redirect back to the @homework view.
        redirect_to @homework, notice: 'Homework was successfully marked as complete.'        
      end
            ....

          private

                def set_homework_student
                    @homework_students =    HomeworkStudent.find(params[:homework_id])
                end

                def set_homework
                  @homework = @homework_students.homeworks.find(params[:id])
                end


                def homework_params
                    params.require(:homework).permit(:user_id, :id, :school_user_id, :homeworks, :significance, :significance_id, :sig_option, :feedback_request, :subject, :source, :description, :due, :completed_at, homework_students: [:completed_on, :complete], school_user: [:f_name, :s_name])
                end



def find_homework
            @homework = Homework.find(params[:id])
        end
end

UPDATED ERROR:

Error message:

undefined method `update_attributes' for nil:NilClass

Rails.root: 

Application Trace | Framework Trace | Full Trace
app/controllers/homeworks_controller.rb:39:in `block in complete'
app/controllers/homeworks_controller.rb:39:in `complete'
Request

Parameters:

{"_method"=>"patch",
 "authenticity_token"=>"OL1vW0BnnJ8TM5lshZWNKisNMrTU2Gp2cY7jYf3T+NRhlx2994q0ndrpehz+vW5unY1EBtSKCHecOJjTDwLHsw==",
 "home_work_students_id"=>"142",
 "homework_id"=>"78",
 "id"=>"78"}

VIEW Update

 <% @homework.homework_students.each do |homework_student| %>
              <%= link_to "complete", complete_homework_path(@homework, homework_student), method: :patch %> 
              <% end %>

回答1:

I mocked this version up based on our comments and it works on my end although it may not be the best approach. You may want to watch this video from Mackenzie Child who uses ajax to make this type of update on his todo app (mark things as complete, etc).

In your routes.rb

resources :homeworks do 
    member do
      patch :complete
    end
    resources :homework_students
end

In your homework_controller.rb

 def complete
    # Loop over each matching homework_student for the @homework where the id matches and update the completed_on attribute.
    # You need to get the indivual homework_student record and update it. You could do it other ways but this seemed to work for me.
    @homework.homework_students.where(id: params[:home_work_students_id]).each do |homework_student|
      homework_student.update_attributes(:completed_on => Time.now)
    end
    # Redirect back to the @homework view.
    redirect_to @homework, notice: 'Homework was successfully marked as complete.'        
  end

before_action :set_homework, only: [:show, :complete]

In your homework show.html.erb

# This assumes you are looping over the @homework.homework_students in your view to create a link for each homework_student record and that will give you access to the homework_student local variable.
<%= link_to "complete", complete_homework_path(:homework_id => @homework, :home_work_students_id => homework_student), method: :patch %>