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

ActiveRecord.where(hash).create callbacks inherit the where(hash) #9894

Closed
brocktimus opened this issue Mar 24, 2013 · 52 comments · Fixed by #12015
Closed

ActiveRecord.where(hash).create callbacks inherit the where(hash) #9894

brocktimus opened this issue Mar 24, 2013 · 52 comments · Fixed by #12015

Comments

@brocktimus
Copy link
Contributor

Running rails 4.0.0-beta1 on ruby 2.0.0-p1.

It seems as though when you perform code like the below, the where clauses are inherited by any callbacks it runs.

Category.where(full_name: 'root/bar').create

The above exhibits the problem. All of the below function as intended.

Category.new(full_name: 'root/bar').save
Category.create(full_name: 'root/bar')
Category.where(full_name: 'root/bar').new.save

By debugging on the rails console it reveals:

# This is basically running
Category.where(full_name: 'root/bar').where(full_name: 'root').first

# When this is expected
Category.where(full_name: 'root').first

This also applies to the first_or_create methods. I'm yet to test the find_or_create methods or the rails 3.x series. Should I write this into an active_record test? Any advice as to where to start?

I've created an example application with this problem here https://github.com/brocktimus/rails_create_differences. Example class used below.

class Category < ActiveRecord::Base

  has_many :children, class_name: 'Category', foreign_key: :parent_id
  belongs_to :parent, class_name: 'Category'

  before_validation :assign_parents, on: :create

  private

  def assign_parents
    parts = full_name.split '/'
    self.name = parts.pop
    self.parent = Category.where(full_name: parts.join('/')).first_or_initialize unless parts.empty?
  end

end
@senny
Copy link
Member

senny commented Mar 26, 2013

Hey @brocktimus and thanks for your report. I'm not sure if this is actually a bug. This problem was reported before and you can follow the discussion here #7853 and here #7391. Those tickets were closed because it was the expected behavior.

/cc @jonleighton

@brocktimus
Copy link
Contributor Author

Right if its working as intended thats fine.

In Rails 3 I would have used find_or_create_by_full_name(full_name).

I'm just playing with rails 4 a bit and most of the transition documentation said to change that to the where(hash).first_or_create_by. I didn't realise the existence of .find_or_create(hash) until looking into the ActiveRecord code to investigate.

Did you want me to document how those methods work anywhere or how things should be changed for compatibilty? Unsure exactly how railsguides and the like work and what happens during changeover between major releases.

@senny
Copy link
Member

senny commented Mar 26, 2013

If we settle that this is the expected behavior It totally makes sense to document it. The fact that already 3 reports were filed regarding the same problem shows that it's not as "expected" as it could be which makes documentation even more necessary.

In my opinion the guides should explain how to get a 3.x app running on 4.0 without such complications. I think it would be good to promote #find_or_create. As the described scenario is a combination of different methods on relation it's kind of hard to put it into the rdocs. Methods that always run into this behavior like first_or_create could be a good place though.

@rafaelfranca @steveklabnik thoughts?

@brocktimus
Copy link
Contributor Author

Is find_or_create available in 3.2? I've been migrating across to the where(hash).first_or_create recently in my 3.2 apps because it was available and was the way rails seemed to be moving forward.

Just figured I'd add my 2c regarding how it looks.

I did think the first_or_create syntax was really nice before this, especially being able to pass in a block for extra parameters. It looked a lot nicer than the original find_or_create_by_column_name methods since you construct it like any other relation and then tack on first_or_create to the end.

It was the guides like that below and some others which suggested this as a drop in replacement.

http://blog.remarkablelabs.com/2012/12/what-s-new-in-active-record-rails-4-countdown-to-2013

The idea of just doing .new.save as a work around seems awkward and I would've expected to be equivalent to create. I guess it ends up being a question of how often is this desired functionality, how often will it trip people up and how complex is it to "fix" right?

@rafaelfranca
Copy link
Member

Yeah, I think we should promote find_or_create since its behavior is closer to the dynamic finders.

@dhh
Copy link
Member

dhh commented Mar 29, 2013

Jon implemented find_or_create_by on relation, so let's just document that.

@ghost ghost assigned senny Mar 29, 2013
@jonleighton
Copy link
Member

I agree. I kinda consider first_or_create and friends to be soft-deprecated, so we should definitely promote find_or_create and friends in the docs.

@brocktimus
Copy link
Contributor Author

Want me to write something up to that effect in the Rails 4 release notes + upgrading ruby on rails guides?

@rafaelfranca
Copy link
Member

@brocktimus if you want, please

@slava-vishnyakov
Copy link

I'm confused.

