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.
Related
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
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?
I've got the following models and associations:
class User < ActiveRecord::Base
has_and_belongs_to_many :songs
end
class Song < ActiveRecord::Base
has_and_belongs_to_many :users
belongs_to :album
delegate :artist, :to => :album, :allow_nil => true
end
class Album < ActiveRecord::Base
has_many :songs
belongs_to :artist
end
class Artist < ActiveRecord::Base
has_many :albums
has_many :songs, :through => :albums
end
I need to be able to call user.albums and user.artists on a regular basis. Is the most efficient option to create has_and_belongs_to_many associations between User and Artist/Album ?
It seems like there should be a better way but I haven't been able to find anything yet.
You could just use has_many :albums, :through => :songs on the user object. Arel(AR) will automatically create the joins for you resulting in one query and will only fetch the albums related to the songs belonging to the User. The same goes for the artists on the User object.
I have some models like these:
class Alpha < ActiveRecord::Base
has_many :items
end
class Beta < ActiveRecord::Base
has_many :items
end
class Item < ActiveRecord::Base
belongs_to :alpha
belongs_to :beta
end
But i want Item model in each database record to belong either to an :alpha or to a :beta but NOT both. Any nice way to do it in Rails 3? or should I model it with AlphaItems and BetaItems instead?
You probably want to use a Polymorphic Association for this. More details - http://guides.rubyonrails.org/association_basics.html#polymorphic-associations
class Alpha < ActiveRecord::Base
has_many :items, :as => :itemable
end
class Beta < ActiveRecord::Base
has_many :items, :as => :itemable
end
class Item < ActiveRecord::Base
belongs_to :itemable, :polymorphic => true
end
Right now I have a rich many-to-many association with VideoVote as the independent record.
class VideoVote < ActiveRecord::Base
belongs_to :user
belongs_to :video
end
class User < ActiveRecord::Base
has_many :video_votes
has_many :voted_videos,
:through => :video_votes,
:source => :video
end
class Video < ActiveRecord::Base
has_many :video_votes
has_many :voted_users,
:through => :video_votes,
:source => :user
end
However, I want to trasform this into a polymorphic association where comments can also have many VideoVotes (I realize this is confusing, so I should probably change it to Votes). (also, a video will have many comments.) How should I do this?
You first want to add voteable_id:integer and voteable_type:string to your video_votes table.
Then your models will look like:
class VideoVote < ActiveRecord::Base
belongs_to :voteable, :polymorphic => true
end
class Comment < ActiveRecord::Base
has_many :video_votes, :as => :voteable
#code
end
class Video < ActiveRecord::Base
has_many :video_votes, :as => :voteable
#code
end
Then you can access them just like any other has_many:
#video.video_votes
#comment.video_votes
#etc.