This project is archived and is in readonly mode.

#2558 ✓wontfix
Jacob Kjeldahl

Support for :joins in ActiveRecord::Base#update_all

Reported by Jacob Kjeldahl | April 24th, 2009 @ 02:14 PM | in 2.x

I needed to update records based on data in another table so I added support for :joins in ActiveRecord::Base#update_all.

Example usage: (from rdoc)


Book.update_all "rating = 'high'", "title LIKE '%Rails%' and authors.name = 'David'", :joins => :authors

It also works on named_scopes:


Book.by_author('David').update_all("rating = 'high'")

where


class Book < ActiveRecord::Base
  belongs_to :author
  named_scope :by_author lambda{|name|
    {:conditions => {:author => {:name => name}}, :joins => :author}
  }

There is also a fork on github at http://github.com/kjeldahl/rails...

Comments and changes to this ticket

  • CancelProfileIsBroken

    CancelProfileIsBroken August 6th, 2009 @ 02:12 PM

    • Tag changed from 2.3.2, :joins, active_record, patch, update_all to 2.3.2, :joins, active_record, bugmash, patch, update_all
  • Jacob Kjeldahl
  • Josh Sharpe

    Josh Sharpe August 8th, 2009 @ 03:33 AM

    The idea makes sense, but the patch doesn't apply:

    Patch does not have a valid e-mail address.

  • John Trupiano

    John Trupiano August 9th, 2009 @ 06:09 PM

    +1 on the idea. i have revised the patch so that it will apply cleanly (unfortunately it has my commit info and not Jacob's).

    The patch, however, causes 3 sqlite test failures. It seems to me that sqlite does not support the "UPDATE tbl INNER JOIN tbl2 ON tbl.id = tbl2.tbl_id" syntax. How do we go about solving this? Do we add a method to each of the adapters (:supports_updates_with_joins?) to check before applying any :joins options? If so, do we fail or simply ignore when :joins is passed in when being used with sqlite (or any other db that doesn't support the syntax)?

  • Rizwan Reza

    Rizwan Reza August 9th, 2009 @ 06:12 PM

    verified

    +1 The patch works only on 2-3-stable. All tests pass.

  • Matt Jones

    Matt Jones August 9th, 2009 @ 06:22 PM

    The patch looks interesting, but I don't think it's a good idea: it seems likely that update_all is limited for a good reason. Encouraging people to, essentially, bypass ActiveRecord goes against the intention of the framework.

  • Elad Meidar

    Elad Meidar August 9th, 2009 @ 06:51 PM

    +1 Verified, -1 Patch i agree with Matt, seems like it's a better approach to leave #update_all limited and not encourage AR workarounds that will probably lead to unexpected issues.

  • Derander

    Derander August 9th, 2009 @ 09:03 PM

    -1 verified the patch

    Agreed w/ Elad, if you're going to do something like this, might as well just write the plain sql.

  • Jeremy Kemper

    Jeremy Kemper August 10th, 2009 @ 06:50 AM

    • State changed from “new” to “wontfix”
    • Tag changed from 2.3.2, :joins, active_record, bugmash, patch, update_all to 2.3.2, :joins, active_record, patch, update_all

    Agree that dropping to SQL is clearer.

  • Jens

    Jens July 19th, 2010 @ 05:10 PM

    • Importance changed from “” to “”

    This is not true and prevents one from clean separation between objects:

    class Parent < AR::Base
      has_many :children
      has_many :grandchildren, :through => :children, :dependent => :nullify
    
      def before_update
        self.grandchildren.delete_all  # this and ".clear" does NOT WORK, thus:
        self.grandchildren.disconnect_disliked_ones # see below
      end
    end
    
    class Child < AR::Base
      has_many :grandchildren
    end
    
    class Grandchild < AR::Base
      def self.disconnect_disliked_ones
        self.update_all("child_id = NULL", "brave IS FALSE")
      end
    end
    

    The idea is that I put the intricacies of the "disconnect" operation where the implementation is, i.e. in the grandchild. Also, the type of connection between grandchild and parent is not restricted per se.

    However, if called from within a Parent instance with ID=42, this will result in an invalid SQL query like

    UPDATE "grandchildren" 
      SET grandchildren.child_id = NULL
      WHERE (grandchildren.brave IS FALSE) 
        AND (children.parent_id = 42)
    

    which will result in an error, because I cannot specify a JOIN clause.

    Of course, I can use Parent.find_by_sql and specify the whole UPDATE operation verbatim, however, then the separation between Parent and Grandchild internal structure is violated.

    Is it not possible to put the JOIN patch into the source and leave it to the user whether to use it or not?

    Thanks,

    Jens

  • Claudio Poli

    Claudio Poli October 1st, 2010 @ 03:30 PM

    I agree on having this functionality in core.

  • eydaimon

    eydaimon November 12th, 2010 @ 06:43 PM

    +1 on supporting this.

    I'd want to be able to do:

    User.find(30).tasks.update_all completed: true
    

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile »

<h2 style="font-size: 14px">Tickets have moved to Github</h2>

The new ticket tracker is available at <a href="https://github.com/rails/rails/issues">https://github.com/rails/rails/issues</a>

Referenced by

Pages