Why does rails not populate an auto-incrementing column in the item returned from a create? Is there a better way to do this?
In rails, when you do a = Foo.create
then a.id
is populated
But if you have a field that was created via
def up
execute "ALTER TABLE my_table ADD COLUMN my_auto_incrementing_column INTEGER AUTO_INCREMENT not null UNIQUE KEY;"
end
Then that field does not appear when you use create. You have to use a reload also.
a = Foo.create
a.id # not nil
a.my_auto_incrementing_column # nil
a.reload
a.my_auto_incrementing_column # is now populated
Version information:
$ ruby -v
ruby 1.9.3p484 (2013-11-22 revision 43786) [x86_64-darwin14.5.0]
$ bundle exec rails -v
Rails 3.2.12
$ mysql --version
mysql Ver 14.14 Distrib 5.6.26, for osx10.10 (x86_64) using EditLine wrapper
Some background:
This code is being applied to a large existing in-production rails codebase that requires that all id fields be UUIDs. The auto_increment column is not a primary key, because it was added after we had discovered that a new external integration partner could not handle using our existing long unique identifiers (UUIDs).
We are working hard to update our version of ruby but we don't want to wait for that as a solution to this problem. Also, after reading changelogs in activerecord, I still don't have proof that any future version of ruby/rails will contain a bugfix for this problem.
The code which I want to improve:
class Foo < ActiveRecord::Base
has_one :object_containing_auto_incrementing_column
def my_method
if self.object_containing_auto_incrementing_column.nil?
self.object_containing_auto_incrementing_column = ObjectContainingAutoIncrementingColumn.create(owner: self)
self.object_containing_auto_incrementing_column.reload
end
self.object_containing_auto_incrementing_column.my_auto_incrementing_column
end
end
After looking at the source code it does not appear that ActiveRecord tries to populate auto-incrementing columns. It only assigns the value that is returned by the INSERT
statement to the #id
attribute and nothing else.
# ActiveRecord:: Persistence::ClassMethods
def create
# ...
self.id ||= new_id if self.class.primary_key
# ...
end
If you want to populate my_auto_incrementing_column
without hitting the DB twice, I think there is no way around patching ActiveRecord itself.
Have a look at how the insert method is implemented:
# Returns the last auto-generated ID from the affected table.
#
# +id_value+ will be returned unless the value is nil, in
# which case the database will attempt to calculate the last inserted
# id and return that value.
#
# If the next id was calculated in advance (as in Oracle), it should be
# passed in as +id_value+.
def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
sql, binds = sql_for_insert(to_sql(arel, binds), pk, id_value, sequence_name, binds)
value = exec_insert(sql, name, binds)
id_value || last_inserted_id(value)
end
There might not be any trivial way to change the current API to populate your field in question.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With