Rails 4 drop down select list different model - ruby-on-rails

New to Rails so go easy on me :-)
I have 2 models: User and Role:
class User < ActiveRecord::Base
has_many :roles
accepts_nested_attributes_for :roles
validates_presence_of :role_id
end
class Role < ActiveRecord::Base
belongs_to :user
end
User has a role_id for the foreign key.
All I'm trying to do is be able to select a role for the user in the users/new form. I know it's easy, but I cannot seem to figure it out...I've literally read for hours today trying to figure it out. The drop down select list appears in the view, but it always fails validation (like it shows up, but never actually associates what the user selects with the User.role_id)
Here is what I have in my form partial to show the drop down:
<%= f.collection_select :role_id, Role.all, :id, :name %>
Can anyone point me in the right direction? Maybe I have to use some sort of nested forms, but nothing I have tried seems to work and this is what I currently have. Do I have to do something in my controller?

If User has many roles, your User model must not have a field: user_id, I think, and I hope, that Users Have and Belongs to many Roles. Then you need a third model:
class User < ActiveRecord::Base
has_many :user_roles
has_many :roles, through: :user_roles
end
class Role < ActiveRecord::Base
has_many :user_roles
has_many :users, through: :user_roles
end
class UserRole < ActiveRecord::Base
belongs_to :user
belongs_to :role
validates_presence_of :role_id, :user_id
end
In your User form you can use this to update relations (look: :role_ids in plural)
<%= f.collection_select :role_ids, Role.all, :id, :name, {}, {multiple: true} %>
And the validation is now in UserRole model.
Edit: If you are using Rails 4.x you need to permit params for a collection of role_ids.
params.require(:user).permit(:user_field1, :user_field2, ... , role_ids: [])

Related

How can Iist current user record on a select list in views in Ruby on rails

How can I list records that belong to the currently logged in user as a select list?
class User < ApplicationRecord
has_many :offices, dependent: :destroy
has_many :staffs, dependent: :destroy
end
class Office < ApplicationRecord
belongs_to :user
has_many :staffs, dependent: :destroy
end
class Staff < ApplicationRecord
belongs_to :user
belongs_to :user
end
I tried the code below but still will have all offices listed instead of the current user's created offices available for selection
<%= f.association :office, label_method: :office_name, value_method: :user, include_blank: false, prompt: "Assign Staff To Office" %>
My expectations are that only offices created by the current user will be listed in the select list item on the staff creation form
Try using a collection select like this
<%= f.collection_select(:office_id, current_user.offices, :id, :name) %>

How to implement a many-to-many in ActiveAdmin?

There a many-to-many:
class Employee < ActiveRecord::Base
has_many :employees_and_positions
has_many :employees_positions, through: :employees_and_positions
end
class EmployeesAndPosition < ActiveRecord::Base
belongs_to :employee
belongs_to :employees_position
end
class EmployeesPosition < ActiveRecord::Base
has_many :employees_and_positions
has_many :employees, through: :employees_and_positions
end
How to implement a choice (check_boxes) positions in the form when adding an employee?
I wrote this variant:
f.inputs 'Communications' do
f.input :employees_positions, as: :check_boxes
end
It displays a list of positions in the form, but does not save nothing to the table (employees_and_positions).
How to fix?
Suppose you have an employee, you can reference the ids of the employees_positions association by using employee.employees_position_ids. Accordingly, you can mass assign pre-existing EmployeesPosition objects using a check_box for each EmployeesPosition, but you need to use the employee_position_ids attribute"
= f.input :employee_position_ids, as: :check_boxes, collection: EmployeesPosition.all
Also, make sure you've whitelisted the employee_position_ids param in your active admin resource:
ActiveAdmin.register Employee do
permit_params employee_position_ids: []
end
http://activeadmin.info/docs/2-resource-customization.html

Rails: Simple Form Association label_method from Grandparent

I am trying to build a form that will create a new record using Simple Form, but I am having trouble showing the correct label in the dropdown list. First, the relevant models:
class Service < ActiveRecord::Base
belongs_to :business
has_one :enrollment
has_many :clients, through: :enrollment
end
class Client < ActiveRecord::Base
belongs_to :business
has_one :enrollment
has_many :services, through: :enrollment
end
class Enrollment < ActiveRecord::Base
belongs_to :service
belongs_to :client
has_many :jobs
end
class Job < ActiveRecord::Base
belongs_to :enrollment
end
The basic idea is that a client will be enrolled in one or more services. A job represents an appointment to perform the service. When creating a new job, I need to select the enrollment that the job belongs to. From the html.erb:
<%= f.association :enrollment, label_method: :service_id, value_method: :id, prompt: 'Choose an enrolled service' %>
This sort of works, but it only shows me the service_id from the Enrollment table. What I want to see is the name of the Client (fname and lname) and the name of the Service concatenated in the dropdown list, like this: "John Doe: Window Washing". The problem is that both of those come from the parents of Enrollments. Basically I need to traverse two associations to get to the label that I want.
I thought about de-normalizing so that the Enrollment record has the data I need, but I'd rather not do that.
Any ideas?
In the Enrollment class define following method:
def name
"#{client.full_name}: #{service.name}"
end
Then you should be able to use this method in your form:
<%= f.association :enrollment, label_method: :name, value_method: :id, prompt: 'Choose an enrolled service' %>
To avoid 2*n+1 sql queries prepare enrollments collection with included clients and services.

