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

Fix find_by with ActiveRecord::Base object #26213

Closed

Conversation

kamipo
Copy link
Member

@kamipo kamipo commented Aug 18, 2016

Fixes #26210.

@rails-bot
Copy link

r? @arthurnn

(@rails-bot has picked a reviewer for you, use r? to override)

hash = args.first

def find_by(hash, *args) # :nodoc:
return super if scope_attributes? || !(Hash === hash) || reflect_on_all_aggregations.any?
return super if hash.values.any? { |v|
v.nil? || Array === v || Hash === v || Relation === v
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noted in passing: this sounds like it would let a Range through, but the below seems ill-equiped to handle that.

@rafaelfranca
Copy link
Member

We expliclty didn't want to users to use active record instances in find methods we even have deprecations to that in 4.2 https://github.com/rails/rails/blob/4-2-stable/activerecord/lib/active_record/core.rb#L139. The proper fix to this is raise an exception.

@kamipo
Copy link
Member Author

kamipo commented Aug 19, 2016

We have a PredicateBuilder::BaseHandler therefore the following find_by query works without deprecation.

  Post.find_by(author_id: author, type: ["Post", "SpecialPost"])

Should we raise an exception for find_by with AR::Base only query?

@rafaelfranca
Copy link
Member

That seems weird. In 4.2 we told people to not use AR instances in finder methods, now we are allowing again. I believe that case is a left over of @tenderlove's work to deprecate support of AR instances in finders.

@tenderlove WDYT about this?

@kamipo
Copy link
Member Author

kamipo commented Aug 19, 2016

This deprecation was added to AR::Core::ClassMethods#find and AR::FinderMethods#find_one (in AR::FinderMethods#find). But find_by should work with AR instance. Otherwise the following query does not work.

  Essay.find_by(writer: author)
  # => SELECT  "essays".* FROM "essays" WHERE "essays"."writer_type" = 'Author' AND "essays"."writer_id" = 'David' LIMIT ?  [["LIMIT", 1]]

@rafaelfranca
Copy link
Member

oh, I see what you mean. Yeah it makes sense. I'll think about a bit before taking the decision to merge or not. r? @rafaelfranca

@rails-bot rails-bot assigned rafaelfranca and unassigned arthurnn Aug 19, 2016
@tenderlove
Copy link
Member

I think something like:

  Essay.find_by(writer: author)

is be OK. What I'd really like to eliminate is

Post.find_by(author_id: author, type: ["Post", "SpecialPost"])

AR objects aren't ids, so it doesn't make sense.

@kamipo
Copy link
Member Author

kamipo commented Aug 25, 2016

Actually find_by is an alias to where(arg, *args).take therefore Post.where(author_id: author, type: ["Post", "SpecialPost"]).take works because PredicateBuilder::BaseHandler exists.

https://github.com/rails/rails/blob/v5.0.0/activerecord/lib/active_record/relation/finder_methods.rb#L78
https://github.com/rails/rails/blob/v5.0.0/activerecord/lib/active_record/relation/predicate_builder/base_handler.rb

Should we deprecate PredicateBuilder::BaseHandler like PredicateBuilder::ClassHandler?

https://github.com/rails/rails/blob/v5.0.0/activerecord/lib/active_record/relation/predicate_builder/class_handler.rb

@matthewd
Copy link
Member

IMO, no. Unlike Model.find(obj), which is pointless, that form is a useful implicit cast, and fits in perfectly with the number of other more elaborate predicate forms we support.

@kamipo
Copy link
Member Author

kamipo commented Aug 26, 2016

I think that it should be consistent behavior both AR::Core::ClassMethods#find_by and AR::FinderMethods#find_by.
Currently Post.find_by(author_id: author) and Post.all.find_by(author_id: author) have inconsistent behavior.
For recovering consistency, I think we have 2 choices.

--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -205,7 +205,7 @@ def find_by(*args) # :nodoc:
         hash = args.first

         return super if hash.values.any? { |v|
-          v.nil? || Array === v || Hash === v || Relation === v
+          v.nil? || Array === v || Hash === v || Relation === v || Base === v
         }

@sgrif
Copy link
Contributor

sgrif commented Aug 31, 2016

Long term I'd actually like to reuse the handlers from the predicate builder as part of the statement cache, as well as provide a point of extension for the connection so we can do things like change arrays to use = ANY on PG. It'd also potentially let us handle ranges properly since there's a known limited subset of queries that we will execute for a given range.

Short term though I think the second option makes the most sense as the temporary stop-gap

kamipo added a commit to kamipo/rails that referenced this pull request Jan 4, 2017
The alternative of rails#26213.

Currently `find_by` and `where` with AR object return inconsistent
result. This is caused by statement cache does not support AR object.
Passing to finder method to fix the issue.

Fixes rails#26210.
@rafaelfranca
Copy link
Member

Closing in favor of #27564

@kamipo kamipo deleted the fix_find_by_active_record_base_object branch January 4, 2017 03:40
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.

8 participants