Warm tip: This article is reproduced from serverfault.com, please click

Rails merge temporary attributes to model object

发布于 2020-12-02 16:28:11

I have a Rails model object that has attributes maps to columns in a DB. I compute couple of temporary fields name and age which are not in the DB columns. Is there a way to merge them into the object temporarily without making changes to the model object?

class MyModel < ApplicationRecord
end

I tried using attributes.merge. and tried :

my_obj = my_obj.attributes.merge({ age: compute_age })

my_obj = my_obj.attributes.merge({ name: compute_name })

The second merge however fails with NoMethodError: undefined method `attributes' for #Hash:0x0000000120060010.

Looks like the moment I merge first one it makes the object a Hash. So I need to do

my_obj = my_obj.attributes.merge({ age: compute_age })

my_obj[:name]=compute_name

All the other properties also are accessed as a hash. However this feels inconsistent and weird to me!

Is there a better option to this than using attributes.merge i.e preserve the object? I guess it maybe cleaner to add the attr to the model object but those attributes are temporary and should not be accessed outside.

Questioner
Prasaanth Neelakandan
Viewed
0
max pleaner 2020-12-03 03:29:48

As soon as you call .attributes you turn the MyModel instance into a hash.

To turn it back into a model you can just pass that hash to the MyModel constructor (assuming you have an attr_accessor for the virtual attribute):

my_obj = MyModel.new(my_obj.attributes.merge(age: compute_age))
my_obj = MyModel.new(my_obj.attributes.merge(name: compute_name))

You can also see here for some other options: Rails update_attributes without save?

edit

to clarify a little more. You can turn a model instance into a hash via .attributes. You can turn it back into a model using Model.new(attributes_hash). While it's a hash, you can set whatever key-vals you want - it's just a hash. But in order to convert it back to a model instance, you need to make sure that all the hash keys correspond to actual attributes on the model (be they persisted attributes defined by the schema, or virtual attributes defined by attr_accessor).

If you just want to turn it into a hash and add multiple arbitrary keyvals there, you can do it with a single merge:

attrs = my_obj.attributes.merge({ age: compute_age, name: compute_name })