Rails 4 Release Notes says this (http://edgeguides.rubyonrails.org/4_0_release_notes.html):

find_or_create_by_... can be rewritten using find_or_create_by(...) or where(...).first_or_create.

Is this incorrect now? Considering that first_or_create is soft-deprecated?

@slava-vishnyakov
Copy link

Let's say I had this:

Model.where(a: 1, b: 1).first_or_create(c: 1)

How can I reproduce this behaviour with new methods?

It's sad, first_or_create was really useful.

@brocktimus
Copy link
Contributor Author

I've updated docrails to try and reflect that the first_or_* methods shouldn't be used as drop in replacements for the others. I don't know if there are similar issues with first_or_initialize, but I'm guessing there could be if you did specific things in an after_initialize block.

brocktimus/docrails@56297d0

Are the changes to magical finders a big enough change to be mentioned in the upgrading_ruby_on_rails guide? Currently there is no mention of them.

I can't comment on the soft deprecation status, but can comment on that code. As it stands it would work, but would be very dependent on any callbacks in your model as the scope of the where will effect them as well.

If you didn't want to use them to prevent potential issues later you could rewrite it in one of the following ways:

Model.where(a: 1, b: 1).first_or_initialize(c: 1).save

creating = Model.find_or_initialize_by(a: 1, b: 1)
creating.c = 1 if creating.new_record?
creating.save

I liked it the first_or_* methods, but if they're too complicated or difficult to maintain I'm not overly attached to them.

@slava-vishnyakov
Copy link

@brocktimus Thank you, that was very helpful! 👍

@vipulnsward
Copy link
Member

Is this still a bug now after changes to http://edgeguides.rubyonrails.org/4_0_release_notes.html ?

@robin850
Copy link
Member

@vipulnsward

Are the changes to magical finders a big enough change to be mentioned in the upgrading_ruby_on_rails guide? Currently there is no mention of them.

I agree that we should mention it in the upgrading guide. Then I think that we can close it.

@brocktimus
Copy link
Contributor Author

Since @jonleighton said the first_or_* methods are soft deprecated should we only mention the drop in replacements of find_or_*_by() ?

@brocktimus
Copy link
Contributor Author

I've done that already in brocktimus/docrails@56297d0 should I just merge that onto master of docrails?

@vipulnsward
Copy link
Member

@brocktimus I have added #12015 based on your changes.
You can merge your changes after that gets reviewed.

@jarmo
Copy link

jarmo commented Sep 27, 2013

I don't understand how this issue has been closed. The first report didn't include any *_by* methods as does not my issue #12305. How come they're related or is the documentation still not sufficiently written?

Documentation only writes about those dynamic methods, but not telling anything that this code should not work as it did before:
Foo.where(name: "bar").create or Foo.where(name: "bar").first_or_create(baz: "bar").

Are you trying to say with the documentation change which led of closing these issues, that the code above should also not work and it is expected behavior of changing the default scope of the class itself (not even singleton class)?

@senny
Copy link
Member

senny commented Sep 27, 2013

For reference I'm posting the example from #12305 :

class Testing < ActiveRecord::Base
  scope :mine, -> { where(field: "mine") }

  after_create do
    Testing.where(field: "other").load
  end
end
irb(main):005:0> Testing.mine.create
   (1.0ms)  begin transaction
  SQL (0.0ms)  INSERT INTO "testings" ("created_at", "field", "updated_at") VALUES (?, ?, ?)  [["created_at", Fri, 20 Sep 2013 15:56:33 UTC +00:00], ["field", "mine"], ["updated_at", Fri, 20 Sep 2013 15:56:33 UTC +00:00]]
  Testing Load (0.0ms)  SELECT "testings".* FROM "testings" WHERE "testings"."field" = 'mine' AND "testings"."field" = 'other'
   (12.0ms)  commit transaction
=> #<Testing id: 2, field: "mine", created_at: "2013-09-20 15:56:33", updated_at: "2013-09-20 15:56:33">

I'll reopen as we did not change documentation for #create in combination with scopes.

@senny senny reopened this Sep 27, 2013
@jarmo
Copy link

jarmo commented Sep 27, 2013

As mentioned above, i don't think that the problem is only not being mentioned in the documentation, but the problem is that the class's scope itself is changed, which should not ever happen (unless specified explicitly with default scope, if i'm not mistaken).

@brocktimus
Copy link
Contributor Author

I dug around myself before creating this ticket. I was using first or create and found that it was calling first and then ORing the result with create. Thus the problem (as I saw it) was within the create method, so thats how I raised the issue.

The reason this was an issue for me (and others) was because first_or_create was the recommended upgrade path from find_or_create_by_* methods in rails 3. This had different behaviour to the original find_or_create_by_* methods so instead I changed documentation to point at the newer find_or_create_by(hash) methods which had the same expected behaviour.

I've got no idea if / when the behaviour of MyModel.query_scope.create was changed. Did it behave differently in previous versions of rails or is the comment more that it is unintuitive?

@jarmo
Copy link

jarmo commented Sep 27, 2013

The example code i wrote in issue #12305 worked fine with ActiveRecord 3.x series. The problem arised when i upgraded to 4.0.

@dhh
Copy link
Member

dhh commented Jul 28, 2015

Too late. Your entitlement to the free labor of others is now a permanent marker in the annals of Rails.

On Jul 28, 2015, at 03:36, ThomasAlxDmy [email protected] wrote:

@brocktimus Sorry don't want my name associated with rails ;)


Reply to this email directly or view it on GitHub.

@ThomasAlxDmy
Copy link

@dhh lmao When did I say I was using rails? Was just trying to help a friend, I dropped rails/AR years ago mostly because of that kind of issues ;)

@dhh
Copy link
Member

dhh commented Jul 28, 2015

Your friend is most welcome to join the community and help device a solution to their issue, if they can do so without your obnoxious entitlement.

Please do be on your merry way to whatever community you've found that has either no issues or is willing to tolerate your attitude about them.

On Jul 28, 2015, at 19:09, ThomasAlxDmy [email protected] wrote:

@dhh lmao When did I say I was using rails? Was just trying to help a friend, I dropped rails/AR years ago mostly because of that kind of issues ;)


