Home » Ruby » Getting types of the attributes in an ActiveRecord object

Getting types of the attributes in an ActiveRecord object

Posted by: admin November 30, 2017 Leave a comment

Questions:

I would like to know if it is possible to get the types (as known by AR – eg in the migration script and database) programmatically (I know the data exists in there somewhere).

For example, I can deal with all the attribute names:

ar.attribute_names.each { |name| puts name }

.attributes just returns a mapping of the names to their current values (eg no type info if the field isn’t set).

Some places I have seen it with the type information:

in script/console, type the name of an AR entity:

>> Driver
=> Driver(id: integer, name: string, created_at: datetime, updated_at: datetime)

So clearly it knows the types. Also, there is .column_for_attribute, which takes an attr name and returns a column object – which has the type buried in the underlying database column object, but it doesn’t appear to be a clean way to get it.

I would also be interested in if there is a way that is friendly for the new “ActiveModel” that is coming (rails3) and is decoupled from database specifics (but perhaps type info will not be part of it, I can’t seem to find out if it is).

Thanks.

Answers:

In Rails 3, for your model “Driver”, you want Driver.columns_hash.

Driver.columns_hash["name"].type  #returns :string

If you want to iterate through them, you’d do something like this:

Driver.columns_hash.each {|k,v| puts "#{k} => #{v.type}"}

which will output the following:

id => integer
name => string
created_at => datetime
updated_at => datetime

Questions:
Answers:

You can access the types of the columns by doing this:

#script/console
Driver.columns.each {|c| puts c.type}

If you want to get a list of all column types in a particular Model, you could do:

Driver.columns.map(&:type) #gets them all
Driver.columns.map(&:type).uniq #gets the unique ones

Questions:
Answers:

In Rails 5, you can do this independently of the Database. That’s important if you use the new Attributes API to define (additional) attributes.

Getting all attributes from a model class:

pry> User.attribute_names
=> ["id",
 "firstname",
 "lastname",
 "created_at",
 "updated_at",
 "email",...

Getting the type:

pry> User.type_for_attribute('email')
=> #<ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter::MysqlString:0x007ffbab107698
 @limit=255,
 @precision=nil,
 @scale=nil>

That’s sometimes more information than needed. There’s a convenience function that maps all these types down to a core set (:integer, :string etc.)

> User.type_for_attribute('email').type
=> :string 

You can also get all that data in one call with attribute_types which returns a 'name': type hash.

Questions:
Answers:

In rails 5 this will give you a list of all field names along with their data type:

Model_Name.attribute_names.each do |k| puts "#{k} = #{Model_Name.type_for_attribute(k).type}" end

Questions:
Answers:

This snippet will give you all the attributes of a model with the associated database data types in a hash. Just replace Post with your Active Record Model.

Post.attribute_names.map {|n| [n.to_sym,Post.type_for_attribute(n).type]}.to_h

Will return a hash like this.

=> {:id=>:integer, :title=>:string, :body=>:text, :created_at=>:datetime, :updated_at=>:datetime, :topic_id=>:integer, :user_id=>:integer}