RAILS: How to get has_many associations of a model
You should be using ActiveRecord reflections.
Then you can type something like this:
A.reflect_on_all_associations.map { |assoc| assoc.name}
which will return your array
[:B, :C]
How to get all has_many associations from an active record collection
Given that @taskflows
is an ActiveRecord::Relation
, you can do this:
@datasets = Dataset.joins(:dataset_assignments).
where(dataset_assignments: {taskflow: @taskflows.joins(:datasets) })
or, in the other direction:
@taskflows = Taskflow.joins(:dataset_assignments).
where(dataset_assignments: {dataset: @datasets.joins(:taskflows) })
joins
generates INNER JOIN
s through many-to-many table and having the receiver being an instance of ActiveRecord::Relation
will preserve other conditions.
As @oreoluwa suggested, you can avoid N+1 queries when enumerating by using includes
:
@taskflows = Taskflow.joins(...).includes(:datasets)
Rails Has Many Association
First ... a word of caution: That railscast is very old. There may be syntactical things in that episode that have been dated by new versions of rails.
Question 1
If you are using the has_many through method then you have to have an id column in the join model because you are using a full blown model. As Ryan mentions in the episode, you'll choose this method if you need to track additional information. If you use the has_and_belongs_to_many method, you will not have an id column in your table.
If you want to achieve a check where you do not allow duplicates in your many-to-many association (ie allow the pairing of item a with item b and again allowing another record of item a to item b), you can use a simple validates line with a scope:
validates_uniqueness_of :model_a_id, :scope => [:model_b_id]
Question 2
You can add indices in your migrations with this code
add_index :table_name, [ :join_a_id, :join_b_id ], :unique => true, :name => 'by_a_and_b'
This would be inserted into the change block below your create_table statement (but not in that create_table block). Check out this question for some more details: In a join table, what's the best workaround for Rails' absence of a composite key?
Question 3
I'm not completely clear on what you're looking to accomplish but if you want to take some action every time a new record is inserted into the join model I would use the after_create active record hook. That would look something like this.
class YourJoinModel < ActiveRecord::Base
after_create :do_something
def do_something
puts "hello world"
end
end
That function, do_something, will be called each time a new record is created.
Question 4
Using the has_many through method will give you access to the additional attributes that you defined in that model on both sides of the relationship. For example, if you have this setup:
class Factory < ActiveRecord::Base
has_many :widgets, :through => :showcases
end
class Widget < ActiveRecord::Base
has_many :factories, :through => :showcases
end
class Showcases < ActiveRecord::Base
belongs_to :factory
belongs_to :widget
attr_accessiable :factory_id, :widget_id, :visible
end
You could say something like
widget = Widget.first
shown = widget.showcases
shown.first.visible
or
shown = widget.showcases.where( :visible=> true )
You can also reach to the other association:
shown.first.factory
in rails, how to return results from two has_many associations under another has_many association or a scope
Unfortunely this is not really a case that ActiveRecord assocations really can handle that well. Assocations just link a single foreign key on one model to a primary key on another model and you can't just chain them together.
If you wanted to join both you would need the following JOIN clause:
JOINS matches ON matches.mentor_id = entities.id
OR matches.mentee_id = entities.id
You just can't get that as assocations are used in so many ways and you can't just tinker with the join conditons. To create a single assocation that contains both categories you need a join table:
class Entity < ApplicationRecord
# Easy as pie
has_many :matchings
has_many :matches, through: :matchings
# this is where it gets crazy
has_many :matchings_as_mentor,
class_name: 'Matching',
->{ where(matchings: { role: :mentor }) }
has_many :matches_as_mentor,
class_name: 'Match',
through: :matchings_as_mentor
has_many :matchings_as_mentee,
class_name: 'Matching',
->{ where(matchings: { role: :mentee }) }
has_many :matches_as_mentee,
class_name: 'Match',
through: :matchings_as_mentor
end
class Matching < ApplicationRecord
enum role: [:mentor, :mentee]
belongs_to :entity
belongs_to :match
validates_uniqueness_of :entity_id, scope: :match_id
validates_uniqueness_of :match_id, scope: :role
end
class Match < ApplicationRecord
# Easy as pie
has_many :matchings
has_many :entities, through: :matching
# this is where it gets crazy
has_one :mentor_matching,
class_name: 'Matching',
->{ where(matchings: { role: :mentee }) }
has_one :mentor, through: :mentor_matching, source: :entity
has_one :mentee_matching,
class_name: 'Matching',
->{ where(matchings: { role: :mentor }) }
has_one :mentee, through: :mentor_matching, source: :entity
end
And presto you can reap the rewards of having a homogenous association:
entities = Entity.includes(:matches)
entities.each do |e|
puts e.matches.order(:name).inspect
end
It is a lot more complex though and you need validations and constraints to ensure that a match can only have one mentor and mentee. its up to you to evaluate if its worth it.
The alternative is doing somehing like:
Match.where(mentor_id: entity.id)
.or(Match.where(mentee_id: entity.id))
Which cannot does not allow eager loading so it would cause a N+1 query issue if you are displaying a list of entites and their matches.
Get first association from a has many through association
Assuming you are on Postgresql
Playlist.
select("DISTINCT ON(playlists.id) playlists.id,
songs.id song_id,
playlists.name,
songs.name first_song_name").
joins(:songs).
order("id, song_id").
map do |pl|
[pl.id, pl.name, pl.first_song_name]
end
Selecting one of a has_many relation in Rails
You can add an additional one-to-one relationship.
# create by running:
# rails g migration AddPrimaryPictureToUser
class AddPrimaryPictureToUser < ActiveRecord::Migration[5.0]
def change
add_column :users, :primary_picture_id, :integer
add_index :users, :primary_picture_id
add_foreign_key :users, :pictures, column: :primary_picture_id
end
end
class User < ApplicationRecord
has_many :pictures, as: :imageable
# This should be belongs_to and not has_one as the foreign key column is
# on the users table
belongs_to :primary_picture,
class_name: 'Picture',
optional: true
end
class Picture < ApplicationRecord
belongs_to :imageable, polymorphic: true
has_one :primary_user,
foreign_key: 'primary_picture_id',
class_name: 'User'
# ...
end
The main reason to do it this way vs for example a boolean flag on the pictures
table is that having a separate relationship makes it easy to join which is important for avoiding N+1 queries which is an issue if you are listing a bunch of users together with their primary image.
@users = User.includes(:primary_picture).all
Rails 4 - How to query a has_many model to find objects based on associations
Use
@posts = Post.includes(:votes).where('votes.user_id <> ? or votes.user_id is null', current_user.id)
I suppose that your query is not retrieving the 3 records as votes.user_id
is null for them.
Related Topics
Differencebetween 'Try' and '&.' (Safe Navigation Operator) in Ruby
Rails - Local Variables Versus Instance Variables
Regex, How to Match Multiple Lines
Check If an Array Is Subset of Another Array in Ruby
Rails Article Helper - "A" or "An"
Escape Single Quote in Xpath with Nokogiri
Faulty Ruby Compilation with Rvm: Getting 'Undefined Symbol: Rb_Digest_Md5_Init' While Running Racku
Ruby Class Inheritance: What Is '<<' (Double Less Than)
How to Set Private Instance Variable Used Within a Method Test
Is Ruby Really an Interpreted Language If All of Its Implementations Are Compiled into Bytecode
Devise Login with User or Admin Models and Basecamp Style Subdomains
How to Check If a Ruby Array Includes One of Several Values
Yaml Indentation for Array in Hash
How to Declare a String with Both Single and Double Quotes in Yaml