Reply to this email directly or view it on GitHub.

@ThomasAlxDmy
Copy link

@dhh You're over reacting my friend, I'm just pointing out true facts. And that's it. There ain't no bad intention. We all have issues but there's way different ways to tackle them. I was reading an interesting article about why is rails community slowly dying (small comparison with node -- which I'm not fan of personally) http://www.google.com/trends/explore?hl=en-US#q=%22ruby+on+rails%22,+/m/0bbxf89&cmpt=q&tz&tz and I do believe that is that kind of attitude that leads to it.

I'm sorry if you can't distinguish your allies from your enemies.

@dhh
Copy link
Member

dhh commented Jul 28, 2015

Your "true facts" are as laughable as your claim of "ally". Rails is at this point a massive system with an incredibly strong, diligent, and ever-expanding community of users and contributors working on improving it.

But there will always be bugs, and this particular bug just wasn't important enough to summon any effort to its fix. Ipso facto.

Rails is not a vendor that owes you, or your friend, anything. It's a common space where everyone can contribute their own fixes and improvements, and in return enjoy that in kind from others.

You contributed nothing but needless scorn and entitlement. That is not the behavior of an "ally". Your post-rationalization for this disgraceful behavior is that the truth of a fact like "bug X has not been fixed for me in duration Y" completely side-steps the point. That truth is both trivial and obvious. You're not providing any service of revelation by sharing.

The tone in which you chose to, though, was indeed in bad intention. Your clearly meant to, and obviously continue to, belittle the work of thousands of contributors who share their work with all for free.

And for what? Do you think anyone will be inspired to help your friend deal with this issue with greater haste because you huffed and puffed? Quite the contrary. Demanding the free labor of others with indignation is no way to inspire their charity.

So, again, please pack your entitlement, your banal analysis of the health of Rails, and be gone. Shoo, shoo.

On Jul 28, 2015, at 20:21, ThomasAlxDmy [email protected] wrote:

@dhh You're over reacting my friend, I'm just pointing out true facts. and that's it. There ain't no bad intention. We all have issues but there's way different ways to tackle them. I was reading an interesting article about why is rails community slowly dying (small comparison with node -- which I'm not fan of personally) http://www.google.com/trends/explore?hl=en-US#q=%22ruby+on+rails%22,+/m/0bbxf89&cmpt=q&tz&tz and I do believe that is that kind of attitude that leads to it.

I'm sorry if you can't distinguish your allies from your enemies.


Reply to this email directly or view it on GitHub.

@rafaelfranca
Copy link
Member

Since we are discussing tends, these are interesting number about what you are complaining here http://issuestats.com/github/rails/rails. I think these numbers speak by their self.

Yes, I'm aware that this issue is open for 2 years. In fact I'm aware of all the issues on the issue tracker. But my time is limited, my interest too and I try to focus my time on the things that most interest me, because after all I work here not because someone pays me, but because I love to work with this team and use my free time to help people.

I'm also aware that this is not a trivial issue to fix and it will require a lot of work and it may break a lot of applications like @brocktimus correctly pointed. I know It will be fixed eventually, because there are so many great people working on this framework that it is just matter of time. Every single day someone new starts to contribute to this framework and it is amazing how a little bit of encouragement can create amazing regular contributors like @senny @chancancode @sgrif @kaspth @robin850 @vipulnsward @meinac just to name few.

I really don't know if Rails is dying, but I don't care, because the machinery that runs this framework is more alive than ever.

@rafaelfranca
Copy link
Member

@brocktimus indeed, this is something that we can just change on Rails 5. The issue is assigned to me, we already have some patches, @sgrif already touched this code too, so I believe we will get it fixed in time to Rails 5.

