Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Manually initialize ActiveRecord object with custom select values

Goal: I want to initialize a record like ActiveRecord does under the hood.

Secondary goal: I also want to understand ActiveRecord better.

Why: I want an ActiveRecord object with values that I selected myself using hard-written SQL.

Analogy: I can do

Foo.first

and that will give me a regular Foo instance with all of its columns Foo<id: 1, name: "ABC">

I can also do

Foo.select('1 AS bar').first

and I'll get an instance of Foo that I can then call foo.bar and it will return 1, EVEN if the Foo table doesn't have a 'bar' column. Nice!

Background: I have a hash of properties, {bar 1, id: 1, name: "ABC"}. I want to initialize an instance of Foo that will have a bar property even though the Foo table doesn't have a bar column.

Error: If I try to initialize Foo with those properties, I'll get the error ActiveModel::UnknownAttributeError: unknown attribute 'bar' for Foo

Condition: I don't want to have an attribute :bar in the Foo class, nor do I want an attr_accessor :bar either.

Reasoning: I know that ActiveRecord is doing something under the hood when I select columns that it doesn't have so that in the end I get an object with those properties. What is it doing?

like image 964
hrdwdmrbl Avatar asked Feb 19 '26 03:02

hrdwdmrbl


1 Answers

Suppose you have an active-record model Foo:

class Foo < ApplicationRecord
end

You may instantiate a new instance of that model with arbitrary attribute values without fetching them through SQL using the ActiveRecord::Persistence.instantiate method:

attributes = {
  "id" => 1,
  "foo" => 1,
  "name" => "ABC"
}

foo = Foo.instantiate(attributes)
# => #<Foo:0x00007fcae44fee88 id: 1>

Attributes Hash requires Strings

Please note that the attributes hash is required provide the keys as String, not as Symbol. So, you need to have attributes = {"id" => 1, "foo" => 1, "name" => "ABC"} rather than attributes = {id: 1, foo: 1, name: "ABC"}.

If you already have a hash with Symbol keys, you may convert it using Hash#stringify_keys:

attributes = {id: 1, foo: 1, name: "ABC"}
attributes.stringify_keys  # => {"id" => 1, "foo" => 1, "name" => "ABC"}

foo = Foo.instantiate(attributes.stringify_keys)

Retrieve Attributes

From the instance foo, you may retrieve the attributes like this:

foo.attributes
# => {"id"=>1, "bar"=>1, "name"=>"ABC"}

# or:
foo.bar
# => 1

Further Resources

  • ActiveRecord::Persistence.instantiate
  • Old, but interesting and very well-done railscast: http://railscasts.com/episodes/239-activerecord-relation-walkthrough
like image 106
fiedl Avatar answered Feb 21 '26 15:02

fiedl



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!