How to use build method with a has_many :through association - ruby-on-rails

My Models:
class Vip < ActiveRecord::Base
belongs_to :organization
has_many :events
has_many :organizations, :through => :events
end
class Organization < ActiveRecord::Base
belongs_to :user
has_many :events
has_many :vips, :through => :events
end
class Event < ActiveRecord::Base
belongs_to :organization
belongs_to :vip
end
My vips Controller:
def create
#organization = Organization.find(params[:organization_id])
#vip = #organization.vips.build(vip_params)
if #vip.save
redirect_to organization_path(#organization)
else
render 'new'
end
end
def vip_params
params.require(:vip).permit(:name, :about, :organization_id)
end
Before I started using the has_many :through associations, the build method would automatically add the foreign key to the new vip. So my vips table would have the organization_id column populated. Since using the has_many associations, the organization_id column is being left NULL on 'vip#create'.
Is there a reason that build wouldn't work the same way anymore with my new associations?

Related

Update nested_attributes

A course has_many students and student has_many courses
Using a json API how would we update course to assign multiple students to a course
Model
class Course < ActiveRecord::Base
has_many :course_students
has_many :students, through: course_students
accepts_nested_attributes_for :course_students
end
class Student < ActiveRecord::Base
has_many :course_students
has_many :courses, through: course_students
end
class CourseStudent < ActiveRecord::Base
belongs_to :course
belongs_to :student
end
Controller
class CoursesController < SessionsController
def update
if #course.update_attributes(course_params)
puts "students should now be added to course"
end
end
def course_params
params.require(:course).permit(:description, :status, course_students_attributes: [:id], course_jobs_attributes: [:id])
end
end
Am I on the right path?
If your relationship is many to many, you are missing the keyword through in the association declaration:
class Course < ActiveRecord::Base
has_many :students, through: :course_students
accepts_nested_attributes_for :course_students
end
class Student < ActiveRecord::Base
has_many :courses, through: :course_students
end
http://guides.rubyonrails.org/association_basics.html#the-has-many-through-association
Also be careful with accepts_nested_attributes_for, specially with validations. Here you can read more: https://robots.thoughtbot.com/accepts-nested-attributes-for-with-has-many-through

How to get related records across a join table in Rails?

In my Rails application I have people which can have many projects and vice versa:
# app/models/person.rb
class Person < ActiveRecord::Base
has_many :people_projects
has_many :projects, :through => :people_projects
end
# app/models/people_project.rb
class PeopleProject < ActiveRecord::Base
belongs_to :person
belongs_to :project
end
# app/models/project.rb
class Project < ActiveRecord::Base
has_many :people_projects
has_many :people, :through => :people_projects
def self.search(person_id)
if person_id
where("person_id = ?", person_id) # not working because no person_id column in projects table
else
scoped
end
end
end
How can I filter the projects by person_id in the index view of my ProjectsController, e.g. by using a URL like this: http://localhost:3000/projects?person_id=164
I can't get my head around this. Please help! Thanks...
Your association definition is not complete for Person and Project models. You also need has_many :people_projects defined.
# app/models/person.rb
class Person < ActiveRecord::Base
has_many :people_projects # <-- This line
has_many :projects, :through => :people_projects
end
# app/models/project.rb
class Project < ActiveRecord::Base
has_many :people_projects # <-- This line
has_many :people, :through => :people_projects
end
# app/models/people_project.rb
# This is defined correctly
class PeopleProject < ActiveRecord::Base
belongs_to :person
belongs_to :project
end
Please reference The has_many :through Association for further details.
With this definition, you will be able to get all the projects of the current user using current_user.projects, just like you've already done in your ProjectsController#index.
Update:
You could use either joins or includes in your search method and apply the where condition. Something like follows:
# app/models/project.rb
class Project < ActiveRecord::Base
has_many :people_projects
has_many :people, :through => :people_projects
def self.search(person_id)
if person_id
includes([:people_projects, :people]).where("people.id = ?", person_id)
else
scoped
end
end
end
You will not have a person_id in the projects table because its a has_many<>has_many relationship.
Simply #person.projects will perform a join btw person_projects & projects tables and returns the appropriate projects.
*I assume,current_user returns a Person object.*
Also, complete your Model definitions. Each of them should list their relation to PeopleProjects
class Person < ActiveRecord::Base
has_many :people_projects
has_many :projects, :through => :people_projects
end
class Project < ActiveRecord::Base
has_many :people_projects
has_many :people, :through => :people_projects
end

Named many to many relations in Rails

How should I create following model in Rails 3.2? Project can have 1+ owners and 1+ users. Both of them are instances of class Person. I've thought about has_and_belongs_to_many but I don't know how to handle two separate collections of Persons for each Project.
You'll need a join model to represent each has-and-belongs-to-many relationship, and you would access using has-many-through as described here:
class ProjectOwnerLink < ActiveRecord::Base
belongs_to :project
belongs_to :owner, class_name: 'Person'
end
class ProjectUserLink < ActiveRecord::Base
belongs_to :project
belongs_to :user, class_name: 'Person'
end
class Project < ActiveRecord::Base
has_many :project_owner_links
has_many :owners, :through => :project_owner_links
has_many :project_user_links
has_many :users, :through => :project_user_links
end
class Person < ActiveRecord::Base
has_many :project_owner_links
has_many :owned_projects, :through => :project_owner_links, :source => :project
has_many :project_user_links
has_many :used_projects, :through => :project_user_links, :source => :project
end
You could define another model Participation that holds the type of the relationship, i.e. the role of the user. (Untested) code:
class Project < ActiveRecord::Base
has_many :participations
has_many :users, :through => :participations
def with_role(role)
includes(:participations).where('participation.role = ?', role)
end
def owners
users.with_role('owner')
end
def participants
users.with_role('participant')
end
end
 
class User < ActiveRecord::Base
has_many :participations
has_many :projects, :through => :participations
def with_role(role)
includes(:participations).where('participation.role = ?', role)
end
def projects_owned
projects.with_role('owner')
end
def projects_participating_in
projects.with_role('participant')
end
end
 
class Participation < ActiveRecord::Base
# has an attribute 'role'
belongs_to :project
belongs_to :user
end
Below is the demo application.
https://github.com/diatmpravin/habtm-demo.git
Please have a look, Let me know if you have any question?

DELETE methods for HABTM associations?

I have the following models:
class Release < ActiveRecord::Base
has_many :products, :dependent => :destroy
has_and_belongs_to_many :tracks
end
class Product < ActiveRecord::Base
belongs_to :release
has_many :releases_tracks, :through => :release, :source => :tracks
has_and_belongs_to_many :tracks
before_save do
self.track_ids = self.releases_track_ids
end
end
class Track < ActiveRecord::Base
has_and_belongs_to_many :releases
end
class ReleaseTracks < ActiveRecord::Base
belongs_to :release
belongs_to :track
end
class ProductsTracks < ActiveRecord::Base
belongs_to :product
belongs_to :track
end
At the moment I can create a release and add tracks to it. When I then create a product it inherits the tracks from the release.
What I want to do is be able to delete individual tracks at the product level, but not the track entry itself, so delete the association in ProductsTracks.
How would I go about writing the appropriate destroy method, which controller should it reside in and how should the link_to be structured?
Have you tried just destroying the tracks at the product level? I believe the default behavior is to destroy the relationship, and not the record at the other end of the relationship.

Rails has_many :through with custom column

I have 2 model associated with module ProjectsUsersRole:
Users can join more than 1 Projects, Project have lots of users, and user join the project with a role saying "admin" or "member":
class User < ActiveRecord::Base
has_many :projects_users_role
has_many :projects, :through => :projects_users_role
end
class Project < ActiveRecord::Base
has_many :projects_users_role
has_many :users, :through => :projects_users_role
end
class ProjectsUsersRole < ActiveRecord::Base
belongs_to :user
belongs_to :project
attr_accessible :role, :user, :project
end
I can get the project of current user:
#projects = current_user.projects
But how to get all the users in the projects with role?
#projects.each do |project|
project.projects_users_role.each do |r|
debug r.role
end
end

Resources