0

I am currently creating a WebApp using RoR 4 and I am using has_many, though: associations between my databases.

I have 3 models Users, UsersSubject and Subjects given below:

class UsersSubject < ActiveRecord::Base
  belongs_to :users
  belongs_to :subjects
end

class Subject < ActiveRecord::Base
  has_many :users_subjects
  has_many :users, through: :users_subjects
end

class User < ActiveRecord::Base
  has_many :users_subjects, :class_name => 'UsersSubject'
  has_many :subjects, through: :users_subjects

  accepts_nested_attributes_for :subjects
end

I am trying to populate the UsersSubject database when I am updating the User using the User controller. Here is my from the partial form of the User:

<div class="control-group nested-fields">
  <div class="contols">
    <%= f.fields_for :subject do |subject| %>
      <%= subject.label "Subjects" %></br>
      <%= subject.collection_select(:subject_id, Subject.all, :id, :name) %>
    <% end %>
  </div>
</div>

and here is my controller:

def edit
  user_id = current_user.id
  subject_id = Subject.where(:name => params[:name])
  @user_sub = UsersSubject.new(user_id: user_id, subject_id: subject_id)
  @user_sub.save
end

When I do this the controller populate the UsersSubject database with the correct user_id but the subject_id is always nil. The Subject database is already populate using the seed.rb file. Can someone help me understand why this is happening and help my fix it?

Thanks in advance

EDIT

Here is my development.log

Started GET "/users/edit" for ::1 at 2016-05-31 18:27:33 +0100
Processing by Users::RegistrationsController#edit as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"ZGvBq1uFUi1RxcInKFz1TnUs2ZzlZsP29aW1mzQBOVBTLm/Dq3C42cQQX0ksmBv95/qHnk08bG3f5u1v9taZgw==", "user"=>{"address"=>"", "city"=>"London", "postcode"=>"", "country"=>"United Kingdom", "subject"=>{"subject_id"=>"1"}}, "commit"=>"Update"}    

Moved controller code to the update action

def update
  user_id = current_user.id
  subject_id = params[:user][:subject][:subject_id] unless params[:user].nil?
  @user_sub = UsersSubject.new(user_id: user_id, subject_id: subject_id)
  @user_sub.save
end

Added the subject permission in the application_controller.rb:

class ApplicationController < ActionController::Base

  before_filter :configure_permitted_parameters, if: :devise_controller?
  protected

  def configure_permitted_parameters
    devise_parameter_sanitizer.for(:account_update) { |u| u.permit(:last_name,
      :first_name, :email, :password, :current_password, 
      subject_attributes: [:id, :subject_id]) }
  end
end

This permits the update action to access the subject_attributes

Changes in the user model

class User < ActiveRecord::Base
  has_many :users_subjects, :class_name => 'UsersSubject', dependent: :destroy
  has_many :subjects, through: :users_subjects

  accepts_nested_attributes_for :subjects
end

2 Answers2

0

Have you looked into using the cocoon gem?

You might also want to take a look at the params you're passing into subject_id, it looks like the full object that you're grabbing from it's name. Print out the "subject_id" that you currently have.

There is a great railscasts video on this topic you might want to look into, the membership is well worth it.

Juan Pablo Ugas
  • 1,085
  • 9
  • 22
  • I am already using the cocoon gem. I understand the fact that I might be passing the full object as an argument. I am new at using RoR, so I am not sure how to print out the "subject_id". – Greg Yharra May 31 '16 at 15:43
  • So the way cocoon works is a little like magic, you have an edit function that you don't really need since cocoon is already saving all of this stuff. In your controller private params you should have something like userssubject_attributes: [:id, :subject_id] – Juan Pablo Ugas May 31 '16 at 16:03
  • Also, you should read this post here on your relationships as you shouldn't need the :through relationship [link](http://stackoverflow.com/questions/11600928/when-should-one-use-a-has-many-through-relation-in-rails) – Juan Pablo Ugas May 31 '16 at 16:18
  • Your Users model nested attr should be written this `accepts_nested_attributes_for :UsersSubject, reject_if: :all_blank, allow_destroy: true` – Juan Pablo Ugas May 31 '16 at 16:22
  • I do need the `:through` relationship, because I want it to be a many_to_many relationship where each users can have multiple subject and each subject can have multiple users – Greg Yharra May 31 '16 at 16:27
  • Your collection select may be causing you the trouble, try changing it like this so the actual subject id is out of the parenthesis `<%= subject.collection_select :subject_id, Subject.order(:name), :id, :name %>` – Juan Pablo Ugas May 31 '16 at 16:29
  • I tried changing the `collection_select` but it didn't fix the problem – Greg Yharra May 31 '16 at 16:40
  • Have you got it working yet? Did you add the Userssubject params as mentioned into your users controller? – Juan Pablo Ugas Jun 01 '16 at 20:42
  • Your collection select will access the subject attributes, you don't need anything in your controller for that. Your current_user.id should be global so you don't actually need it in your controller, you can set that as a value to a hidden form field. If the user is adding their own subjects, then you don't even need a nested field. If an admin is adding subjects to a user then that could use a nested field. But if I am a user, I can just use a form from the Userssubject and save it directly in there but make sure that both fields have been created with user:references and subject:references. – Juan Pablo Ugas Jun 01 '16 at 20:49
  • Yes I got it working, please look at the `edit` section to see how I got it working, and I wanted to thank you for your help, I appreciate it – Greg Yharra Jun 01 '16 at 20:50