@rafaelfranca rafaelfranca added this to the 5.0.0 milestone Jul 28, 2015
@ThomasAlxDmy
Copy link

@dhh Actually I don't think you're right. Because now I got your full attention, then people will come watch to see what's happening, and that issue will be work on again. Awareness is the key.

Do you realize the bug has been open for 2 years and it's still not fixed?

IS a true fact, and if you want to laugh about it, feel free but then I really wonder what kind of leader you are for "Your community". As you see it was not address against anyone, it was just an open statement. I have not asked for anything, nor I said that you owe me anything.

Do you think you're going to win that argument by insulting and trolling me? In my humble opinion you should focus on more important thing for "Your community" than attacking the ones that want to raise awareness.

For my friends we found a way around it, so now it would just be helping "Your community"... Again, I think your attitude is very disappointing far far away from constructive. You should learn that contributing to a project is not the only way to make it move forward (note that I do not claim I ever did), and you should be grateful to all the people that are still trying to make Rails a better word rather than trolling them for your own sake.

@rafaelfranca
Copy link
Member

@dhh Actually I don't think you're right. Because now I got your full attention, then people will come watch to see what's happening, and that issue will be work on again. Awareness is the key.

In my experience that is not what happens. In fact this issue is an outlier, for the reasons that I already explained. So even with awareness the fix is still not trivial and I doubt it will be fixed.

@jarmo
Copy link

jarmo commented Jul 28, 2015

@joemsak
Copy link

joemsak commented Jul 28, 2015

You left out the WOW... part of your quote, dude, which was kind of the finer end of your shitty point

@ThomasAlxDmy
Copy link

@joemsak stay polite please ;). And Yes WOW to express how surprise I was, did not bring much in the conversation, re-mentioning it felt useless to me (don't feed the trolls). Instead of trolling feel free to leave constructive comment and show that you're a grown man.

To come back to what Rafael said, in my humble opinion having bug is one things, not documenting and letting them hide into the code is another one.

@joemsak
Copy link

joemsak commented Jul 28, 2015

You've made an excellent point about leaving constructive comments.

@rafaelfranca
Copy link
Member

If we document a bug it is not a bug anymore but a feature 😄. Like I said this bug will die, when we think it is time.

@ThomasAlxDmy
Copy link

😂😂 That's an extreme way of seeing things but why not...

@CaseyLeask
Copy link

Please leave these wonderful people alone. Myself and many others appreciate their work.

@nynhex
Copy link

nynhex commented Jul 29, 2015

Seriously, this guy has become a troll himself. The issue will be fixed when it's fixed. The Rails core team along with the entire community is constantly working on both features and bugs which amazes me. Personally, I'm grateful for all of their hard work and sacrifice for the betterment of the framework.

If people want to bitch and whine about a long standing "bug" then perhaps they should quit the rant, write some code, and create a PR. Last time I checked the core team and contributors are not on some corporate payroll and are not obligated to do anything. They do it for the love of the framework and the community which I think is fabulous.

Hopefully this thread dies, it's getting way out of hand.

Myself and thousands others appreciate all you guys do!

@prusswan
Copy link

Certainly, this is just one of many issues with AR (the other one which comes to mind being #9813 which can be "solved" by upgrading, which is not always a practical solution)

I don't even recall the reason why I subscribed to this, but over time I learnt to be more defensive against the very framework itself (especially functionality that is obscure or only used by a minority).

@brocktimus
Copy link
Contributor Author

@rafaelfranca good to hear. Doesn't worry me too much since I just found it when playing with pre release stuff.

@prusswan if ever I'm like "I wonder what happens when I..." and I think the behaviour is a bit edge case-y I'll just write a test around the "broader" task I'm trying to accomplish. Caught a few bugs in similar cases where I've been relying upon edge cases which have changed during an upgrade.

pleary added a commit to inaturalist/inaturalist that referenced this issue Oct 2, 2015
@brocktimus
Copy link
Contributor Author

@sgrif based on your comment in #23286 (comment) should this issue also be closed since its currently targeting 5.0?

@rafaelfranca rafaelfranca removed this from the 5.0.0 [temp] milestone Apr 5, 2016
kamipo added a commit to kamipo/rails that referenced this issue Feb 7, 2019
…and callbacks

`relation.create` populates scope attributes to new record by `scoping`,
it is necessary to assign the scope attributes to the record and to find
STI subclass from the scope attributes.

But the effect of `scoping` is class global, it was caused undesired
behavior that pollute all class level querying methods in initialization
block and callbacks (`after_initialize`, `before_validation`,
`before_save`, etc), which are user provided code.

To avoid the leaking scope issue, restore the original current scope
before initialization block and callbacks are invoked.

Fixes rails#9894.
Fixes rails#17577.
Closes rails#31526.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.