Those are my models:
class User < ActiveRecord::Base
has_many :memberships
has_many :groups, through: :memberships
end
class Group < ActiveRecord::Base
has_many : memberships
has_many :users, through: :memberships
belongs_to :group_type
end
class Membership < ActiveRecord::Base
belongs_to :user
belongs_to :group
end
class GroupType < ActiveRecord::Base
has_many :groups
end
I would like to assign groups to users. there are several types of groups, users can choose only one group from each type. in the view there should a select tag for each group type (a user don't have to be a member of each of the group types).
I can add a type_id column to the membership model and create a instance for each group for each new user, and then update rows, with something like (for type 1):
<%= form_for (#user) do |f|
<% #membership = Membership.where (user_id: :#user.id, group_type: :1) %>
<%= f.collection_select (:membership, :group_id, Group.all.where(type: 1), :id, :name) %>
<% end %>
(not sure that it will work)
anyway I'm sure there is a better way.. how can I connect the models with the one group for each type limitation? (with, ofcourse, a way to edit existing memberships).
I would appriciate any assistance...
Thank you!
Your model structure look good. You could also include the group_type in the membership and than validate against uniqueness.
class Membership < ActiveRecord::Base
belongs_to :user
belongs_to :group
belongs_to :group_type
validates :user_id, uniqueness: { scope: [:group_id, :group_type_id],
message: "can only join one group of this type." }
end
The form should create a membership.
<%= form_for (Membership.new) do |f|
<%= f.hidden_field :user_id, value: #user.id %>
<%# your selection for a group %>
<% end %>
In the controller you have to load the group from the DB, than add the group_type_id to params and .save -> The validation will handle the rest.
Related
I have been stuck on this problem for a while.
Need to make a form for competitions category with custom inputs. It should take all values from Information table and build the inputs, but the tricky part is that it should be saved to Category_informations table.
class Competition < ApplicationRecord
has_many :categories
has_many :informations
end
class Category < ApplicationRecord
belongs_to :competetion
has_many :category_informations
has_many :information, through: competition
end
class CategoryInformation
belongs_to :catagory
belongs_to :information
end
class Information < ApplicationRecord
belongs_to :competetion
has_many :category_informations
end
Competition -> name
Category -> name, competition_id
Information -> name, competition_id
Category_informations -> value, category_id, information_id
Take a look at this gem: https://github.com/plataformatec/simple_form
Simple Form aims to be as flexible as possible while helping you with powerful components to create your forms.
Let's take a simple example:
class Machine < ActiveRecord::Base
has_many :parts , inverse_of: :machine
accepts_nested_attributes_for :parts
end
class Part < ActiveRecord::Base
# name:string
belongs_to :machine
end
With these models, we can use simple_form to update the machine and its associated parts in a single form:
<%= simple_form_for #machine do |m| %>
<%= m.simple_fields_for :parts do |p| %>
<%= p.input :name %>
<% end %>
<% end %>
For 'new' action, build the nested model from the controller:
class MachinesController < ApplicationController
def new
#machine = Machine.new
#machine.parts.build
end
end
Source: https://github.com/plataformatec/simple_form/wiki/Nested-Models
Sounds to me like you're looking for accepts_nested_attributes_for
See:
https://apidock.com/rails/v3.2.3/ActiveRecord/NestedAttributes/ClassMethods/accepts_nested_attributes_for
https://rubyplus.com/articles/3681-Complex-Forms-in-Rails-5
Also, check out the cocoon gem.
I have a common many to many relationship, these are the models:
class Employee < ApplicationRecord
has_many :related_professions
has_many :professions, through: :related_professions
accepts_nested_attributes_for :related_professions
end
class RelatedProfession < ApplicationRecord
belongs_to :employee
belongs_to :profession
accepts_nested_attributes_for :profession
end
class Profession < ApplicationRecord
has_many :related_professions
has_many :employees ,through: :related_professions
end
I also have a form for saving Employees. In this form I would like to render all the Professions in a multiple select for the user to choose as needed. I want that when the user submits the form, the IDs of all the selected professions be saved in the RelatedProfession pivot table (which just have three columns: id, employee_id, profession_id). This is the part of my form for the select:
<div class="field">
<%= form.label :professions %>
<%= form.fields_for :related_professions do |rp| %>
<%= rp.collection_select :profession_id, Profession.all, :id, :name, {}, {multiple: true} %>
<% end %>
</div>
And this is the part in my EmployeeController that allows the parameters:
# Never trust parameters from the scary internet, only allow the white list through.
def employee_params
params.require(:employee).permit(:name, related_professions_attributes: [:id, profession_id: [:id]])
end
The first problem is that the form does not load the Professions if the Employee does not have any assigned. I had to manually add one to the DB and then it would populate the select.
Second problem is that when I try to update the Employee (and also the RelatedProfession pivot table) by selecting a different Profession, it won't work, and I get this error:
Related professions profession must exist
I know there must be something wrong in the permit parameters and form that is not building the select correctly.
I appreciate the help. Thanks in advance.
You no need nested attributes to created has_many through relations,
You can just pass it as array of ids.
class Employee < ApplicationRecord
has_many :related_professions
has_many :professions, through: :related_professions
end
class RelatedProfession < ApplicationRecord
belongs_to :employee
belongs_to :profession
end
class Profession < ApplicationRecord
has_many :related_professions
has_many :employees ,through: :related_professions
end
In form also just select ids of Professions.
<div class="field">
<%= form.label :professions %>
<%= rp.collection_select :profession_ids, Profession.all, :id, :name, {}, {multiple: true} %>
</div>
change strong params to allow profession_ids as array.
def employee_params
params.require(:employee).permit(:name, profession_ids: [])
end
Hope this solves your problem.
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
I have the following model structure: A Carnival has many Events, which has many Competitors. A competitor has a polymorphic Participant, which is either a Student, or a Team (which has many students). In code:
class Carnival < ActiveRecord::Base
has_many :events
end
class Event < ActiveRecord::Base
belongs_to :event_type
belongs_to :division
has_and_belongs_to_many :genders
has_and_belongs_to_many :grades
has_many :competitors
has_many :students, through: :competitors, :source => :participant, :source_type => 'Student'
belongs_to :carnival
end
class Competitor < ActiveRecord::Base
belongs_to :event
belongs_to :participant, polymorphic: true
belongs_to :grade
belongs_to :house
end
class Team < ActiveRecord::Base
has_one :competitor, :as => :participant
has_one :event, :through => :competitor
has_and_belongs_to_many :students
end
class Student < ActiveRecord::Base
belongs_to :grade
belongs_to :house
belongs_to :gender
has_many :competitors, :as => :participant
has_many :events, :through => :competitors
has_many :carnivals, :through => :events
has_and_belongs_to_many :teams
end
There is a points attribute on Competitor, so that after an Event is run points are assigned to them.
What I would like is a series of methods that would give me all Students along with their total points.
Student.with_total_points_for_carnival(c)
Student.with_individual_points_for_carnival(c)
Student.with_team_points_for_carnival(c)
I got some SQL that does total points (though I'm a little uncomfortable with how/if it works properly, since I only really stumbled upon it):
SELECT
name,
SUM(points) as points
FROM
public.students_teams,
public.students,
public.teams,
public.competitors,
public.events
WHERE
students_teams.team_id = teams.id AND
students.id = students_teams.student_id AND
competitors.participant_id = teams.id AND
events.id = competitors.event_id AND
carnival_id = 1
GROUP BY
public.students.id
ORDER BY
points DESC
This gives total points. My Rails attempts give me results, but they aren't correct, and they aren't student records either:
def self.with_total_points_for(carnival)
Student.
joins(:competitors, :events, teams: { competitor: :event}).
where('events.carnival_id = ?', [carnival.id]).
group('students.id').
sum('competitors.points')
end
Any guidance as to how to get this working? So that I could go:
<% Student.with_total_points_for(#carnival).each do |s| %>
<%= s.name %>: <%= s.total_points %> <br>
<% end %>
Even better would be a function or scope like Student.with_all_points_for which would let me do:
<% Student.with_all_points_for(#carnival).each do |s| %>
<%= s.name %>: <%= s.total_points %> / <%= s.individual_points %> / <%= s.team_points %> <br>
<% end %>
but baby-steps for now...
I am not sure I caught your aim correctly but I think it might help you
Add needed fields to select
def self.with_total_points_for(carnival)
Student.
joins(:competitors, :events, teams: { competitor: :event}).
where('events.carnival_id = ?', [carnival.id]).
group('students.id').
select('students.*, SUM(competitors.points) AS total_points')
end
Some notes:
(I don't check it but I have similar issue before) if you use Postgres you shouldn't use students.* you should list all needed columns directly: select('students.id, students.name, ..., total_points')
(it can be useful) you can list in select any columns from involved tables but remember that you use group in the query
PS I am not sure that there is an easy way to get individual_points and team_points in one simple query. I think you should use subqueries for this purposes and insert them into main query. Be careful because it might be a heavy request to DB and an optimization will be required.
I have such terrible models:
class ParentalRelation < ActiveRecord::Base
belongs_to :parent
belongs_to :student
belongs_to :counselor
belongs_to :parental_relation_type
end
class ParentalRelationType < ActiveRecord::Base
has_many :parental_relations
end
class Parent < ActiveRecord::Base
has_many :parental_relations
has_many :students, :through => :parental_relations
has_many :counselors, :through=> :parental_relations
has_many :parental_relation_types, :through=> :parental_relations
belongs_to :user, :dependent=> :destroy
belongs_to :occupation_type
accepts_nested_attributes_for :user
end
Parental relation types are like father, mother, etc. The reasoning is that a parental relation between one counselor, one parent and one student is unique and counselors should not see the relations that belong other counselors.
In controllers/parent_controller/edit action I have:
#parental_relation= ParentalRelation.find_by_counselor_id_and_student_id_and_parent_id(x, y, z)
In views/parent/_form.html.erb I have:
<%= form_for #parent do |f| %>
inside that form I need a collection_select for ParentalRelationType.all and select the parent's parental_relation_type_id for that particular parental relation, but I can't find the syntax to do it.
I tried adding
<%= collection_select(#parental_relation, :parental_relation_type_id, ParentalRelationType.all, :id, :name) %>
underneath the form_for, but the relation type id is 2, and default 1 is selected instead.
Added this to parents/_form
<%= fields_for #counselor_student_parent do |csp| %>
<%= f.label :parental_relation_type_id %>
<%= collection_select(:student_counselor_parent, :parental_relation_type_id, ParentalRelationType.all, :id, :name) %>
<% end %>
And this to parents_controller/new
def new
#counselor= Counselor.find(params[:counselor_id])
#student= Student.find(params[:student_id])
#parent= #student.parents.build
#parent_user= #parent.build_user
#counselor_student_parent= #counselor.student_counselor_parents.build
end