rails / rails

Ruby on Rails

Home Page:https://rubyonrails.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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

brocktimus opened this issue · comments

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

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

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.

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?

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?

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

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

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.

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

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?

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.

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.

@brocktimus Thank you, that was very helpful! 👍

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

@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.

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

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

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

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)?

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.

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).

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?

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

It seems like the problem is that the lifecycle hooks/callbacks are being evaluated within a scope. What if we were to make all off these hooks be performed without an enclosing scope? That is, when would we need/want them to be performed inside a scope?

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 4-1-stable, 4-0-stable branches 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.

The problem still exists in rails 4.1.1 - i can reproduce the problem with example described in #12305.

Thanks. We do have a fix for at #15355. I'll link this issue there.

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 4-1-stable, 4-0-stable branches 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.

The problem still exists in rails 4.2.0 - i can reproduce the problem with example described in #12305.

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

@ThomasAlxDmy its OSS, feel free to open a PR with a fix. Comments like that really aren't helpful.

@rafaelfranca @senny since this has been behaviour for all of the 4.x series is it worth reconsidering what intended behaviour should be given 5 is on the horizon? Possibly more people relying on this behaviour now than not. I'm not sure.

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

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 notifications@github.com wrote:

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


Reply to this email directly or view it on GitHub.

@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 ;)

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 notifications@github.com 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.

@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.

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 notifications@github.com 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.

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.

@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.

@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.

@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.

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

@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.

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

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.

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

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

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!

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).

@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.

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