Drop-Down-Menu for Many-to-Many relation in rails using nested attributes

I have three tables via many-to-many-association: Supermarket, Product and Supply.
Each Supermarket can hold many products and each product can be sold in many supermarkets. The association is build via the Supply-model.
Supermarket:
class Supermarket < ActiveRecord::Base
attr_accessible :name, :address, :products_attributes
has_many :supplies
has_many :products, :through => :supplies
accepts_nested_attributes_for :products
end
Product:
class Product < ActiveRecord::Base
attr_accessible :name, :supermarkets_attributes
has_many :supplies
has_many :supermarkets, :through => :supplies
accepts_nested_attributes_for :supermarkets
end
Association via Supply:
class Supply < ActiveRecord::Base
attr_accessible :supermarket_id, :product_id
belongs_to :supermarket
belongs_to :product
end
I have created the scaffolds and populated the Supermarket-table.
In my Product form, i want to use one (or more) drop-down-menu(s) to select the correspondent Supermarket-name(s). Goal is to create a new product while also creating the association via the Supply-table.
What should the code look like in form and/or controller for the products if I want to select the corresponding supermarkets from there?
In you products form you need to add this line...
<%= collection_select(:product, :supermarket_ids, SuperMarket.all, :id, :name, {}, { :multiple => true } )%>
You also shouldn't need to use an accepts_nested_attributes for this, the many to many association you already have set up should take care of the rest.
I think in views
<%= f.collection_select "super_market_ids[]",#super_markets,:id,:name,{},{:multiple=>"multiple'} %>
I am not sure about super_market_ids or super_market_ids[] and syntax just verified once.
In select tag if you want checkbox type multi select there is an chosen library that will help you to build nicer UI,

How to use foreign key in model and form builder?

I have two models: User and Location as below:
class User < ActiveRecord::Base
attr_accessible :location, :password, :user_name, :password_confirmation
validates :location, :user_name, :presence => true
validates :password, :presence => true, :confirmation => true
has_one :location, :foreign_key => 'location'
end
class Location < ActiveRecord::Base
attr_accessible :loc_id, :loc_name
belongs_to :user, :foreign_key => 'loc_id'
end
You can see that I use the custom foreign_key for the models. I use form builder to build a user sign up form, but when I submit data the error occurs:
Location(#2170327880) expected, got String
I use simple_form to build the form, related code is:
= f.input :location, :collection => Location.all.collect {|c| [c.loc_name, c.loc_id]}
How can I resolve this problem? Or must I use the default foreign_key like location_id for the association?
Thanks.
Update:
When I rename the location field in User model to loc_id and remove the :foreign_key like this:
class User < ActiveRecord::Base
attr_accessible :loc_id, :password, :user_name, :password_confirmation
validates :loc_id, :user_name, :presence => true
validates :password, :presence => true, :confirmation => true
has_one :location, :foreign_key => 'location'
end
class Location < ActiveRecord::Base
attr_accessible :loc_id, :loc_name
belongs_to :user
end
It works fine. But I still want to know how to associate the User and Location model.
P.S. I use Location model to store the country code and country name, which will never update by User.
It sounds like you actually want to have
class User < ActiveRecord::Base
belongs_to :location
end
class Location < ActiveRecord::Base
has_many :users
end
This means that a user has a location_id column. If you do things the other way around (user_id column on location) then a given location can only be associated to one user. The rails way is that location_id on users 'points' at the id column in the locations table. If you want it to point at a different column, use the :primary_key option (The :foreign_key option would be if you wanted the column on users to be called something other than location_id)
In terms of the form, you can't do f.select :location - forms don't know how to transfer a complicated object like that. In these cases you want to set the form to control the location_id attribute, i.e.
= f.input :location_id, :collection => Location.all.collect {|c| [c.loc_name, c.id]}
If you go down the route of having the location id column refer to the loc_id column on location, then you'd need to change that to be
= f.input :location_id, :collection => Location.all.collect {|c| [c.loc_name, c.loc_id]}
Personally if you're only just starting out with rails I'd stick to the defaults
It looks like you're misusing foreign key. In the User model, you should have just has_one :location and the location model should have a user_id attribute. In the location model, you only need to write belongs_to :user. A foreign key is always an index into another (foreign) table.
class User < ActiveRecord::Base
# stuff
has_one :location
end
class Location < ActiveRecord::Base
# more stuff
belongs_to :user
end
If what you ultimately want to do is select all users with the same location, you might want to set the models up a little differently. Right now, because each user has its own location record in the location table, you would end up with duplicate locations, and you'd have to find all of these for a unique location and extract the user_ids from them.
Instead do this
class User < ActiveRecord::Base
has_one :placement
has_one :location, through: :placement
end
class Placement < ActiveRecord::Base
belongs_to :user
belongs_to :location
end
class Location < ActiveRecord::Base
has_many :placements
has_many :users, through: :placements
end
. As for the migrations, Placement should have a :user_id and :location_id. You can drop the :user_id that you currently have in Location. What this code says is that we have many users and we have many unique locations, and we place users in unique locations by creating placements, which indicate that a user with :user_id is located in location with :location_id. Also, don't forget to add a the line
add_index :placements, [:user_id, :location_id], unique: true
so that you can't place a user in a location more than once.
EDIT: Forgot to add: you can get all users in a location by simply getting the location record and calling location.users

Resources