我遇到一种情况,即在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,不幸的是我目前无法升级它。
答案 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