是否可以卸载ActiveRecord关联?

时间:2011-03-21 17:23:06

标签: ruby-on-rails activerecord associations

我遇到一种情况,即在flash中保存的模型导致marshall data too short错误,并且发现其原因是正在加载一个巨大的关联,然后当放入会话时数据太大为了这个领域。

我的课程看起来像这样:

class Location < ActiveRecord::Base
  belongs_to :country
  belongs_to :state
  belongs_to :city
end

class Country < ActiveRecord::Base
  has_many :states
  has_many :locations
end

class State < ActiveRecord::Base
  belongs_to :country
  has_many :cities
  has_many :locations
end

class City < ActiveRecord::Base
  belongs_to :state
  has_many :locations
end

不幸的是,Location中的验证访问self.state.cities,它将该州的所有城市加载到内存中(在本例中,超过1000条记录)。因此,当Location对象通过flash时,会导致上述错误。

我意识到代码可以重构为不能访问关联(事实上,这就是我目前所做的),但我想知道是否有任何方法卸载来自内存的关联数据(当然不会将其从数据库中删除)。

这个项目正在使用Rails 2.3.4,不幸的是我目前无法升级它。

1 个答案:

答案 0 :(得分:1)

第1部分:解决上述问题:

要清空已加载的关联数据,请在关联上使用target方法:

location.state.cities.target=[]

如果您现在访问cities

location.state.cities
=> []

第2部分:避免问题的笨拙方法:

访问cities时避免加载关联。使用

self.state.cities.all 

而不是

self.state.cities

第3部分:有些东西闻起来很可疑:

为什么要将对象存储到闪存中? Flash通常用于发送文本消息。如果您将非字符串分配给闪存,您将立即运行到cookie大小限制。

此外,当您确认为什么需要加载所有城市?这是否意味着你在Ruby中验证?你能发布验证码吗?在大多数情况下,您可以通过将验证移动到DB来优化它。

编辑:根据评论

扩展答案

我在Rails 2.3.x中遵循这种模式。我想在与edit操作相关联的视图中访问的变量始终是update操作中可用变量的子集。

class ProductsController < ApplicationController

  before_filter :init_data, :only => [:new, :create, :edit, :update, 
                                      :destroy, :show]
  def update
    @product.attributes = params[:product]
    if @product.save
      flash[:notice] = "Successfully saved the product."
      redirect_to  product_path(@product)
    else
      render :action => 'edit'
    end
  end

private
  def init_data
    switch(action.to_sym)
      when :new, :create
        @product = Product.new(params[:product])
      when :edit, update, :destroy, :show 
        @product = Product.find(params[:id])
    end
  end    
end