Reputation: 788
Assuming I am abstracting code, and am looping through the column names of object x, what's the best way to detect if a column is an association or not?
I know I can do this, but I am wondering if there is a better way:
@user = User.first
@user.attributes.keys.each do |column|
if column[-3..-1] == "_id" && @user.respond_to?(column[0..-4].to_sym)
puts "#{column} is an association / relation."
else
puts "#{column} is not an assocation / relation."
end
end
end
Any built-in Rails methods or helpers to detect associations? Code above is neither pretty nor fool proof. Thanks!
Upvotes: 11
Views: 3051
Reputation: 1416
I'm sure that selected solution is better, but if you live in "ideal world", where everybody follow the rails convention, I suppose you can rely on this:
2.2 Schema Conventions Active Record uses naming conventions for the columns in database tables, depending on the purpose of these columns.
Foreign keys - These fields should be named following the pattern singularized_table_name_id (e.g., item_id, order_id). These are the fields that Active Record will look for when you create associations between your models.
So just look for _id suffix at the end of column name:
Model.column_names.select{|cn| cn.include?("_id")}
Upvotes: 1
Reputation: 107718
One way to do this would be to reflect upon all associations for that class:
associations = class_goes_here.reflect_on_all_associations
And then to find just the belongs_to
ones, since those will have the _id
field:
associations = associations.select { |a| a.macro == :belongs_to }
Then you can find the foreign key used on these associations by doing this:
association_foreign_keys = associations.map(&:foreign_key)
I wouldn't use @user.attributes
to get the attributes and then keys
on that to get the column names. I would use User.column_names
to get the column names.
Therefore, with all that explained, you can then change your code to be this to make it more foolproof:
associations = User.reflect_on_all_associations
associations = associations.select { |a| a.macro == :belongs_to }
association_foreign_keys = associations.map(&:foreign_key)
User.column_names.each do |column|
if association_foreign_keys.include?(column)
puts "#{column} is an association / relation."
else
puts "#{column} is not an assocation / relation."
end
end
Upvotes: 20