accessing data in many to many relations - ruby-on-rails

I created many to many association of Teacher and Sclass. ClassTeacher is the join table name.
class Teacher < ActiveRecord::Base
has_many :class_teachers
has_many :Sclasses, :through => :class_teachers
attr_accessible :teacher_id
attr_accessible :tname
attr_accessible :mob
attr_accessible :email
end
class Sclass < ActiveRecord::Base
set_primary_key :year
has_many :class_teachers
has_many :teachers, :through => :class_teachers
attr_accessible :year
end
class ClassTeacher < ActiveRecord::Base
belongs_to :teacher
belongs_to :sclass
attr_accessible :year
attr_accessible :teacher_id
end
I have tlist.html.erb, tnew.html.erb in view -> teachers folder, similarly I have slist.html.erb, snew.html.erb in view -> sclasses folder.
When I add a record for Teacher, it should ask for years too and save the record. Similarly for year it should ask for teacher_ids and save the record. How can I do this?Where will these records be saved?

I'm not sure this answers your question but when you set up a many-to-many relationship your model gets some 'virtual' attributes, called [association_name]_ids. So in your case the Sclass model will have a teacher_ids attribute, and the Teacher model will have sclass_ids.
So if for example you wanted to assign teachers with ids 1, 2 and 3 to the first sclass you could do this:
sclass = Sclass.first
sclass.teacher_ids = [1, 2, 3]
sclass.save
To do this in a form you'd probably want something like a row of checkboxes with the name sclass[teacher_ids][] (when creating/editing an Sclass), something like this:
<% for teacher in Teacher.all -%>
<%= checkbox_tag "sclass[teacher_ids][]", teacher.id %>
<% end -%>

Related

How to work with rich join tables in Rails

I am stuck struggling with rich join tables in Rails 5 and need help getting on track. The app I'm writing will help me track which of our company's suppliers carry which brands of products. Because I also need to track whether each supplier is authorized or unauthorized for each of the brands they sell, and whether they carry those brands in stock, I thought the best approach was to use a join table and store the attributes there. In other words:
Suppliers <---> Lines <---> Brands
Beyond the foreign key references for a Supplier and a Brand, the Line record also has two boolean attributes: .is_authorized and .carries_stock.
My models:
/models/supplier.rb
class Supplier < ApplicationRecord
has_many :lines, :dependent => :destroy
has_many :brands, :through => :lines
accepts_nested_attributes_for :lines
end
/models/brand.rb
class Brand < ApplicationRecord
has_many :lines, :dependent => :destroy
has_many :suppliers, :through => :lines
end
/models/line.rb
class Line < ApplicationRecord
belongs_to :supplier
belongs_to :brand
validates_presence_of :supplier
validates_presence_of :brand
end
I've been able to set up the controller and supplier edit form to allow creating records in the Lines table, but have no clue how to allow the users to edit the .is_authorized and .carries_stock attributes. I have been able to get the create/edit supplier form to work by adding the following snippet:
/views/suppliers/_form.html.erb
<h4>Brands</h4>
<%= form.collection_check_boxes(:brand_ids, Brand.all, :id, :name) do |b| %>
<%= b.label class:"label-checkbox" do%>
<%= b.check_box + b.text%>
<%end%>
<br />
<% end %>
The form looks like this now but doesn't allow me to edit the rich attributes .is_authorized and .carries_stock. I'd like the form to look something more like this. Where do I go from here?
Thanks!!!

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 4 drop down select list different model

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: [])

Rails 3 has_many :through concept