0

Check your params in your rails server log. From what I guess it should be coming like this

"user" => {
  "name"=>"Michael Jackson",
  "subject" => {
    "subject_id"=>"1"
  }
}

But in your controller method, you're trying to access it like params[:name], instead you should do something like this, based on how you've nested in your form.

subject_id = params[:user][:subject][:subject_id]

subject_id is nested inside subject, which is nested inside the user object.

Hope this helps. :)

Reference code:

Do this to your edit action.

def edit
  p params #this print out the inspected params in the console log
  fail
  #this stops the code from executing further, by raising an error.
  #So we can inspect the params passed to our method in server log
  ...
end

Go to your form in your browser, and after that clear your rails server console, (Command + K) for mac, and For ubuntu, press keep pressing enter to move the current log up the screen. Now click submit, and check the server log. You'll notice something like

Started POST "/users"
Processing by UsersController#edit
Parameters: {"utf8"=>..... "user" => { "name" => "Michael Jackson", "subject" => { "subject_id" => "1" }}}

So what you want to do is, you need to create a new many to many relation between User and Subject. You're getting the user_id from devise's current_user and subject_id in your edit method from the params, as I defined. How you're receiving params might differ from how I mentioned.

Final code

def edit
  user_id = current_user.id
  subject_id = params[:user][:subject][:subject_id]
  @user_sub = UsersSubject.new(user_id: user_id, subject_id: subject_id)
  @user_sub.save
end
Community
  • 1
  • 1
Kumar
  • 3,116
  • 2
  • 16
  • 24
  • Moreover if your going according to convention, the controller code you've written should go inside `update` action. By convention edit only renders your form. Just wanted to point out, in case you've done otherwise. :) – Kumar May 31 '16 at 15:27
  • Thanks for you answer. I am currently using Devise for the User, and I have the `edit.html.erb` to store my form, that's why I put the controller code inside the `edit` actions, should I still move it to the `update` actions? Secondly, I don't understand the nested part of your answer, because according to the guide [link](http://guides.rubyonrails.org/association_basics.html#the-has-many-through-association) and scaling it to my case there is no connection between `Users` and `Subjects`, well the only way to connect the two is to go through the `UsersSubjects` table, could you explain more? – Greg Yharra May 31 '16 at 16:09
  • You don't need to pick it up from `development.log`. You can do see it where you ran `rails server` in your console. Just do one more thing before the line where you put fail. Write `p params`. I've updated my code. – Kumar May 31 '16 at 17:49
  • I hope you can see the similarity in the parameters that was passed. – Kumar May 31 '16 at 18:17
  • Please use the final code. Make sure to remove `fail` from your code. :) – Kumar May 31 '16 at 18:25
  • I have updated my log, and now I do understand the `subject_id = params[:user][:subject][:subject_id]` but even if I do that, I still get `subject_id: nil` in the `UsersSubject` database – Greg Yharra May 31 '16 at 18:38
  • Just like how you did `p params`, after doing `@user_sub.save`, write `p @user_sub` in the next line. You'll see the object in your rails server log. It should contain `subject_id`. And did you follow the updated code, i wrote? – Kumar May 31 '16 at 18:41
  • I did follow your code, I added ` unless params[:user].nil?` otherwise I get an error and this is what I get `#` but it seems like `suject` is an `Unpermitted parameter` – Greg Yharra May 31 '16 at 19:18
  • I fixed it. Thank you very much for your help, I appreciate it! :) – Greg Yharra May 31 '16 at 20:57
  • Glad it worked out. Would you mention what was wrong? – Kumar Jun 01 '16 at 04:06
  • I added the `subject_attributes` section to the `application_controller` to add the permission, please look at the edit section of the post – Greg Yharra Jun 01 '16 at 10:58