I'm building a training website where I have two models, User and Course, that are associated with a third model, CourseCompletions. The third model is for keeping track of which user has completed which courses and vice versa. The first two models have controllers whereas the third one does not.

I implemented the functionality for completing a course and it works (clicking the "complete course" button on the course page inserts the appropriate row into the course_completion table if the user has not completed that course before), but I'm unsure about how robust and secure my implementation is. This is in Course_Controller.rb:

helper methods omitted for brevity

def complete_course
    @course = current_course
    @user = current_user
    if !already_completed
        @course.course_completions.create(user_id: @user.id, course_id: @course.id, completion_date: Time.now)
        flash[:success] = "Congratulations! Your progress has been saved."
        redirect_to course_path
    else
        flash[:success] = "Looks like you have already completed this course before, but mad props for reviewing it!"
        redirect_to course_path
    end
end

My questions are as follows:

  1. Should I be calling create like I am doing, or is build (or create!) a better option?
  2. Should I be using strong parameters inside that function? If so, how do I do that in this particular case?

Thank you in advance.

有帮助吗?

解决方案

Change this line :

 @course.course_completions.create(user_id: @user.id, course_id: @course.id, completion_date: Time.now)

to

 Course_completion.create(user_id: @user.id, course_id: @course.id, completion_date: Time.now)

or to

  Course_completion.create(user: @user, course: @course, completion_date: Time.now)

or to

@course.course_completions.build(user: @user, completion_date: Time.now) # you can also use new instead of build, but build is preferred in this case

build is just an alias for new, but still the preferred way if you create an object through a collection like : @course.course_completions.build..

create is equivalent to .new + .save, it allow you save an object at one time.

create! is equivalent to .new + .save!, it's the same as create the only difference is that it throws an exception if the object won't save.

you don't need in this case to use strong parameters, strong parameters are important with forms to prevent the user updating non permitted fields, imagine you have a form with these fields : name, email, password, in this case a malicious user can add a field dynamically to your form using firebug for example let say admin, then he set the field to true, this is a security problem if you have a field called admin in your database, so to prevent the user to set this field (which normally is not in your form) we use strong parameters to specify only the fields we allow the user to update.

update :

to answer your comment about the difference between the 3 code parts above :

there is no difference between them, but if you want the two first are the same, either you write user_id: @user.id or user: @user, Rails is smart to understand that you want to set the foreign key which is user_id.

the third is just a different syntax or variation, instead of create a new object from the model Course_completion then insert the user_id and course_id like we did in the two first example, you have just to create a new object based on a collection, i mean by collection your "course_completions", so because your @course has_many course_completions (you can say that @course has a collection called course_completions)

to add a new object to your course_completions "collection" you have just to write @course.course_completions.build, then you pass the user_id and completion_date values to it, but what about course_id value ? the answer is that your collection is already based on @course (@course.course_completions) so you don't need to set the course_id, Rails know about it.

Hope this help

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top