I need some conceptual help:
Assume your Users are essentially a business. You have Employees and you have Staff Positions. Essentially, one employee could hold multiple positions and one position could hold multiple employees.
My have_many :through is working between the Employees and the Positions through a join table Staffization. However, my edit form for the employee is returning ALL the Positions as checkboxes for the whole app, not just the ones for this particular User. And, none are being saved when I submit an update.
Do I need to do something different with my associations, or is there a better way to narrow the data in the forms?
My models:
class User < ActiveRecord::Base
has_many :employees, :dependent => :destroy
has_many :positions, :dependent => :destroy
class Employee < ActiveRecord::Base
belongs_to :user
has_many :positions, :through => :staffizations
has_many :staffizations, :dependent => :destroy
class Position < ActiveRecord::Base
belongs_to :user
has_many :employees, :through => :staffizations
has_many :staffizations, :dependent => :destroy
class Staffization < ActiveRecord::Base
belongs_to :employee
belongs_to :position
My employee edit fields form is set up to return checkboxes for the possible positions the employee could hold but is return all the positions in the whole app and is not updating the data when I hit submit:
- Position.all.each do |position|
= check_box_tag :position_ids, position.position_name, #employee.positions.include?(position), :name => 'employee[position_ids][]'
= label_tag :position_ids, position.position_name
My employees controller update def added the line for the have_many :through association. Is this where I should narrow the return down to the current signed in user's employees and positions?
#employee.attributes = {'position_ids' => []}.merge(params[:employee] || {})
First, shouldn't you be using :
class Employee
has_and_belongs_to_many :positions
end
class Position
has_and_belongs_to_many :employees
end
then, you could narrow the available positions with :
Position.where(:user_id => #employee.user_id).each # etc.
you could even make a scope for it:
class Position
def available_for_employee employee
where(:user_id => employee.user_id)
end
end
... and then use this in a helper that generates your checkboxes
def position_checkboxes_for_employee employee
Position.available_for_employee(employee).each do |position|
= check_box_tag :position_ids, position.position_name, #employee.positions.include?(position), :name => 'employee[position_ids][]'
= label_tag :position_ids, position.position_name
end
end
returning ALL the positions as checkboxes is exactly what you'd want, no?
what if a employee changes positions? you'd need that checkbox then, not only the checked ones..
Thanks to a friend, since my have_many through between my employees and positions belongs to the business. I needed to add the attr_accessible position_ids and attr_accessible employee_ids to the respective models. In addition, in my employee view field, I needed to add the options so that my call for positions only calls those positions associated with this business, like so:
- Position.find_all_by_user_id(#employee.user_id).each do |position|
= check_box_tag :position_ids, position.id, #employee.positions.include?(position), :name => 'employee[position_ids][]'
= label_tag :position_ids, position.position_title

belongs_to has_many assosiation help

I created a joined table for the models category and products (both created with scaffold). The Product model is this:
class Product < ActiveRecord::Base
belongs_to :category
def category_id
category.id if category
end
def category_id=(id)
self.category = Category.find_by_id(id) unless id.blank?
end
end
and the category model is this:
class Category < ActiveRecord::Base
has_and_belongs_to_many :products
end
In the form.html.erb I create a dropbox with all the classes for the user to choose from:
<p>
<label for="product_category_id">Category:</label><br />
<%= f.collection_select :category_id, Category.find(:all), :id, :name, :prompt => "Select a Category" %>
</p>
Yet when I take a look at the show of the product:
<p>
<b>Category:</b>
<%= #product.category_id %>
</p>
or the list of the products (index.html.erb):
<td><%= product.category_id %></td>
There's no category. Just blank. I don't get it. Is something wrong with the category_id method or the association?
Firstly, you don't need the explicit category_id and category_id= methods. ActiveRecord will handle those for you for a belongs_to association.
Secondly, there seems to be a mismatch between whether you want a has_and_belongs_to_many or a has_many/belongs_to association. If you have a join table then you have the former, in which case both sides of the association should be declared with has_and_belongs_to_many. If you are just using a category_id on the products table then the other end of your association on Category should be has_many :products.
With a join model:
class Categorization < ActiveRecord::Base
belongs_to :category
belongs_to :product
end
you would define in your Product class:
has_many :categorizations
has_many :categories, :through => :categorizations
Then, because your association is a 'many' association, you do not get a .category method on a product. You do however get a categories method (plus several more methods - look at the has_many documentation). If you name your collection_select category_ids then it should work as expected. You may also want to add the 'multiple' option to the select in order to choose more than one category.
Your association is obviously incorrect. As pointed out, the category has_many products. And in case you want to use a many-to-many relationship you're strongly advised to use the has_many :through relationship.

Resources