David Lesches
David Lesches

Reputation: 788

Rails / ActiveRecord: detect if a column is an association or not

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

Answers (2)

Ivan Shamatov
Ivan Shamatov

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

Ryan Bigg
Ryan Bigg

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

Related Questions