Skip to content
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

Creating records with default datetimes creates but does not set the value in the new object #28229

Closed
chrismanderson opened this issue Feb 28, 2017 · 9 comments

Comments

@chrismanderson
Copy link

When creating records of a model with a default time migration such as

t.datetime :modified_at, default: -> { 'CURRENT_TIMESTAMP' }, null: false

the value is created, but not present in the new object.

Steps to reproduce

begin
  require "bundler/inline"
rescue LoadError => e
  $stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler"
  raise e
end

gemfile(true) do
  source "https://rubygems.org"
  # Activate the gem you are reporting the issue against.
  gem "activerecord", "5.0.1"
  gem "sqlite3"
end

require "active_record"
require "minitest/autorun"
require "logger"

# Ensure backward compatibility with Minitest 4
Minitest::Test = MiniTest::Unit::TestCase unless defined?(Minitest::Test)

# This connection will do for database-independent bug reports.
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Base.logger = Logger.new(STDOUT)

ActiveRecord::Schema.define do
  create_table :posts, force: true do |t|
    t.string :title, default: 'hello'
    t.datetime :modified_at, default: -> { 'CURRENT_TIMESTAMP' }, null: false
    t.timestamps
  end
end

class Post < ActiveRecord::Base
end

class BugTest < Minitest::Test
  def test_create_missing_default
    post = Post.create!

    assert_equal 'hello', post.title
    refute_nil post.created_at
    refute_nil post.modified_at, "default value should be present"
    assert_equal post.modified_at.to_i, post.created_at.to_i
  end

  def test_save_missing_default
    post = Post.new
    post.save

    assert_equal 'hello', post.title
    refute_nil post.created_at
    refute_nil post.modified_at, "default value should be present"
    assert_equal post.modified_at.to_i, post.created_at.to_i
  end
end

Expected behavior

modified_at should be present in the returned object.

Actual behavior

modified_at is nil.

#<Post id: 1, title: "hello", modified_at: nil, created_at: "2017-02-28 22:00:36", updated_at: "2017-02-28 22:00:36">

modified_at is present in the database, and if I reload the object, it is visible.

#<Post id: 1, title: "hello", modified_at: "2017-02-28 22:00:36", created_at: "2017-02-28 22:00:36", updated_at: "2017-02-28 22:00:36">

System configuration

Rails version: 5.0.1

Ruby version: 2.4.0

More info

Same result on sqlite and postgres.

@rafaelfranca
Copy link
Member

Hmmm. I'm not sure there is a way to implement that. Since the default is being set by the database there is no way to rails to know which value is being set to show to you.

@kamipo @sgrif @matthewd WDYT?

@sngeth
Copy link
Contributor

sngeth commented Mar 1, 2017

Seems like the same issue referenced here with a Postgres default uuid.

#17605

Workaround is just to reload the instance if you're willing to take the hit.

@chrismanderson
Copy link
Author

The big negative/worry to me is that there was no indication something was amiss. I thought my default migrations were incorrect and not working for before discovering the issue.

Understand it's a tough bug to fix; but wonder if there's a way to at least trigger a warning when a record is created with this type of model so the programmer is not mislead about what's going on.

@krainboltgreene
Copy link
Contributor

Rails knows the columns, and knows the ones with default values, it also knows the attributes going in and (by deduction) knows which attributes don't have values.

Rails's RETURNING should be modified to account for this situation.

@al2o3cr
Copy link
Contributor

al2o3cr commented Mar 2, 2017

@krainboltgreene Neither MySQL nor SQLite have an equivalent construct, so they'd have to fall back to a SELECT anyways.

@krainboltgreene
Copy link
Contributor

@al2o3cr Sure.

@rails-bot
Copy link

rails-bot bot commented Jun 10, 2017

This issue has been automatically marked as stale because it has not been commented on for at least three months.
The resources of the Rails team are limited, and so we are asking for your help.
If you can still reproduce this error on the 5-1-stable branch or on master, please reply with all of the information you have about it in order to keep the issue open.
Thank you for all your contributions.

@rails-bot rails-bot bot closed this as completed Jun 18, 2017
@thedrewbisset
Copy link

@rafaelfranca - any chance this could get another look? I recognize the complexity - particularly given how small a gain it would bring - but I'd suggest without it the support for database-level defaults in the current form is not feature complete. If it's not feasible, I would almost rather the framework not expose it as an option in the first place. Thanks for your consideration 🙏

@rails-bot rails-bot bot removed the stale label Dec 13, 2017
@rafaelfranca
Copy link
Member

Feel free to work on it. Right now this problem is not priority for the Rails team since there is a workaround.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants