many to many relationship crud - ruby-on-rails

I have users and each user has addresses and each address belong to a case
how can i add the result of case new to show in case show
in case new
<%= form_for #case,:url => {:action => :create, :id => #user.id} do |f| %>
Address:<br>
<%= f.select :address_ids, options_for_select(#user.addresses.all.pluck(:name,:id)) %><br><br>
<% end %>
in case show (this gives me all the addresses for the user not the address that I choose from case new.html.erb
<strong>Address: </strong><% #user.addresses.each do |s| %>
<%= s.name %>
<% end %>
case model
class Case < ActiveRecord::Base
belongs_to :user
has_many :addresses, dependent: :destroy
in user model
class User < ActiveRecord::Base
has_many :cases
has_many :addresses, dependent: :destroy
in address model
class Address < ActiveRecord::Base
belongs_to :user
belongs_to :case
end

It retrieves all the addresses because you query the address from the user himself not from the case object that you supposedly created from the new view.
So, first, I would add create action to create the case first (to which we will assign the chosen address):
def create
# #user can be retrieved from the passed id in the url
#user = User.last
#case = Case.create(user: #user)
#case.addresses << Address.where(id: params.require(:case)[:address_ids])
end
and change your controller show action to something like this:
def show
# #case can be retrieved any way you like, this is just an example
#case = Case.last
end
and make your show view like this:
<strong>Address: </strong>
<% #case.addresses.each do |s| %>
<%= s.name %>
<% end %>
This would solve your issue.
Some notes to consider later:
So far this new view only allows you to choose one address (not multiple) so it won't be needed to use has_many association (if you want to use it, then you have to figure out a way to choose multiple addresses).
The #user.id can be passed through the url instead of explicitly defining it in the form. Look at Nested Resources for more info.
The url attribute in the form can be replaced by _path helpers, the same above link would give you a hint as well.
Try to stick to Ruby styling, like indentation and naming. For example, the |s| is not descriptive .. would be better to be named address or addr
As I can see, you can try using through instead of duplicating the associations. E.g. Case belongs_to User & Case has_many Addresses, so User can has_many cases & User has_many addresses, through: :cases .. like this you can retrieve user's addresses specific to that case. Like this, when you try to create an address and retrieve it from #user.addresses, you will get an error because an address already needs a user and a case to be created in the first place, so the associations are not set perfectly and as a result you will need workarounds to prevent these errors.

Related

undefined method `concert_id' for #<FollowUp::ActiveRecord_Associations_CollectionProxy:0x00007f8d481b3dc0> Did you mean? concat

I have a model of follow_ups and volunteers as:
class FollowUp < ApplicationRecord
belongs_to :volunteer
belongs_to :member
belongs_to :concert
end
class Volunteer < ApplicationRecord
enum type: [:admin, :regular]
has_many :follow_ups, dependent: :delete_all
has_many :members, through: :follow_ups
end
Now I wanted to print follow_ups by all volunteers.
It was working fine when I tried in rails console i.e Volunteer.first.follow_ups
I want to show those value in the form, what I tried is:
Volunteer.all.each do |volunteer|
volunteer.follow_ups.concert_id
end
The has_many relation denotes a one-to-many association. Instead of returning a single object, it returns a collection of follow_ups.
That said, you can't do volunteer.follow_ups.concert_id, because follow_ups is an Active Record collection. Make an iteration instead:
volunteer.follow_ups.each { |follow_up| puts follow_up.concert_id }
The Ruby on Rails documentation has great content about Active Record Associations.
To collect such information you should use:
volunteer.follow_ups.pluck(:concert_id)
Edit:
It's very important to note that using pluck is more efficient than using iterators like map and each due to saving server RAM and request time. Then you can print to rails logger:
volunteer.follow_ups.pluck(:concert_id).each{|ci| Rails.logger.info ci}
Edit2
Referring to your text
I want to show those value in the form
If I understand you, you want to show concert_id of each follow_up in the volunteer form. in this case you should add
accepts_nested_attributes_for :follow_ups in your volunteer.rb
then:
<%= form_for #volunteer do |f| %>
<%= f.fields_for :follow_ups do |form_builder| %>
<%= label_tag "custom_label", "follow up id : #{form_builder.object.id}, concert_id : #{form_builder.object.concert_id}%>
<% end %>
<% end %>
The fields_for helper will iterate through all follow_ups , then you can get the object for each follow_up using object which allow you to deal with object directly and get your concert_id attribute from it.

Fetch id of selected option in dropdown in controller

I have a model named 'Assessment':
class Assessment < ApplicationRecord
has_many :assessment_students
has_many :students, through: :assessment_students
end
Join table is:
class AssessmentStudent < ApplicationRecord
belongs_to :student
belongs_to :assessment
end
There is another model:
class Classroom < ApplicationRecord
has_many :classroom_students
has_many :students, through: :classroom_students
has_many :assessments
end
In show,html.erb of classrooms, I have a dropdown which shows all assessments (generated from assessment table).
Code is:
<%= collection_select(:assessment :assessment_id, Assessment.all, :id, :assessment_name , :prompt => true) %>
Requirement of the project is: Based on the assessment chosen by the user in the show.html.erb page, we have to show all students details like name etc assigned to that particular assessment. I have stored this data in join table 'AssessmentStudent '. However, I am not sure how to pass id from the above collection_select to classroom controller. I have below code:
show.html.erb:
<%= collection_select(:assessment :assessment_id, Assessment.all, :id, :assessment_name , :prompt => true) %>
<div id="divResult">
<% #assessmentstudents1.each do |t| %>
<% t.assessment_students.each do |record| %>
<%= record.student_id %>
<% end %>
<% end %>
</div>
classroom controller:
def show
#assessmentstudents1 = Assessment.find(params[:assessment][:assessment_id]).preload(:assessment_students)
end
def classroom_params
params.require(:classroom).permit(:classroom_name, :classroom_year, :customer_id, :classroom_student, :student_ids => [])
params.require(:assessment).permit(:assessment_id)
end
I would first recommend to make a change to the controller structure of your application. Because the responsibility of the show action of your ClassroomsController should be to display the details of the classroom. When you want to show the details of an Assessment, that should be handled by an AssessmentsController.
First of all, I'm gonna assume that your Assessment model has a belongs_to :classroom association. Then I would suggest creating the following structure.
config/routes.rb
resources :classrooms
resources :assessments
app/views/classrooms/show.html.erb
<% #classroom.assessments.each do |assessment| %>
<%= link_to assessment_name, assessment_path(assessment) %>
<% end %>
app/controllers/assessments_controller.rb
class AssessmentsController < ApplicationController
def show
#assessment = Assessment.find(:id)
end
end
app/views/assessments/show.html.erb
<h1>Students for <%= #assessment.assessment_name %></h1>
<% #assessment.students.each do |student| %>
<p><%= student.student_name %></p>
<% end %>
<h2>In classroom:</h2>
<p><%= #assessment.classroom.classroom_name %></p>
So to explain what is happening here, we have configured the routes to allow the server to respond to the url /assessments/:id which will lead to the AssessmentsController#show action being called.
That action is being called when the user clicks on any of the links that we have setup in the classrooms/show template. Note that I used individual links for now instead of a select dropdown, because it is easier to setup and understand how it works. And like the previous answer suggested, using a select tag requires a little bit of JavaScript to get working.
And lastly, when the assessments/show template is being rendered, it will list out all of the students related to that particular assignment. And I also included which classroom it is assigned to (if my assumption of belongs_to was correct).
On a side note, if your question tag of ruby-on-rails-3 is correct, then the usage of params.permit and params.require is invalid, because that is something that was introduced in Rails 4 (unless I'm mistaken). And in any case, you only have to use permit when database updates take place, which means the create and update actions, not index, show, etc, because it is a way of restricting which changes are allowed.
Tag select does nothing by itself. So there are two possible options.
The first is wrapping select in form tag then submitting the form will lead to request. The second is writing a handler using JavaScript which will listen to select changes.

Access current id for a has_one relationship in rails

I have two models in my app. One is called meetings and the other is outcome. I wanted to create the outcome of each meeting using: #outcome=current_meeting.outcome.build(params[:outcome]). Further, each meeting would have only one outcome so it is clearly a has_one relationship. I am really confused about getting the current_meeting. I have the meetings and outcomes models as:
Meeting Model
class Meeting < ActiveRecord::Base
attr_accessible :info, :meeting_date, :name, :venue
has_many :participants, :dependent => :destroy
has_one :outcome, :dependent => :destroy
validates_presence_of :name, :info, :meeting_date, :venue
end
Outcome Model
class Outcome < ActiveRecord::Base
attr_accessible :result
belongs_to :meeting
validates :meeting_id, presence: true
end
I want the new outcome to be present inside the show of the meeting, if there are none present and if there is one present creating new outcome should be made impossible
meetings/show.html.erb
<% if #meeting.outcomes.any? %>
<%= render #outcomes %>
<% else %>
<%= link_to "Add the outcome of meeting", new_outcome_path %>
<% end %>
My controllers are:
Meetings controller
def show
#meeting = Meeting.find(params[:id])
#outcomes = #meeting.outcomes.all
end
Outcomes controller
def new
#outcome = current_meeting.microposts.new
end
def create
#outcome = current_meeting.outcomes.build(params[:outcome])
if #outcome.save
flash[:success] = "Outcome created!"
redirect_to root_url
else
render 'static_pages/home'
end
end
I don't know how to find out the current_meeting. Please help.
First of all, the question is very confusing as to the plurality of outcome vs outcomes. If a Meeting has_one Outcome then you you would use the singular form when referring to the reference. Basically, given has_one :outcome, ":outcome" is the method name to be used. So you'd say meeting.outcome instead of meeting.outcomes. And the build method for has_one would be like meeting.build_outcome instead of meeting.outcomes.build. The latter is the api for a has_many relationship.
With that out of the way, if you want to get the current Meeting from the Outcomes controller, the best way to do this is with nested resources. So in the routes file you'd have, e.g.:
resources :meetings do
resources :outcomes
end
After you do that, run rake routes to see the routes available to you. In there you'll see an expected url format of POST /meetings/:id/outcomes which you would use here. So in this case, the create method would get the Meeting object from params[:id], from which the outcome relationship can be created.
At first glance it does not seem like you are defining current_meeting anywhere. You probably already know this and if so the case would be that you are unsure of how/where to define it. You will probably need to do that somewhere in the code. This could mean saying something like this meeting is current because it is during the current time and/or today. This is based on how your app works to determine this logic.
In your controller or in a helper you will need to write a method that gives you the current meeting if one exists. From there the current_meeting variable in your controller will be set correctly and should call your other methods right.
If I have misunderstood the issue I apologize and please provide any other details you can and I can try to help.

Update join model in has_many through association, using the two other models

I've read through many other topics here (1, 2, 3...) but none really solved my problem.
Here are my 3 models.
User
has_many :memberships
has_many :accounts, :through => :memberships
accepts_nested_attributes_for :memberships
end
Account
has_many :memberships
has_many :users, :through => :memberships
accepts_nested_attributes_for :memberships
end
Membership
attr_accessible :account_id, :url, :user_id
belongs_to :account
belongs_to :user
end
As you can see, my join model Membership has an additional attribute: :url.
In my Accounts table, I store names of online services, such as GitHub, Stack Overflow, Twitter, Facebook, LinkedIn.. I have 9 in total. It's a fixed amount of accounts that I don't tend to update very often.
In my User form, I'd like to create this:
The value entered in any of these field should be submitted in the Memberships table only, using 3 values:
url (the value entered in the text field)
user_id (the id of the current user form)
account_id (the id of the related account, e.g. LinkedIn is '5')
I have tried 3 options. They all work but only partially.
Option #1
<% for account in #accounts %>
<%= f.fields_for :memberships do |m| %>
<div class="field">
<%= m.label account.name %><br>
<%= m.text_field :url %>
</div>
<% end %>
<% end %>
I want to have 9 text field, one for each account. So I loop through my accounts, and create a url field related to my memberships model.
It shows my fields correctly on the first time, but the next time it'll display 81 fields:
Option #2
<% #accounts.each do |account| %>
<p>
<%= label_tag(account.name) %><br>
<%= text_field_tag("user[memberships_attributes][][url]") %>
<%= hidden_field_tag("user[memberships_attributes][][account_id]", account.id) %>
<%= hidden_field_tag("user[memberships_attributes][][user_id]", #user.id) %>
</p>
<% end %>
I'm trying to manually enter the 3 values in each column of my Memberships tables.
It works but :
displaying both account and user id's doesn't seem very secure (no?)
it will reset the fields everytime I edit my user
it will duplicate the values on each submit
Option #3 (best one yet)
<%= f.fields_for :memberships do |m| %>
<div class="field">
<%= m.label m.object.account.name %><br>
<%= m.text_field :url %>
</div>
<% end %>
I'm creating a nested form in my User form, for my Membership model.
It works almost perfectly:
exactly 9 fields, one for each account
no duplicates
But, it only works if my Memberships table is already populated! (Using Option #2 for example).
So I tried building some instances using the UsersController:
if (#user.memberships.empty?)
#user.memberships.build
end
But I still get this error for my m.label m.object.account.name line.
undefined method `name' for nil:NilClass
Anyway, I'm probably missing something here about has_many through models. I've managed to create has_and_belongs_to_many associations but here, I want to work on that join model (Membership), through the first model (User), using information about the third model (Account).
I'd appreciate your help. Thank you.
in the controller, fetch the list of memberships for a particular user
# controller
# no need to make this an instance variable since you're using fields_for in the view
# and we're building additional memberships later
memberships = #user.memberships
then loop through each account and build a membership if the user has no membership for an account yet.
# still in the controller
Account.find_each do |account|
unless memberships.detect { |m| m.account_id == account.id }
#user.memberships.build account_id: account.id
end
end
then in your view, you change nothing :)
I would use the following data-design approach. All users in your system should have the
memebership entries for all possible accounts. The active configurations will have a value for the url field.
User
has_many :memberships
has_many :accounts, :through => :memberships
has_many :active_accounts, :through => :memberships,
:source => :account, :conditions => "memberships.url IS NOT NULL"
accepts_nested_attributes_for :memberships
end
Now
curent_user.active_accounts # will return the accounts with configuration
curent_user.accounts # will return all possible accounts
Add a before_filter to initialize all the memberships that a user can have.
class UsersController
before_filter :initialize_memberships, :only => [:new, :edit]
private
def initialize_memberships
accounts = if #user.accounts.present?
Account.where("id NOT IN (?)", #user.account_ids)
else
Account.scoped
end
accounts.each do |account|
#user.memberships.build(:account_id => account.id)
end
end
end
In this scenario you need to initialize the memeberships before the new action and all the memberships should
be saved in the create action ( even the ones without url).
Your edit action doesn't need to perform any additional data massaging.
Note:
I am suggesting this approach as it makes the management of the form/data straight forward. It should only
be used if the number of Account's being associated is handful.

Checkboxes are getting but not putting many-to-many roles

I'm trying to set up a Rails 3 app to handle user roles with Devise and CanCan.
My relationships are as follows
class User < ActiveRecord::Base
has_many :users_roles
has_many :roles, :through => :users_roles
end
class Role < ActiveRecord::Base
has_many :users_roles
has_many :users, :through => :users_roles
end
class UsersRole < ActiveRecord::Base
belongs_to :user
belongs_to :role
end
Actually everything is working fine. Devise is handling the authentication perfectly. CanCan is restricting user behaviour based on ability.rb.
But I have a ridiculous problem with setting a UsersRole.
I have defined checkboxes on the User edit page like so.
<% for role in Role.all %>
<%= check_box_tag "user[role_ids][]", role.id, #user.roles.include?(role) %>
<%=h role.name.camelize %>
<% end %>
<%= hidden_field_tag "user[role_ids][]", "" %>
If I create a UserRole via the console, then these checkboxes are checked according to the users role.
But I cannot set or change roles using these checkboxes!
I've been all around the houses with this — variations of syntax, switched to a HABTM and roles_mask approach, rebuilt my models and controllers several times — all to no effect.
Actually the title of my question is not entirely correct - the checkboxes are putting
_method put
authenticity_token XGl6s1iyXJfahdgftc3df8q1ZeehMVzs3LxiQH98jGw=
commit Update
user[current_password] password
user[email] user#example.com
user[name] User Name
user[password]
user[password_confirmatio...
user[role_ids][] 1
user[role_ids][] 4
user[role_ids][]
utf8 ✓
But these values are not being set in the database.
What am I doing wrong??!!!
My guess is that you have specified attr_accesible in your User model and that role_ids is not listed there (and it should not be)
If that is combined with an update_attributes call in your Controller then role_ids will never be set properly.
If this is the case then you should manually be able to set the role_ids in your Controller like this before you update or save:
#user.role_ids = params[:user][:role_ids]
Of course, I'm not certain this is the case since you did not include your controller code or any details with the User model.
Edit:
Instead if doing the #user.update_attributes in the Controller, you could change it to the following instead:
#The same as update_attributes, but without the save
#user.attributes = params[:user]
# Here the role_ids get assigned manually so it does not need to be in attr_accesible
#user.role_ids = params[:user][:role_ids] if params[:user]
# And here it gets checked if the save was successfull
if #user.save
render :action => :show
else
render :action => :edit
end

Resources