Rails 3 ActiveRecord::Relation random associations behavior

独自空忆成欢 提交于 2020-01-03 05:05:54

问题


I am currently having a strange issue with an application migrated from rails 2.3.8 to rails 3 (3.0.1, 3.0.2, 3.0.3). At random moments associations behave strangely. In some cases, an associated object will return the Relation object, instead of the corresponding model. This seems to happen mostly on polymorphic associations. For example:

class A
  belongs_to :b, :polymorphic => true
end

class B
  has_many :a, :as => :source
end

When invoking "a.b" this will "sometimes" return the Relation object (causing a "undefined method ... for ActiveRecord::Relation" error to raise) and some other times it will return the correct B object. When the relation object is returned, it may sometimes be "fixed" temporarily by restarting the server, but it will eventually show up again.

Another issue i get is that when "getting" associated objects, sometimes the required filters are not automatically applied (where element id = ...). this causes the query to return the first object in the table and not the correct associated object.

This is becoming a very frustrating issue, specially since i don't seem to find anyone else with this or similar issues.

All finder methods in the application have been migrated to the new rails form, but this strange behavior remains.

The current configuration being used is: Ubuntu 10 nginx server passenger (3.0.2) rails (3.0.3) ruby 1.9.2p0 (2010-08-18 revision 29036)


回答1:


After digging a bit deeper into the Active Record code, my co-worker and I found that the belongs_to_association.rb and belongs_to_polymorphic_association.rb were still using the old finder methods in the "find_target" method.

We ran a couple tests by logging the resulting objects of this method from different queries and discovered the old finders were returning the ActiveRecord::Relation at random moments (loading the same object it would sometimes return the object and other times the Relation object).

We overrode the find_target method for these 2 classes in initializer files, changing the finders used there to the new rails3 format (klass.select(...).where(...), etc). This seems to have solved the issue.

The "has_many" association files are already using the new format, so these haven't caused any issues.

We applied these "fixes" to the rails 3.0.3 and hope that they will be resolved in future releases.

Here are our initializer files in case someone else bumps into this problem: belongs_to_polymorphic_association.rb:

#initializers/belongs_to_polymorphic_association.rb
module ActiveRecord
  # = Active Record Belongs To Polymorphic Association
  module Associations
    class BelongsToPolymorphicAssociation < AssociationProxy #:nodoc:
      private
        def find_target
          return nil if association_class.nil?

          target =
            if @reflection.options[:conditions]
              association_class.select(@reflection.options[:select]).where(conditions).where(:id => @owner[@reflection.primary_key_name]).includes(@reflection.options[:include]).first
            else
              association_class.select(@reflection.options[:select]).where(:id => @owner[@reflection.primary_key_name]).includes(@reflection.options[:include]).first
            end
          set_inverse_instance(target, @owner)

          target
        end      
    end
  end
end

belongs_to_association.rb:

#initializers/belongs_to_association.rb
module ActiveRecord
  # = Active Record Belongs To Associations
  module Associations
    class BelongsToAssociation < AssociationProxy #:nodoc:

      private
        def find_target
          key_column = (@reflection.options[:primary_key]) ? @reflection.options[:primary_key].to_sym : :id

          options = @reflection.options.dup
          (options.keys - [:select, :include, :readonly]).each do |key|
            options.delete key
          end
          options[:conditions] = conditions

          the_target= @reflection.klass.select(options[:select]).where(key_column => @owner[@reflection.primary_key_name]).where(options[:conditions]).includes(options[:include]).readonly(options[:readonly]).order(options[:order]).first if @owner[@reflection.primary_key_name]
          set_inverse_instance(the_target, @owner)
          the_target
        end

    end
  end
end

Hope this is useful for someome




回答2:


Thanks for sharing this. The problem with associations gone. But I still had this issue even with straight "find" method.

@post = Post.find(params[:id)
@post.update_attributes...

will fail with ActiveRecord::Relation error. However

@post = Post.find_by_id(params[:id])
@post.update_attributes...

will work

Seems like strange lazy loading behaviour



来源:https://stackoverflow.com/questions/4522288/rails-3-activerecordrelation-random-associations-behavior

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!