-
Notifications
You must be signed in to change notification settings - Fork 21.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Postgres default function values do not get loaded on create #34237
Comments
Rather than But either way would be an improvement. |
Currently the solution is to immediately reload the model (and that's what I do). That's not much of a penalty (an extra DB inquiry). There's many other things that affect what value eventually ends up in the column (e.g. triggers) which makes me lean more towards a reload |
Oh good point, rather than AR try to automatically discover what to put in My current work-around is by doing something a little bit different than a reload. I just override the relevant attribute method to do a lazy Really it ought to check not just # Due to rails bug, we don't immediately have the database-provided value after create. :(
# If we ask for it and it's empty, go to the db to get it
# https://github.com/rails/rails/issues/21627
def friendlier_id(*_)
in_memory = super
if !in_memory && persisted? && !@friendlier_id_retrieved
in_memory = self.class.where(id: id).limit(1).pluck(:friendlier_id).first
write_attribute(:friendlier_id, in_memory)
clear_attribute_change(:friendlier_id)
# just to avoid doing it multiple times if it's still unset in db for some reason
@friendlier_id_retrieved = true
end
in_memory
end |
I tackled that exact problem in a POC over here. It wasn't that simple as I had to override quite some AR core code. So it's not a very great implementation and will probably break as soon as internals change. |
If this will be implemented, I it will definitely override some AR core code ;) so it's a great reference! Still, for this I assume we need somebody very familiar with the AR internals |
This issue has been automatically marked as stale because it has not been commented on for at least three months. |
ActiveRecord should support adding additional I almost would see something like:
Where the |
Support for adding additional |
This won't work in situations where database triggers will update the row, think of database-backed timestamps or database-backed counter cache columns. As you only ever get the id returned, you have to manually reload the record as of now. |
@tbuehlmann that seems like a separate concern, given that Rails does not natively support managing triggers as part of a schema. It does, however, support computed defaults, so it's reasonable to expect AR models to reflect those values with no additional effort. |
@md5 Sure, ActiveRecord doesn't native support managing triggers, but the effect of a trigger could be a new value in the column before the insert or update is complete. If ActiveRecord is concerned with the true state of the world it's only fair that it opens an avenue to be informed of what columns are getting their values out of band. In my opinion, |
@yawboakye I think I made it pretty clear I support the idea of explicitly adding values to |
Right, right now it handles no db-set values. Having it handle db-set values for those with database defaults configured in a migration would handle a whole lot of use cases very well, and be a big step forward. As per the title of this GH issue. No need to solve everything to solve something. I agree with @md5 , and think more expanded feature discussion could be on a different ticket. (Of course, this ticket is already closed as stale anyway, and apparently commenting on it does not re-open it. :( That doesn't necessarily mean Rails team would look upon a PR unfavorably if one did appear...) |
@md5 Apologies for misunderstanding your point. |
Hi, I'm having trouble with this matter too and I'm using the callback workaround but I would love for a proper solution to exist 😊 @md5's approach seems reasonable, triggers being a whole other issue, although I would argue that implementing a manually set list of A solution providing the columns with db-set values automatically could be implemented afterward, piggybacking on the manual implementation, and be a nice-to-have. What do you think? ❤️ P.S. Could the issue be reopened since people are commenting on it? |
I guess with no existing Rails maintainers expressing interest, what would have to happen is someone to create a PR? I agree it's hard to motivate to spend time doing this when you don't know if the PR will get attention or have any chance of being merged. I agree this issue should be open because it is a real issue, affecting multiple people, with or without a current solution. But that may not be how Rails manages it's issues. Any kind of advice from maintainers would be very welcome. |
I'll just add that Postgres 12 supports fully generated columns on otherwise normal tables, so the use case here will expand due to that |
@jrochkind, I too want to hear advice from maintainers, since otherwise a PR would be a shot in the dark. Any ideas how to get their attention? Because as it is now, being a closed, stale issue, we're basically lost in space. Getting in contact with a maintainer could also lead to reopening the issue - as it is a real & active issue. [If we were open, we would be on page 2, sorted by comments - and on page 1 sorted by thumbsup👍.] |
Updated my proof of concept for Rails 6.0.0.rc1: https://github.com/tbuehlmann/active_record-returning_attributes. Have to admit, monkeypatching core code isn't fun. |
@tbuehlmann Nice work on a potential solution for a fix. It would be great if we could get it working without needing to configure |
@rafaelfranca Apologies if you're the wrong person to ask about this. But is this functionality that would make sense as a part of ActiveRecord core ? If so, can we reopen this issue ? Below I've rewritten to steps for reproduction, and desired functionality: # frozen_string_literal: true
require "bundler/inline"
gemfile(true) do
source "https://rubygems.org"
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
# Activate the gem you are reporting the issue against.
gem "activerecord", "6.0.0.rc1"
gem "pg"
end
require "active_record"
require "minitest/autorun"
require "logger"
# This connection will do for database-independent bug reports.
ActiveRecord::Base.establish_connection(adapter: "postgresql", encoding: 'unicode', database: 'rails_issues', username: 'postgres', host: 'localhost', port: '5432')
ActiveRecord::Base.logger = Logger.new(STDOUT)
ActiveRecord::Schema.define do
enable_extension "pgcrypto"
create_table :posts, force: true do |t|
t.string :name
t.string :default_value_string
t.string :default_generated_string, default: -> { "nextval('posts_id_seq')" }
t.integer :some_number
t.bigint :default_value_int, default: -> { "nextval('posts_id_seq')" }
t.uuid :uuid
end
change_column_default :posts, :uuid, 'gen_random_uuid()'
change_column_default :posts, :default_value_string, 'default_string'
end
class Post < ActiveRecord::Base
end
class BugTest < Minitest::Test
# This fails
def test_db_generated_columns_returned
post = Post.create!(
name: 'custom name',
some_number: 2
)
assert_equal 'custom name', post.name
assert_equal 'default_string', post.default_value_string
refute_nil post.default_generated_string, "expected the string to be present"
refute_nil post.default_value_int, "expected the int to be present"
refute_nil post.uuid, "expected the uuid to be present"
end
# This passes because of `#reload`
def test_all_attributes_are_set_on_reload
post = Post.create!(
name: 'custom name',
some_number: 2
)
post.reload
assert_equal 'custom name', post.name
assert_equal 'default_string', post.default_value_string
refute_nil post.default_generated_string
refute_nil post.default_value_int
refute_nil post.uuid
end
end |
Yes. To my eye, the ideal behaviour would be @tbuehlmann's explicit
No; this is a feature request, and by policy we don't track feature requests in this issue tracker. (A missing feature is not a bug, and the more strongly people pretend to believe otherwise, the less emotional energy I, for one, have to deal with it. [A comment for the thread in general, not directed at you.]) |
@matthewd great, thanks for the feedback. So would the best approach be to begin a PR, and open discussions like this there? With a potential solution already committed. |
Hey guys! @tbuehlmann @choncou @matthewd was a PR already started so it can be linked here? |
@omarsotillo: Nope, don't think so. |
I just saw that the It reminded me of this issue, which is near and dear to my heart. |
This issue just beat me. So is the best solution for now just doing a |
@woniesong92 Yup I believe so. |
+1 for implementing this, just hit the problem in a project. |
@00dav00 I do imply that PRs are the correct way and are welcomed/accepted to then discuss this matter further. Implied both from 1) @matthewd's response:
as well as 2) the contribution instructions from rubyonrails.org. I guess somebody just needs to take a stab at it - I myself sadly don't have the time to spare. |
@davegson @md5 @matthewd I created an initial PR with a possible solution for this. For now it only has a basic test for record creation which is passing. Please let me know if you consider this is the right approach to solve this issue. With that I will continue to do the required modifications for record updates and check other tests are passing. Thanks! |
@00dav00 thank you so much! At first glance I really like what I see. Though I would mention I've never contributed to the Rails code myself, just inspected it from time to time for my own projects. What I think you could improve upon is your wording/presentation of the PR. As @matthewd mentioned, this issue is not a bug, hence it was rightfully closed, but a "missing feature". So your PR is not attempting to "solve a bug", but "adding a feature". Describe it that way, and feel free to copy my intro texts. Show and describe both the problem and your provided solution, so reviewers immediately see what it is all about. (I created a pastebin of my initial markdown so you can copy it more easily - especially the tables were a hassle ;) I'm sure that will spice things up and help the rails team then review the code part. |
Until such a feature (hopefully) emerges, I'm going with the simplest solution: |
This issue caught me off guard, so I did a bit of digging and then a write up on it. In case it's useful to anyone else who ends up here, here it is: https://dev.to/jbranchaud/a-few-methods-for-returning-default-values-when-creating-activerecord-objects-1457 |
I also noticed the issue when using uuid column as the primary key, i.e.
Then
This is because Rails tries to use the primary key to find the record and then reload. Without the primary key, it cannot find the record. Is it bad practice to use mysql-generated uuid as the primary key? Probably it's better to generate uuid in Rails using |
Steps to reproduce
1. Create a model
2. Create a migration with a DB function as
default
.rails db:migrate
3. Create a new order
rails console
Expected behavior
I expect
create
to setuuid
(by the postgres default) and for it to set the newuuid
value into theorder
AR instance, as it does with theid
.Actual behavior
The
uuid
does get set in the db, but it is not returned into theorder
instance.Current workaround
Calling
order.reload
does then retrieve the values set by postgres. Other workarounds are described in this issueBackground
This issue already describes this bug, but the issue was initially a request to support functional defaults, which has since been implemented.
This quote from the discussion summarizes it pretty well:
This blog post describes the issue wonderfully.
Next steps
So a solution could be an option to tell AR to use
RETURNING *
in some instances. But I myself am not familiar with the ActiveRecord internals so I feel this should be further discussed.System configuration
Rails version: 5.2.1
Ruby version: 2.4.1
Postgres version: 9.6.3
The text was updated successfully, but these errors were encountered: