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

Deprecate plural association names on singular associations #45163

Conversation

HParker
Copy link
Contributor

@HParker HParker commented May 23, 2022

Before this change associations could be called by their plural or singular name,

i.e.

  class Post < ActiveRecord::Base
    has_many :comments
  end

  class Comment < ActiveRecord::Base
    belongs_to :post
  end

  # this works with `post` as the key:
  Comment.where(post: Post.first)
  # this also works with `:posts` as the key (after this change this will warn):
  Comment.where(posts: Post.first)

This PR deprecates using the plural association name when the association was not defined as plural.

Motivated by #45020 which found that singularize call is slow and happens very often when you are querying a column name which is not a relation, so it must try both paths. After this deprecation cycle we can save that time in the where(column_name: "value") case and where(association: "value") case.

After this deprecation cycle, we can also avoid a potentially annoying edge case where you are unable to query a column with a singular name the same as your association's name.

It seems this singularize was added 2 years ago here: #39465 , however inheritance and calculation tests still pass with this change removed. I also added a test for the enum case that is referenced in the the issue associated with the above PR to be extra safe.

for extra clarity, here is a test case that will pass after this deprecation that doesn't today,

# frozen_string_literal: true

require "bundler/inline"

gemfile(true) do
  source "https://rubygems.org"

  git_source(:github) { |repo| "https://github.com/#{repo}.git" }

  gem "rails", github: "rails/rails", branch: "main"
  gem "sqlite3"
end

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

# 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|
  end

  create_table :comments, force: true do |t|
    t.string :posts
    t.integer :post_id
  end
end

class Post < ActiveRecord::Base
  has_many :comments
end

class Comment < ActiveRecord::Base
  belongs_to :post
end

class BugTest < Minitest::Test
  def test_association_stuff
    comment = Comment.create!(posts: "hi there")

    assert_equal 1, Comment.where(posts: "hi there").count
  end
end

@HParker HParker force-pushed the deprecate-plural-association-names-on-singular-reflections branch 2 times, most recently from c4a77f7 to 522502f Compare May 24, 2022 19:36
@byroot
Copy link
Member

byroot commented May 25, 2022

Nice find. Looking at the commit that introduced this, it seems like an accidental feature. Honestly I'm not even sure whether this is worth a deprecation cycle, as it never was actually a feature, it just happened to work.

@byroot byroot requested a review from kamipo May 25, 2022 14:14
@HParker
Copy link
Contributor Author

HParker commented May 25, 2022

Yeah, I tested this and we never hit the deprecation warning in our test suite. I am up for skipping the deprecation cycle as well unless anyone feels strongly.

@byroot
Copy link
Member

byroot commented May 26, 2022

I also tested this branch on our app, we do hit this deprecation a bit but it's clearly a mistake and will be very easy to fix on our side. So I really think we should not bother with a deprecation.

@byroot
Copy link
Member

byroot commented May 26, 2022

At best if we want to make the error easier to fix, we could look into a "did_you_mean" style of error message.

@HParker HParker force-pushed the deprecate-plural-association-names-on-singular-reflections branch 4 times, most recently from 7016773 to d6bdece Compare May 31, 2022 15:48
@HParker
Copy link
Contributor Author

HParker commented May 31, 2022

@byroot Updated with a DidYouMean error. I really like the way it reads actually! Should we still have a date/release in mind where we remove this error? We still will be paying the singularize cost when we query by column name with this change.

@byroot
Copy link
Member

byroot commented May 31, 2022

We still will be paying the singularize cost when we query by column name with this change.

Ah right, I was hopping we wouldn't but I now see how it's tricky.

What about introducing an Active Record setting to disable this check, and have it default to disabled on 7.1 ?

@byroot
Copy link
Member

byroot commented May 31, 2022

Actually thinking again about it. We really shouldn't bother. Let's just strip it now.

@HParker HParker force-pushed the deprecate-plural-association-names-on-singular-reflections branch from d6bdece to 4b5ba40 Compare May 31, 2022 20:50
Before this change associations could be called by their plural or singular name,

i.e.

	  class Post < ActiveRecord::Base
	    has_many :comments
	  end

	  class Comment < ActiveRecord::Base
	    belongs_to :post
	  end

	  # this works with `post` as the key:
	  Comment.where(post: Post.first)
	  # this also works with `:posts` as the key (after this change this will warn):
	  Comment.where(posts: Post.first)

This PR deprecates using the plural association name when the association was not defined as plural.

Motivated by rails#45020 which found that singularize call is slow and happens very often when you are querying a column name which is not a relation, so it must try both paths. After this deprecation cycle we can save that time in the `where(column_name: "value")` case.

After this deprecation cycle, we can also avoid a potentially annoying edge case where you are unable to query a column with a singular name the same as your association's name.

It seems this singularize was added 2 years ago here: rails#39465 , however inheritance and calculation tests still with this change removed.
@HParker HParker force-pushed the deprecate-plural-association-names-on-singular-reflections branch from 4b5ba40 to 92eeaff Compare May 31, 2022 20:55
@HParker
Copy link
Contributor Author

HParker commented May 31, 2022

I love deleting code! Updated.

Yeah, later we check if the table_name matches the table name, so I think every real world case will get caught by that before doing anything unexpected.

@byroot byroot merged commit e0b58f6 into rails:main May 31, 2022
@HParker HParker deleted the deprecate-plural-association-names-on-singular-reflections branch June 7, 2022 15:10
HParker added a commit to HParker/rails that referenced this pull request Jun 16, 2022
This removes the singularize from `where` which runs on all `expand_from_hash` keys which might be reflections or column names. This saves a lot of time by avoiding singularizing column names.

Previously in rails#45163 the singularize was removed entirely. after some reflection, I think it is better to at least give a warning for one release since `where` is a very popular API and the problems you can run into with incorrect relation could be hard to debug.

Configurable with `ActiveRecord::Base.allow_deprecated_singular_assocaitions_name = false` / `config.active_record.allow_deprecated_singular_assocaitions_name = false`
willnet added a commit to willnet/rails that referenced this pull request Jun 23, 2022
…me [skip ci]

The only time rails#45163 and rails#45344 have an effect is when the hash value passed to `where` is a model object. The current sample code does not change behavior between Rails 7.0 and 7.1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants