Rails 3 ActiveRecord::Relation 随机关联行为

发布于 2024-10-09 03:47:35 字数 711 浏览 0 评论 0原文

我目前在从 Rails 2.3.8 迁移到 Rails 3(3.0.1、3.0.2、3.0.3)的应用程序中遇到一个奇怪的问题。 在随机时刻,关联会表现得很奇怪。在某些情况下,关联对象将返回 Relation 对象,而不是相应的模型。这似乎主要发生在多态关联上。例如:

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

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

当调用“ab”时,这“有时”会返回 Relation 对象(导致引发“未定义的方法... for ActiveRecord::Relation”错误),而有时它会返回正确的 B 对象。 当关系对象返回时,它有时可能会通过重新启动服务器来暂时“修复”,但它最终会再次出现。

我遇到的另一个问题是,当“获取”关联对象时,有时不会自动应用所需的过滤器(其中元素 id = ...)。这会导致查询返回表中的第一个对象,而不是正确的关联对象。

这正在成为一个非常令人沮丧的问题,特别是因为我似乎没有找到其他人遇到这个或类似的问题。

应用程序中的所有查找器方法都已迁移到新的 Rails 形式,但这种奇怪的行为仍然存在。

当前使用的配置是: 乌班图10 nginx服务器 乘客 (3.0.2) 导轨 (3.0.3) ruby 1.9.2p0(2010-08-18 修订版 29036)

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)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

疑心病 2024-10-16 03:47:35

在深入研究 Active Record 代码后,我和我的同事发现belongs_to_association.rb和belongs_to_polymorphic_association.rb仍在“find_target”方法中使用旧的查找器方法。

我们通过记录来自不同查询的此方法的结果对象来运行一些测试,发现旧的查找器在随机时刻返回 ActiveRecord::Relation(加载同一对象有时会返回该对象,有时会返回 Relation 对象)。

我们在初始化程序文件中重写了这两个类的 find_target 方法,将其中使用的查找器更改为新的 Rails3 格式(klass.select(...).where(...) 等)。这似乎已经解决了问题。

“has_many”关联文件已经在使用新格式,因此不会造成任何问题。

我们将这些“修复”应用到了 Rails 3.0.3 中,并希望它们能够在未来的版本中得到解决。

这是我们的初始化程序文件,以防其他人遇到此问题:
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

希望这对某些人有用

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

牵你手 2024-10-16 03:47:35

感谢您分享这个。关联的问题消失了。但即使使用直接的“查找”方法,我仍然遇到这个问题。

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

将因 ActiveRecord::Relation 错误而失败。不过

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

会工作

似乎很奇怪的延迟加载行为

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文